qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v1 RFC Zisslpcfi 1/9] target/riscv: adding zimops and zisslpcfi extension to RISCV cpu config
       [not found] <20230209062404.3582018-1-debug@rivosinc.com>
@ 2023-02-09  6:23 ` Deepak Gupta
  2023-02-11  3:19   ` weiwei
  2023-02-15  2:52   ` LIU Zhiwei
  2023-02-09  6:23 ` [PATCH v1 RFC Zisslpcfi 2/9] target/riscv: zisslpcfi CSR, bit positions and other definitions Deepak Gupta
                   ` (7 subsequent siblings)
  8 siblings, 2 replies; 42+ messages in thread
From: Deepak Gupta @ 2023-02-09  6:23 UTC (permalink / raw)
  To: Palmer Dabbelt, Alistair Francis, Bin Meng
  Cc: Deepak Gupta, Kip Walker, qemu-riscv, qemu-devel

Introducing riscv `zisslpcfi` extension to riscv target. `zisslpcfi`
extension provides hardware assistance to riscv hart to enable control
flow integrity (CFI) for software.

`zisslpcfi` extension expects hart to implement `zimops`. `zimops` stands
for "unprivileged integer maybe operations". `zimops` carve out certain
reserved opcodes encodings from integer spec to "may be operations"
encodings. `zimops` opcode encodings simply move 0 to rd.
`zisslpcfi` claims some of the `zimops` encodings and use them for shadow
stack management or indirect branch tracking. Any future extension can
also claim `zimops` encodings.

This patch also adds a dependency check for `zimops` to be enabled if
`zisslpcfi` is enabled on the hart.

Signed-off-by: Deepak Gupta <debug@rivosinc.com>
Signed-off-by: Kip Walker  <kip@rivosinc.com>
---
 target/riscv/cpu.c | 13 +++++++++++++
 target/riscv/cpu.h |  2 ++
 2 files changed, 15 insertions(+)

diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index cc75ca7667..6b4e90eb91 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -110,6 +110,8 @@ static const struct isa_ext_data isa_edata_arr[] = {
     ISA_EXT_DATA_ENTRY(svnapot, true, PRIV_VERSION_1_12_0, ext_svnapot),
     ISA_EXT_DATA_ENTRY(svpbmt, true, PRIV_VERSION_1_12_0, ext_svpbmt),
     ISA_EXT_DATA_ENTRY(xventanacondops, true, PRIV_VERSION_1_12_0, ext_XVentanaCondOps),
+    ISA_EXT_DATA_ENTRY(zimops, true, PRIV_VERSION_1_12_0, ext_zimops),
+    ISA_EXT_DATA_ENTRY(zisslpcfi, true, PRIV_VERSION_1_12_0, ext_cfi),
 };
 
 static bool isa_ext_is_enabled(RISCVCPU *cpu,
@@ -792,6 +794,11 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
             return;
         }
 
+        if (cpu->cfg.ext_cfi && !cpu->cfg.ext_zimops) {
+            error_setg(errp, "Zisslpcfi extension requires Zimops extension");
+            return;
+        }
+
         /* Set the ISA extensions, checks should have happened above */
         if (cpu->cfg.ext_zdinx || cpu->cfg.ext_zhinx ||
             cpu->cfg.ext_zhinxmin) {
@@ -1102,6 +1109,12 @@ static Property riscv_cpu_properties[] = {
 #ifndef CONFIG_USER_ONLY
     DEFINE_PROP_UINT64("resetvec", RISCVCPU, env.resetvec, DEFAULT_RSTVEC),
 #endif
+    /*
+     * Zisslpcfi CFI extension, Zisslpcfi implicitly means Zimops is
+     * implemented
+     */
+    DEFINE_PROP_BOOL("zisslpcfi", RISCVCPU, cfg.ext_cfi, true),
+    DEFINE_PROP_BOOL("zimops", RISCVCPU, cfg.ext_zimops, true),
 
     DEFINE_PROP_BOOL("short-isa-string", RISCVCPU, cfg.short_isa_string, false),
 
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index f5609b62a2..9a923760b2 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -471,6 +471,8 @@ struct RISCVCPUConfig {
     uint32_t mvendorid;
     uint64_t marchid;
     uint64_t mimpid;
+    bool ext_zimops;
+    bool ext_cfi;
 
     /* Vendor-specific custom extensions */
     bool ext_XVentanaCondOps;
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v1 RFC Zisslpcfi 2/9] target/riscv: zisslpcfi CSR, bit positions and other definitions
       [not found] <20230209062404.3582018-1-debug@rivosinc.com>
  2023-02-09  6:23 ` [PATCH v1 RFC Zisslpcfi 1/9] target/riscv: adding zimops and zisslpcfi extension to RISCV cpu config Deepak Gupta
@ 2023-02-09  6:23 ` Deepak Gupta
  2023-02-11  3:32   ` weiwei
  2023-02-15  3:31   ` LIU Zhiwei
  2023-02-09  6:23 ` [PATCH v1 RFC Zisslpcfi 3/9] target/riscv: implements CSRs and new bits in existing CSRs in zisslpcfi Deepak Gupta
                   ` (6 subsequent siblings)
  8 siblings, 2 replies; 42+ messages in thread
From: Deepak Gupta @ 2023-02-09  6:23 UTC (permalink / raw)
  To: Palmer Dabbelt, Alistair Francis, Bin Meng
  Cc: Deepak Gupta, Kip Walker, qemu-riscv, qemu-devel

`zisslpcfi` extension adds two new CSRs. CSR_SSP and CSR_LPLR.
- CSR_SSP: This CSR holds shadow stack pointer for current privilege mode
           CSR_SSP is accessible in all modes. Each mode must establish
           it's own CSR_SSP.

- CSR_LPLR: This CSR holds label value set at the callsite by compiler.
            On call target label check instructions are emitted by
            compiler which check label value against value present in
            CSR_LPRL.

Enabling of `zisslpcfi` is controlled via menvcfg (for S/HS/VS/U/VU) and
henvcfg (for VS/VU) at bit position 60.

Each mode has enable/disable bits for forward cfi. Backward cfi doesn't
have separate enable/disable bits for S and M mode. User forward cfi and
user backward cfi enable/disable bits are in mstatus/sstatus CSR.
Supervisor forward cfi enable/disable bit are in menvcfg and henvcfg CSR.
Machine mode forward cfi enable/disable bit is in mseccfg CSR.

If forward cfi enabled, all indirect branches must land on a landing pad
instruction (`lpcll`, introduced in later commits). CPU/hart tracks this
internally using a landing pad tracker called `elp` short for `expecting
landing pad`. An interrupt can occur between an indirect branch and
target. If such an event occurs `elp` is saved away in mstatus/sstatus
CSR

Signed-off-by: Deepak Gupta <debug@rivosinc.com>
Signed-off-by: Kip Walker  <kip@rivosinc.com>
---
 target/riscv/cpu.h      |  5 +++++
 target/riscv/cpu_bits.h | 25 +++++++++++++++++++++++++
 target/riscv/pmp.h      |  3 ++-
 3 files changed, 32 insertions(+), 1 deletion(-)

diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 9a923760b2..18db61a06a 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -181,6 +181,11 @@ struct CPUArchState {
 
     uint32_t features;
 
+    /* CFI Extension user mode registers and state */
+    uint32_t     lplr;
+    target_ulong ssp;
+    cfi_elp      elp;
+
 #ifdef CONFIG_USER_ONLY
     uint32_t elf_flags;
 #endif
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index 8b0d7e20ea..1663ba5775 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -39,6 +39,10 @@
 
 /* Control and Status Registers */
 
+/* CFI CSRs */
+#define CSR_LPLR            0x006
+#define CSR_SSP             0x020
+
 /* User Trap Setup */
 #define CSR_USTATUS         0x000
 #define CSR_UIE             0x004
@@ -542,6 +546,10 @@
 #define MSTATUS_TVM         0x00100000 /* since: priv-1.10 */
 #define MSTATUS_TW          0x00200000 /* since: priv-1.10 */
 #define MSTATUS_TSR         0x00400000 /* since: priv-1.10 */
+#define MSTATUS_UFCFIEN     0x00800000 /* Zisslpcfi-0.1 */
+#define MSTATUS_UBCFIEN     0x01000000 /* Zisslpcfi-0.1 */
+#define MSTATUS_SPELP       0x02000000 /* Zisslpcfi-0.1 */
+#define MSTATUS_MPELP       0x04000000 /* Zisslpcfi-0.1 */
 #define MSTATUS_GVA         0x4000000000ULL
 #define MSTATUS_MPV         0x8000000000ULL
 
@@ -572,12 +580,21 @@ typedef enum {
 #define SSTATUS_XS          0x00018000
 #define SSTATUS_SUM         0x00040000 /* since: priv-1.10 */
 #define SSTATUS_MXR         0x00080000
+#define SSTATUS_UFCFIEN     MSTATUS_UFCFIEN /* Zisslpcfi-0.1 */
+#define SSTATUS_UBCFIEN     MSTATUS_UBCFIEN /* Zisslpcfi-0.1 */
+#define SSTATUS_SPELP       MSTATUS_SPELP   /* Zisslpcfi-0.1 */
 
 #define SSTATUS64_UXL       0x0000000300000000ULL
 
 #define SSTATUS32_SD        0x80000000
 #define SSTATUS64_SD        0x8000000000000000ULL
 
+#define CFISTATUS_M_MASK    (MSTATUS_UFCFIEN | MSTATUS_UBCFIEN | \
+                             MSTATUS_MPELP | MSTATUS_SPELP)
+
+#define CFISTATUS_S_MASK    (SSTATUS_UFCFIEN | SSTATUS_UBCFIEN | \
+                             SSTATUS_SPELP)
+
 /* hstatus CSR bits */
 #define HSTATUS_VSBE         0x00000020
 #define HSTATUS_GVA          0x00000040
@@ -747,10 +764,14 @@ typedef enum RISCVException {
 #define MENVCFG_CBIE                       (3UL << 4)
 #define MENVCFG_CBCFE                      BIT(6)
 #define MENVCFG_CBZE                       BIT(7)
+#define MENVCFG_SFCFIEN                    BIT(59)
+#define MENVCFG_CFI                        BIT(60)
 #define MENVCFG_PBMTE                      (1ULL << 62)
 #define MENVCFG_STCE                       (1ULL << 63)
 
 /* For RV32 */
+#define MENVCFGH_SFCFIEN                   BIT(27)
+#define MENVCFGH_CFI                       BIT(28)
 #define MENVCFGH_PBMTE                     BIT(30)
 #define MENVCFGH_STCE                      BIT(31)
 
@@ -763,10 +784,14 @@ typedef enum RISCVException {
 #define HENVCFG_CBIE                       MENVCFG_CBIE
 #define HENVCFG_CBCFE                      MENVCFG_CBCFE
 #define HENVCFG_CBZE                       MENVCFG_CBZE
+#define HENVCFG_SFCFIEN                    MENVCFG_SFCFIEN
+#define HENVCFG_CFI                        MENVCFG_CFI
 #define HENVCFG_PBMTE                      MENVCFG_PBMTE
 #define HENVCFG_STCE                       MENVCFG_STCE
 
 /* For RV32 */
+#define HENVCFGH_SFCFIEN                    MENVCFGH_SFCFIEN
+#define HENVCFGH_CFI                        MENVCFGH_CFI
 #define HENVCFGH_PBMTE                      MENVCFGH_PBMTE
 #define HENVCFGH_STCE                       MENVCFGH_STCE
 
diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h
index da32c61c85..f5bfc4955b 100644
--- a/target/riscv/pmp.h
+++ b/target/riscv/pmp.h
@@ -43,7 +43,8 @@ typedef enum {
     MSECCFG_MMWP  = 1 << 1,
     MSECCFG_RLB   = 1 << 2,
     MSECCFG_USEED = 1 << 8,
-    MSECCFG_SSEED = 1 << 9
+    MSECCFG_SSEED = 1 << 9,
+    MSECCFG_MFCFIEN =  1 << 10
 } mseccfg_field_t;
 
 typedef struct {
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v1 RFC Zisslpcfi 3/9] target/riscv: implements CSRs and new bits in existing CSRs in zisslpcfi
       [not found] <20230209062404.3582018-1-debug@rivosinc.com>
  2023-02-09  6:23 ` [PATCH v1 RFC Zisslpcfi 1/9] target/riscv: adding zimops and zisslpcfi extension to RISCV cpu config Deepak Gupta
  2023-02-09  6:23 ` [PATCH v1 RFC Zisslpcfi 2/9] target/riscv: zisslpcfi CSR, bit positions and other definitions Deepak Gupta
@ 2023-02-09  6:23 ` Deepak Gupta
  2023-02-15  5:47   ` LIU Zhiwei
  2023-02-09  6:23 ` [PATCH v1 RFC Zisslpcfi 4/9] target/riscv: helper functions for forward and backward cfi Deepak Gupta
                   ` (5 subsequent siblings)
  8 siblings, 1 reply; 42+ messages in thread
From: Deepak Gupta @ 2023-02-09  6:23 UTC (permalink / raw)
  To: Palmer Dabbelt, Alistair Francis, Bin Meng
  Cc: Deepak Gupta, Kip Walker, qemu-riscv, qemu-devel

CSR_SSP and CSR_LPLR are new CSR additions to cpu/hart. This patch allows
access to these CSRs. A predicate routine handles access to these CSR as
per specification.

This patch also implments new bit definitions in menvcfg/henvcfg/mstatus/
sstatus CSRs to master enabled cfi and enable forward cfi in S and M mode.
mstatus CSR holds forward and backward cfi enabling for U mode.

There is no enabling bit for backward cfi in S and M mode. It is always
enabled if extension is implemented by CPU.

Signed-off-by: Deepak Gupta <debug@rivosinc.com>
Signed-off-by: Kip Walker  <kip@rivosinc.com>
---
 target/riscv/csr.c | 137 ++++++++++++++++++++++++++++++++++++++++++++-
 target/riscv/pmp.c |   9 +++
 2 files changed, 145 insertions(+), 1 deletion(-)

diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index 0db2c233e5..24e208ebed 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -163,6 +163,50 @@ static RISCVException ctr32(CPURISCVState *env, int csrno)
     return ctr(env, csrno);
 }
 
+static RISCVException cfi(CPURISCVState *env, int csrno)
+{
+    /* no cfi extension */
+    if (!env_archcpu(env)->cfg.ext_cfi) {
+        return RISCV_EXCP_ILLEGAL_INST;
+    }
+    /*
+     * CONFIG_USER_MODE always allow access for now. Better for user mode only
+     * functionality
+     */
+#if !defined(CONFIG_USER_ONLY)
+    /* current priv not M */
+    if (env->priv != PRV_M) {
+        /* menvcfg says no CFI */
+        if (!get_field(env->menvcfg, MENVCFG_CFI)) {
+            return RISCV_EXCP_ILLEGAL_INST;
+        }
+
+        /* V = 1 and henvcfg says no CFI. raise virtual instr fault */
+        if (riscv_cpu_virt_enabled(env) &&
+            !get_field(env->henvcfg, HENVCFG_CFI)) {
+            return RISCV_EXCP_VIRT_INSTRUCTION_FAULT;
+        }
+
+        /*
+         * LPLR and SSP are not accessible to U mode if disabled via status
+         * CSR
+         */
+        if (env->priv == PRV_U) {
+            if (csrno == CSR_LPLR &&
+                !get_field(env->mstatus, MSTATUS_UFCFIEN)) {
+                return RISCV_EXCP_ILLEGAL_INST;
+            }
+            if (csrno == CSR_SSP &&
+                !get_field(env->mstatus, MSTATUS_UBCFIEN)) {
+                return RISCV_EXCP_ILLEGAL_INST;
+            }
+        }
+    }
+#endif
+
+    return RISCV_EXCP_NONE;
+}
+
 #if !defined(CONFIG_USER_ONLY)
 static RISCVException mctr(CPURISCVState *env, int csrno)
 {
@@ -485,6 +529,32 @@ static RISCVException seed(CPURISCVState *env, int csrno)
 #endif
 }
 
+/* Zisslpcfi CSR_LPLR read/write */
+static int read_lplr(CPURISCVState *env, int csrno, target_ulong *val)
+{
+    *val = env->lplr;
+    return RISCV_EXCP_NONE;
+}
+
+static int write_lplr(CPURISCVState *env, int csrno, target_ulong val)
+{
+    env->lplr = val & (LPLR_UL | LPLR_ML | LPLR_LL);
+    return RISCV_EXCP_NONE;
+}
+
+/* Zisslpcfi CSR_SSP read/write */
+static int read_ssp(CPURISCVState *env, int csrno, target_ulong *val)
+{
+    *val = env->ssp;
+    return RISCV_EXCP_NONE;
+}
+
+static int write_ssp(CPURISCVState *env, int csrno, target_ulong val)
+{
+    env->ssp = val;
+    return RISCV_EXCP_NONE;
+}
+
 /* User Floating-Point CSRs */
 static RISCVException read_fflags(CPURISCVState *env, int csrno,
                                   target_ulong *val)
@@ -1227,7 +1297,7 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno,
 
     /* flush tlb on mstatus fields that affect VM */
     if ((val ^ mstatus) & (MSTATUS_MXR | MSTATUS_MPP | MSTATUS_MPV |
-            MSTATUS_MPRV | MSTATUS_SUM)) {
+            MSTATUS_MPRV | MSTATUS_SUM | MSTATUS_UFCFIEN | MSTATUS_UBCFIEN)) {
         tlb_flush(env_cpu(env));
     }
     mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE |
@@ -1250,6 +1320,11 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno,
         }
     }
 
+    /* If cfi extension is available, then apply cfi status mask */
+    if (env_archcpu(env)->cfg.ext_cfi) {
+        mask |= CFISTATUS_M_MASK;
+    }
+
     mstatus = (mstatus & ~mask) | (val & mask);
 
     if (xl > MXL_RV32) {
@@ -1880,9 +1955,17 @@ static RISCVException write_menvcfg(CPURISCVState *env, int csrno,
                                   target_ulong val)
 {
     uint64_t mask = MENVCFG_FIOM | MENVCFG_CBIE | MENVCFG_CBCFE | MENVCFG_CBZE;
+    uint64_t cfi_mask = MENVCFG_CFI | MENVCFG_SFCFIEN;
 
     if (riscv_cpu_mxl(env) == MXL_RV64) {
         mask |= MENVCFG_PBMTE | MENVCFG_STCE;
+        if (env_archcpu(env)->cfg.ext_cfi) {
+            mask |= cfi_mask;
+            /* If any cfi enabling bit changes in menvcfg, flush tlb */
+            if ((val ^ env->menvcfg) & cfi_mask) {
+                tlb_flush(env_cpu(env));
+            }
+        }
     }
     env->menvcfg = (env->menvcfg & ~mask) | (val & mask);
 
@@ -1900,8 +1983,17 @@ static RISCVException write_menvcfgh(CPURISCVState *env, int csrno,
                                   target_ulong val)
 {
     uint64_t mask = MENVCFG_PBMTE | MENVCFG_STCE;
+    uint64_t cfi_mask = MENVCFG_CFI | MENVCFG_SFCFIEN;
     uint64_t valh = (uint64_t)val << 32;
 
+    if (env_archcpu(env)->cfg.ext_cfi) {
+            mask |= cfi_mask;
+            /* If any cfi enabling bit changes in menvcfg, flush tlb */
+            if ((val ^ env->menvcfg) & cfi_mask) {
+                tlb_flush(env_cpu(env));
+            }
+    }
+
     env->menvcfg = (env->menvcfg & ~mask) | (valh & mask);
 
     return RISCV_EXCP_NONE;
@@ -1954,6 +2046,7 @@ static RISCVException write_henvcfg(CPURISCVState *env, int csrno,
                                   target_ulong val)
 {
     uint64_t mask = HENVCFG_FIOM | HENVCFG_CBIE | HENVCFG_CBCFE | HENVCFG_CBZE;
+    uint64_t cfi_mask = HENVCFG_CFI | HENVCFG_SFCFIEN;
     RISCVException ret;
 
     ret = smstateen_acc_ok(env, 0, SMSTATEEN0_HSENVCFG);
@@ -1963,6 +2056,18 @@ static RISCVException write_henvcfg(CPURISCVState *env, int csrno,
 
     if (riscv_cpu_mxl(env) == MXL_RV64) {
         mask |= HENVCFG_PBMTE | HENVCFG_STCE;
+        /*
+         * If cfi available and menvcfg.CFI = 1, then apply cfi mask for
+         * henvcfg
+         */
+        if (env_archcpu(env)->cfg.ext_cfi &&
+            get_field(env->menvcfg, MENVCFG_CFI)) {
+            mask |= cfi_mask;
+            /* If any cfi enabling bit changes in henvcfg, flush tlb */
+            if ((val ^ env->henvcfg) & cfi_mask) {
+                tlb_flush(env_cpu(env));
+            }
+        }
     }
 
     env->henvcfg = (env->henvcfg & ~mask) | (val & mask);
@@ -1988,9 +2093,19 @@ static RISCVException write_henvcfgh(CPURISCVState *env, int csrno,
                                   target_ulong val)
 {
     uint64_t mask = HENVCFG_PBMTE | HENVCFG_STCE;
+    uint64_t cfi_mask = HENVCFG_CFI | HENVCFG_SFCFIEN;
     uint64_t valh = (uint64_t)val << 32;
     RISCVException ret;
 
+    if (env_archcpu(env)->cfg.ext_cfi &&
+        get_field(env->menvcfg, MENVCFG_CFI)) {
+        mask |= cfi_mask;
+        /* If any cfi enabling bit changes in henvcfg, flush tlb */
+        if ((val ^ env->henvcfg) & cfi_mask) {
+            tlb_flush(env_cpu(env));
+        }
+    }
+
     ret = smstateen_acc_ok(env, 0, SMSTATEEN0_HSENVCFG);
     if (ret != RISCV_EXCP_NONE) {
         return ret;
@@ -2270,6 +2385,11 @@ static RISCVException read_sstatus_i128(CPURISCVState *env, int csrno,
         mask |= SSTATUS64_UXL;
     }
 
+    if ((env_archcpu(env)->cfg.ext_cfi) &&
+         get_field(env->menvcfg, MENVCFG_CFI)) {
+        mask |= CFISTATUS_S_MASK;
+    }
+
     *val = int128_make128(sstatus, add_status_sd(MXL_RV128, sstatus));
     return RISCV_EXCP_NONE;
 }
@@ -2281,6 +2401,11 @@ static RISCVException read_sstatus(CPURISCVState *env, int csrno,
     if (env->xl != MXL_RV32 || env->debugger) {
         mask |= SSTATUS64_UXL;
     }
+
+    if ((env_archcpu(env)->cfg.ext_cfi) &&
+         get_field(env->menvcfg, MENVCFG_CFI)) {
+        mask |= CFISTATUS_S_MASK;
+    }
     /* TODO: Use SXL not MXL. */
     *val = add_status_sd(riscv_cpu_mxl(env), env->mstatus & mask);
     return RISCV_EXCP_NONE;
@@ -2296,6 +2421,12 @@ static RISCVException write_sstatus(CPURISCVState *env, int csrno,
             mask |= SSTATUS64_UXL;
         }
     }
+
+    /* If cfi available and menvcfg.CFI = 1, apply CFI mask for sstatus */
+    if ((env_archcpu(env)->cfg.ext_cfi) &&
+         get_field(env->menvcfg, MENVCFG_CFI)) {
+        mask |= CFISTATUS_S_MASK;
+    }
     target_ulong newval = (env->mstatus & ~mask) | (val & mask);
     return write_mstatus(env, CSR_MSTATUS, newval);
 }
@@ -4001,6 +4132,10 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
     /* Crypto Extension */
     [CSR_SEED] = { "seed", seed, NULL, NULL, rmw_seed },
 
+    /* User mode CFI CSR */
+    [CSR_LPLR] = { "lplr", cfi, read_lplr, write_lplr },
+    [CSR_SSP]  = { "ssp", cfi, read_ssp, write_ssp },
+
 #if !defined(CONFIG_USER_ONLY)
     /* Machine Timers and Counters */
     [CSR_MCYCLE]    = { "mcycle",    any,   read_hpmcounter,
diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c
index d1126a6066..89745d46cd 100644
--- a/target/riscv/pmp.c
+++ b/target/riscv/pmp.c
@@ -579,6 +579,15 @@ void mseccfg_csr_write(CPURISCVState *env, target_ulong val)
     /* Sticky bits */
     val |= (env->mseccfg & (MSECCFG_MMWP | MSECCFG_MML));
 
+    /* M-mode forward cfi to be enabled if cfi extension is implemented */
+    if (env_archcpu(env)->cfg.ext_cfi) {
+        val |= (val & MSECCFG_MFCFIEN);
+        /* If forward cfi in mseccfg is being toggled, flush tlb */
+        if ((env->mseccfg ^ val) & MSECCFG_MFCFIEN) {
+                tlb_flush(env_cpu(env));
+        }
+    }
+
     env->mseccfg = val;
 }
 
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v1 RFC Zisslpcfi 4/9] target/riscv: helper functions for forward and backward cfi
       [not found] <20230209062404.3582018-1-debug@rivosinc.com>
                   ` (2 preceding siblings ...)
  2023-02-09  6:23 ` [PATCH v1 RFC Zisslpcfi 3/9] target/riscv: implements CSRs and new bits in existing CSRs in zisslpcfi Deepak Gupta
@ 2023-02-09  6:23 ` Deepak Gupta
  2023-02-15  6:26   ` LIU Zhiwei
  2023-02-09  6:24 ` [PATCH v1 RFC Zisslpcfi 5/9] target/riscv: state save and restore of zisslppcfi state Deepak Gupta
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 42+ messages in thread
From: Deepak Gupta @ 2023-02-09  6:23 UTC (permalink / raw)
  To: Palmer Dabbelt, Alistair Francis, Bin Meng
  Cc: Deepak Gupta, Kip Walker, qemu-riscv, qemu-devel

Implementation for forward cfi and backward cfi needs helper function
to determine if currently fcfi and bcfi are enabled. Enable depends on
privilege mode and settings in sstatus/menvcfg/henvcfg/mseccfg CSRs.

Signed-off-by: Deepak Gupta <debug@rivosinc.com>
Signed-off-by: Kip Walker  <kip@rivosinc.com>
---
 target/riscv/cpu.h        |  2 ++
 target/riscv/cpu_helper.c | 51 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 53 insertions(+)

diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 18db61a06a..d14ea4f91d 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -568,6 +568,8 @@ bool riscv_cpu_virt_enabled(CPURISCVState *env);
 void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable);
 bool riscv_cpu_two_stage_lookup(int mmu_idx);
 int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch);
+bool cpu_get_fcfien(CPURISCVState *env);
+bool cpu_get_bcfien(CPURISCVState *env);
 hwaddr riscv_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
 G_NORETURN void  riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
                                                MMUAccessType access_type, int mmu_idx,
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index 9a28816521..a397023840 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -30,6 +30,7 @@
 #include "sysemu/cpu-timers.h"
 #include "cpu_bits.h"
 #include "debug.h"
+#include "pmp.h"
 
 int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch)
 {
@@ -40,6 +41,56 @@ int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch)
 #endif
 }
 
+bool cpu_get_fcfien(CPURISCVState *env)
+{
+#ifdef CONFIG_USER_ONLY
+    return false;
+#else
+    /* no cfi extension, return false */
+    if (!env_archcpu(env)->cfg.ext_cfi) {
+        return false;
+    }
+
+    switch (env->priv) {
+    case PRV_U:
+        return (env->mstatus & MSTATUS_UFCFIEN) ? true : false;
+    case PRV_S:
+        return (env->menvcfg & MENVCFG_SFCFIEN) ? true : false;
+    case PRV_M:
+        return (env->mseccfg & MSECCFG_MFCFIEN) ? true : false;
+    default:
+        g_assert_not_reached();
+    }
+#endif
+}
+
+bool cpu_get_bcfien(CPURISCVState *env)
+{
+#ifdef CONFIG_USER_ONLY
+    return false;
+#else
+    /* no cfi extension, return false */
+    if (!env_archcpu(env)->cfg.ext_cfi) {
+        return false;
+    }
+
+    switch (env->priv) {
+    case PRV_U:
+        return (env->mstatus & MSTATUS_UBCFIEN) ? true : false;
+
+        /*
+         * no gating for back cfi in M/S mode. back cfi is always on for
+         * M/S mode
+         */
+    case PRV_S:
+    case PRV_M:
+        return true;
+    default:
+        g_assert_not_reached();
+    }
+#endif
+}
+
 void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc,
                           target_ulong *cs_base, uint32_t *pflags)
 {
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v1 RFC Zisslpcfi 5/9] target/riscv: state save and restore of zisslppcfi state
       [not found] <20230209062404.3582018-1-debug@rivosinc.com>
                   ` (3 preceding siblings ...)
  2023-02-09  6:23 ` [PATCH v1 RFC Zisslpcfi 4/9] target/riscv: helper functions for forward and backward cfi Deepak Gupta
@ 2023-02-09  6:24 ` Deepak Gupta
  2023-02-15  6:10   ` LIU Zhiwei
  2023-02-09  6:24 ` [PATCH v1 RFC Zisslpcfi 6/9] target/riscv: MMU changes for back cfi's shadow stack Deepak Gupta
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 42+ messages in thread
From: Deepak Gupta @ 2023-02-09  6:24 UTC (permalink / raw)
  To: Palmer Dabbelt, Alistair Francis, Bin Meng
  Cc: Deepak Gupta, Kip Walker, qemu-riscv, qemu-devel

zisslpcfi's forward cfi if enabled on a hart, enables tracking of
indirect branches. CPU/hart internally keeps a state `elp` short
for expecting landing pad instruction. This state goes into
LP_EXPECTED on an indirect branch. But an interrupt/exception can occur
before target instruction is executed. In such a case this state must be
preserved so that it can be restored later. zisslpcfi saves elp state in
`sstatus` CSR. This patch saves elp state in sstatus CSR on trap delivery
while restores from sstatus CSR on trap return.

Additionally state in sstatus CSR must have save and restore zisslpcfi
state on exiting from hypervisor and entering into hypervisor.

Signed-off-by: Deepak Gupta <debug@rivosinc.com>
Signed-off-by: Kip Walker  <kip@rivosinc.com>
---
 target/riscv/cpu_bits.h   |  5 +++++
 target/riscv/cpu_helper.c | 26 ++++++++++++++++++++++++++
 target/riscv/op_helper.c  | 12 ++++++++++++
 3 files changed, 43 insertions(+)

diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index 1663ba5775..37100ec8f6 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -594,6 +594,11 @@ typedef enum {
 
 #define CFISTATUS_S_MASK    (SSTATUS_UFCFIEN | SSTATUS_UBCFIEN | \
                              SSTATUS_SPELP)
+/* enum for branch tracking state in cpu/hart */
+typedef enum {
+    NO_LP_EXPECTED = 0,
+    LP_EXPECTED = 1,
+} cfi_elp;
 
 /* hstatus CSR bits */
 #define HSTATUS_VSBE         0x00000020
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index a397023840..fc188683c9 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -534,6 +534,16 @@ void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env)
     if (riscv_has_ext(env, RVF)) {
         mstatus_mask |= MSTATUS_FS;
     }
+
+    /*
+     * If cfi extension available, menvcfg.CFI = 1 and henvcfg.CFI = 1,
+     * then apply CFI mask on mstatus
+     */
+    if (env_archcpu(env)->cfg.ext_cfi &&
+        get_field(env->menvcfg, MENVCFG_CFI) &&
+        get_field(env->henvcfg, HENVCFG_CFI)) {
+        mstatus_mask |= CFISTATUS_S_MASK;
+    }
     bool current_virt = riscv_cpu_virt_enabled(env);
 
     g_assert(riscv_has_ext(env, RVH));
@@ -1723,6 +1733,10 @@ void riscv_cpu_do_interrupt(CPUState *cs)
     if (env->priv <= PRV_S &&
             cause < TARGET_LONG_BITS && ((deleg >> cause) & 1)) {
         /* handle the trap in S-mode */
+        /* save elp status */
+        if (cpu_get_fcfien(env)) {
+            env->mstatus = set_field(env->mstatus, MSTATUS_SPELP, env->elp);
+        }
         if (riscv_has_ext(env, RVH)) {
             uint64_t hdeleg = async ? env->hideleg : env->hedeleg;
 
@@ -1772,6 +1786,10 @@ void riscv_cpu_do_interrupt(CPUState *cs)
         riscv_cpu_set_mode(env, PRV_S);
     } else {
         /* handle the trap in M-mode */
+        /* save elp status */
+        if (cpu_get_fcfien(env)) {
+            env->mstatus = set_field(env->mstatus, MSTATUS_MPELP, env->elp);
+        }
         if (riscv_has_ext(env, RVH)) {
             if (riscv_cpu_virt_enabled(env)) {
                 riscv_cpu_swap_hypervisor_regs(env);
@@ -1803,6 +1821,14 @@ void riscv_cpu_do_interrupt(CPUState *cs)
         riscv_cpu_set_mode(env, PRV_M);
     }
 
+    /*
+     * Interrupt/exception/trap delivery is asynchronous event and as per
+     * Zisslpcfi spec CPU should clear up the ELP state. If cfi extension is
+     * available, clear ELP state.
+     */
+    if (cpu->cfg.ext_cfi) {
+        env->elp = NO_LP_EXPECTED;
+    }
     /* NOTE: it is not necessary to yield load reservations here. It is only
      * necessary for an SC from "another hart" to cause a load reservation
      * to be yielded. Refer to the memory consistency model section of the
diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
index 878bcb03b8..d15893aa82 100644
--- a/target/riscv/op_helper.c
+++ b/target/riscv/op_helper.c
@@ -176,6 +176,12 @@ target_ulong helper_sret(CPURISCVState *env)
         riscv_cpu_set_virt_enabled(env, prev_virt);
     }
 
+    /* If forward cfi enabled for target, restore elp status */
+    if (cpu_get_fcfien(env)) {
+        env->elp = get_field(env->mstatus, MSTATUS_SPELP);
+        env->mstatus = set_field(env->mstatus, MSTATUS_SPELP, 0);
+    }
+
     riscv_cpu_set_mode(env, prev_priv);
 
     return retpc;
@@ -220,6 +226,12 @@ target_ulong helper_mret(CPURISCVState *env)
         riscv_cpu_set_virt_enabled(env, prev_virt);
     }
 
+    /* If forward cfi enabled for target, restore elp status */
+    if (cpu_get_fcfien(env)) {
+        env->elp = get_field(env->mstatus, MSTATUS_MPELP);
+        env->mstatus = set_field(env->mstatus, MSTATUS_MPELP, 0);
+    }
+
     return retpc;
 }
 
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v1 RFC Zisslpcfi 6/9] target/riscv: MMU changes for back cfi's shadow stack
       [not found] <20230209062404.3582018-1-debug@rivosinc.com>
                   ` (4 preceding siblings ...)
  2023-02-09  6:24 ` [PATCH v1 RFC Zisslpcfi 5/9] target/riscv: state save and restore of zisslppcfi state Deepak Gupta
@ 2023-02-09  6:24 ` Deepak Gupta
  2023-02-15  8:43   ` LIU Zhiwei
  2023-02-09  6:24 ` [PATCH v1 RFC Zisslpcfi 7/9] target/riscv: Tracking indirect branches (fcfi) using TCG Deepak Gupta
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 42+ messages in thread
From: Deepak Gupta @ 2023-02-09  6:24 UTC (permalink / raw)
  To: Palmer Dabbelt, Alistair Francis, Bin Meng
  Cc: Deepak Gupta, Kip Walker, qemu-riscv, qemu-devel

zisslpcfi protects returns(back cfi) using shadow stack. If compiled with
enabled compiler, function prologs will have `sspush ra` instruction to
push return address on shadow stack and function epilogs will have
`sspop t0; sschckra` instruction sequences. `sspop t0` will pop the
value from top of the shadow stack in t0. `sschckra` will compare `t0`
and `x1` and if they don't match then hart will raise an illegal
instruction exception.

Shadow stack is read-only memory except stores can be performed via
`sspush` and `ssamoswap` instructions. This requires new PTE encoding for
shadow stack. zisslpcfi uses R=0, W=1, X=0 (an existing reserved encoding
) to encode a shadow stack. If backward cfi is not enabled for current
mode, shadow stack PTE encodings remain reserved. Regular stores to
shadow stack raise AMO/store access fault. Shadow stack loads/stores on
regular memory raise load access/store access fault.

This patch creates a new MMU TLB index for shadow stack and flushes TLB
for shadow stack on privileges changes. This patch doesn't implement
`Smepmp` related enforcement on shadow stack pmp entry. Reason being qemu
doesn't have `Smepmp` implementation yet. `Smepmp` enforcement should come
whenever it is implemented.

Signed-off-by: Deepak Gupta <debug@rivosinc.com>
Signed-off-by: Kip Walker  <kip@rivosinc.com>
---
 target/riscv/cpu-param.h  |   1 +
 target/riscv/cpu.c        |   2 +
 target/riscv/cpu.h        |   3 ++
 target/riscv/cpu_helper.c | 107 +++++++++++++++++++++++++++++++-------
 4 files changed, 94 insertions(+), 19 deletions(-)

diff --git a/target/riscv/cpu-param.h b/target/riscv/cpu-param.h
index ebaf26d26d..a1e379beb7 100644
--- a/target/riscv/cpu-param.h
+++ b/target/riscv/cpu-param.h
@@ -25,6 +25,7 @@
  *  - M mode 0b011
  *  - U mode HLV/HLVX/HSV 0b100
  *  - S mode HLV/HLVX/HSV 0b101
+ *  - BCFI shadow stack   0b110
  *  - M mode HLV/HLVX/HSV 0b111
  */
 #define NB_MMU_MODES 8
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 6b4e90eb91..14cfb93288 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -584,6 +584,8 @@ static void riscv_cpu_reset_hold(Object *obj)
     }
     /* mmte is supposed to have pm.current hardwired to 1 */
     env->mmte |= (PM_EXT_INITIAL | MMTE_M_PM_CURRENT);
+    /* Initialize ss_priv to current priv. */
+    env->ss_priv = env->priv;
 #endif
     env->xl = riscv_cpu_mxl(env);
     riscv_cpu_update_mask(env);
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index d14ea4f91d..8803ea6426 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -379,6 +379,7 @@ struct CPUArchState {
     uint64_t sstateen[SMSTATEEN_MAX_COUNT];
     target_ulong senvcfg;
     uint64_t henvcfg;
+    target_ulong ss_priv;
 #endif
     target_ulong cur_pmmask;
     target_ulong cur_pmbase;
@@ -617,6 +618,8 @@ void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong);
 #define TB_FLAGS_PRIV_HYP_ACCESS_MASK   (1 << 2)
 #define TB_FLAGS_MSTATUS_FS MSTATUS_FS
 #define TB_FLAGS_MSTATUS_VS MSTATUS_VS
+/* TLB MMU index for shadow stack accesses */
+#define MMU_IDX_SS_ACCESS    6
 
 #include "exec/cpu-all.h"
 
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index fc188683c9..63377abc2f 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -657,7 +657,8 @@ void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable)
 
 bool riscv_cpu_two_stage_lookup(int mmu_idx)
 {
-    return mmu_idx & TB_FLAGS_PRIV_HYP_ACCESS_MASK;
+    return (mmu_idx & TB_FLAGS_PRIV_HYP_ACCESS_MASK) &&
+           (mmu_idx != MMU_IDX_SS_ACCESS);
 }
 
 int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts)
@@ -745,6 +746,38 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv)
      * preemptive context switch. As a result, do both.
      */
     env->load_res = -1;
+
+    if (cpu_get_bcfien(env) && (env->priv != env->ss_priv)) {
+        /*
+         * If backward CFI is enabled in the new privilege state, the
+         * shadow stack TLB needs to be flushed - unless the most recent
+         * use of the SS TLB was for the same privilege mode.
+         */
+        tlb_flush_by_mmuidx(env_cpu(env), 1 << MMU_IDX_SS_ACCESS);
+        /*
+         * Ignoring env->virt here since currently every time it flips,
+         * all TLBs are flushed anyway.
+         */
+        env->ss_priv = env->priv;
+    }
+}
+
+typedef enum {
+    SSTACK_NO,          /* Access is not for a shadow stack instruction */
+    SSTACK_YES,         /* Access is for a shadow stack instruction */
+    SSTACK_DC           /* Don't care about SS attribute in PMP */
+} SStackPmpMode;
+
+static bool legal_sstack_access(int access_type, bool sstack_inst,
+                                bool sstack_attribute)
+{
+    /*
+     * Read/write/execution permissions are checked as usual. Shadow
+     * stack enforcement is just that (1) instruction type must match
+     * the attribute unless (2) a non-SS load to an SS region.
+     */
+    return (sstack_inst == sstack_attribute) ||
+        ((access_type == MMU_DATA_LOAD) && sstack_attribute);
 }
 
 /*
@@ -764,7 +797,7 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv)
 static int get_physical_address_pmp(CPURISCVState *env, int *prot,
                                     target_ulong *tlb_size, hwaddr addr,
                                     int size, MMUAccessType access_type,
-                                    int mode)
+                                    int mode, SStackPmpMode sstack)
 {
     pmp_priv_t pmp_priv;
     int pmp_index = -1;
@@ -812,13 +845,16 @@ static int get_physical_address_pmp(CPURISCVState *env, int *prot,
  *               Second stage is used for hypervisor guest translation
  * @two_stage: Are we going to perform two stage translation
  * @is_debug: Is this access from a debugger or the monitor?
+ * @sstack: Is this access for a shadow stack? Passed by reference so
+            it can be forced to SSTACK_DC when the SS check is completed
+            based on a PTE - so the PMP SS attribute will be ignored.
  */
 static int get_physical_address(CPURISCVState *env, hwaddr *physical,
                                 int *prot, target_ulong addr,
                                 target_ulong *fault_pte_addr,
                                 int access_type, int mmu_idx,
                                 bool first_stage, bool two_stage,
-                                bool is_debug)
+                                bool is_debug, SStackPmpMode *sstack)
 {
     /* NOTE: the env->pc value visible here will not be
      * correct, but the value visible to the exception handler
@@ -830,6 +866,7 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical,
     hwaddr ppn;
     RISCVCPU *cpu = env_archcpu(env);
     int napot_bits = 0;
+    bool is_sstack = (sstack != NULL) && (*sstack == SSTACK_YES);
     target_ulong napot_mask;
 
     /*
@@ -851,6 +888,8 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical,
         if (get_field(env->mstatus, MSTATUS_MPRV)) {
             mode = get_field(env->mstatus, MSTATUS_MPP);
         }
+    } else if (mmu_idx == MMU_IDX_SS_ACCESS) {
+        mode = env->priv;
     }
 
     if (first_stage == false) {
@@ -966,7 +1005,7 @@ restart:
             int vbase_ret = get_physical_address(env, &vbase, &vbase_prot,
                                                  base, NULL, MMU_DATA_LOAD,
                                                  mmu_idx, false, true,
-                                                 is_debug);
+                                                 is_debug, NULL);
 
             if (vbase_ret != TRANSLATE_SUCCESS) {
                 if (fault_pte_addr) {
@@ -983,7 +1022,7 @@ restart:
         int pmp_prot;
         int pmp_ret = get_physical_address_pmp(env, &pmp_prot, NULL, pte_addr,
                                                sizeof(target_ulong),
-                                               MMU_DATA_LOAD, PRV_S);
+                                               MMU_DATA_LOAD, PRV_S, SSTACK_NO);
         if (pmp_ret != TRANSLATE_SUCCESS) {
             return TRANSLATE_PMP_FAIL;
         }
@@ -1010,6 +1049,18 @@ restart:
             }
         }
 
+        /*
+         * When backward CFI is enabled, the R=0, W=1, X=0 reserved encoding
+         * is used to mark Shadow Stack (SS) pages. If back CFI enabled, allow
+         * normal loads on SS pages, regular stores raise store access fault
+         * and avoid hitting the reserved-encoding case. Only shadow stack
+         * stores are allowed on SS pages. Shadow stack loads and stores on
+         * regular memory (non-SS) raise load and store/AMO access fault.
+         * Second stage translations don't participate in Shadow Stack.
+         */
+        bool sstack_page = (cpu_get_bcfien(env) && first_stage &&
+                            ((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W));
+
         if (!(pte & PTE_V)) {
             /* Invalid PTE */
             return TRANSLATE_FAIL;
@@ -1021,7 +1072,7 @@ restart:
                 return TRANSLATE_FAIL;
             }
             base = ppn << PGSHIFT;
-        } else if ((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W) {
+        } else if (((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W) && !sstack_page) {
             /* Reserved leaf PTE flags: PTE_W */
             return TRANSLATE_FAIL;
         } else if ((pte & (PTE_R | PTE_W | PTE_X)) == (PTE_W | PTE_X)) {
@@ -1038,16 +1089,21 @@ restart:
         } else if (ppn & ((1ULL << ptshift) - 1)) {
             /* Misaligned PPN */
             return TRANSLATE_FAIL;
-        } else if (access_type == MMU_DATA_LOAD && !((pte & PTE_R) ||
-                   ((pte & PTE_X) && mxr))) {
+        } else if (access_type == MMU_DATA_LOAD && !(((pte & PTE_R) ||
+                   sstack_page) || ((pte & PTE_X) && mxr))) {
             /* Read access check failed */
             return TRANSLATE_FAIL;
-        } else if (access_type == MMU_DATA_STORE && !(pte & PTE_W)) {
+        } else if ((access_type == MMU_DATA_STORE && !is_sstack) &&
+                   !(pte & PTE_W)) {
             /* Write access check failed */
             return TRANSLATE_FAIL;
         } else if (access_type == MMU_INST_FETCH && !(pte & PTE_X)) {
             /* Fetch access check failed */
             return TRANSLATE_FAIL;
+        } else if (!legal_sstack_access(access_type, is_sstack,
+                                        sstack_page)) {
+            /* Illegal combo of instruction type and page attribute */
+            return TRANSLATE_PMP_FAIL;
         } else {
             /* if necessary, set accessed and dirty bits. */
             target_ulong updated_pte = pte | PTE_A |
@@ -1107,18 +1163,27 @@ restart:
                          ) << PGSHIFT) | (addr & ~TARGET_PAGE_MASK);
 
             /* set permissions on the TLB entry */
-            if ((pte & PTE_R) || ((pte & PTE_X) && mxr)) {
+            if ((pte & PTE_R) || ((pte & PTE_X) && mxr) || sstack_page) {
                 *prot |= PAGE_READ;
             }
             if ((pte & PTE_X)) {
                 *prot |= PAGE_EXEC;
             }
-            /* add write permission on stores or if the page is already dirty,
-               so that we TLB miss on later writes to update the dirty bit */
+            /*
+             * add write permission on stores or if the page is already dirty,
+             * so that we TLB miss on later writes to update the dirty bit
+             */
             if ((pte & PTE_W) &&
                     (access_type == MMU_DATA_STORE || (pte & PTE_D))) {
                 *prot |= PAGE_WRITE;
             }
+            if (sstack) {
+                /*
+                 * Tell the caller to skip the SS bit in the PMP since we
+                 * resolved the attributes via the page table.
+                 */
+                *sstack = SSTACK_DC;
+            }
             return TRANSLATE_SUCCESS;
         }
     }
@@ -1190,13 +1255,13 @@ hwaddr riscv_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
     int mmu_idx = cpu_mmu_index(&cpu->env, false);
 
     if (get_physical_address(env, &phys_addr, &prot, addr, NULL, 0, mmu_idx,
-                             true, riscv_cpu_virt_enabled(env), true)) {
+                             true, riscv_cpu_virt_enabled(env), true, NULL)) {
         return -1;
     }
 
     if (riscv_cpu_virt_enabled(env)) {
         if (get_physical_address(env, &phys_addr, &prot, phys_addr, NULL,
-                                 0, mmu_idx, false, true, true)) {
+                                 0, mmu_idx, false, true, true, NULL)) {
             return -1;
         }
     }
@@ -1291,6 +1356,8 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
     bool two_stage_indirect_error = false;
     int ret = TRANSLATE_FAIL;
     int mode = mmu_idx;
+    bool sstack = (mmu_idx == MMU_IDX_SS_ACCESS);
+    SStackPmpMode ssmode = sstack ? SSTACK_YES : SSTACK_NO;
     /* default TLB page size */
     target_ulong tlb_size = TARGET_PAGE_SIZE;
 
@@ -1318,7 +1385,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
         /* Two stage lookup */
         ret = get_physical_address(env, &pa, &prot, address,
                                    &env->guest_phys_fault_addr, access_type,
-                                   mmu_idx, true, true, false);
+                                   mmu_idx, true, true, false, &ssmode);
 
         /*
          * A G-stage exception may be triggered during two state lookup.
@@ -1342,7 +1409,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
 
             ret = get_physical_address(env, &pa, &prot2, im_address, NULL,
                                        access_type, mmu_idx, false, true,
-                                       false);
+                                       false, NULL);
 
             qemu_log_mask(CPU_LOG_MMU,
                     "%s 2nd-stage address=%" VADDR_PRIx " ret %d physical "
@@ -1353,7 +1420,8 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
 
             if (ret == TRANSLATE_SUCCESS) {
                 ret = get_physical_address_pmp(env, &prot_pmp, &tlb_size, pa,
-                                               size, access_type, mode);
+                                               size, access_type, mode,
+                                               SSTACK_NO);
 
                 qemu_log_mask(CPU_LOG_MMU,
                               "%s PMP address=" HWADDR_FMT_plx " ret %d prot"
@@ -1377,7 +1445,8 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
     } else {
         /* Single stage lookup */
         ret = get_physical_address(env, &pa, &prot, address, NULL,
-                                   access_type, mmu_idx, true, false, false);
+                                   access_type, mmu_idx, true, false,
+                                   false, &ssmode);
 
         qemu_log_mask(CPU_LOG_MMU,
                       "%s address=%" VADDR_PRIx " ret %d physical "
@@ -1386,7 +1455,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
 
         if (ret == TRANSLATE_SUCCESS) {
             ret = get_physical_address_pmp(env, &prot_pmp, &tlb_size, pa,
-                                           size, access_type, mode);
+                                           size, access_type, mode, ssmode);
 
             qemu_log_mask(CPU_LOG_MMU,
                           "%s PMP address=" HWADDR_FMT_plx " ret %d prot"
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v1 RFC Zisslpcfi 7/9] target/riscv: Tracking indirect branches (fcfi) using TCG
       [not found] <20230209062404.3582018-1-debug@rivosinc.com>
                   ` (5 preceding siblings ...)
  2023-02-09  6:24 ` [PATCH v1 RFC Zisslpcfi 6/9] target/riscv: MMU changes for back cfi's shadow stack Deepak Gupta
@ 2023-02-09  6:24 ` Deepak Gupta
  2023-02-15  8:55   ` LIU Zhiwei
  2023-02-16  6:05   ` Richard Henderson
  2023-02-09  6:24 ` [PATCH v1 RFC Zisslpcfi 8/9] target/riscv: Instructions encodings, implementation and handlers Deepak Gupta
  2023-02-09  6:24 ` [PATCH v1 RFC Zisslpcfi 9/9] target/riscv: diassembly support for zisslpcfi instructions Deepak Gupta
  8 siblings, 2 replies; 42+ messages in thread
From: Deepak Gupta @ 2023-02-09  6:24 UTC (permalink / raw)
  To: Palmer Dabbelt, Alistair Francis, Bin Meng
  Cc: Deepak Gupta, Kip Walker, qemu-riscv, qemu-devel

zisslpcfi protects forward control flow (if enabled) by enforcing all
indirect call and jmp must land on a landing pad instruction `lpcll`
short for landing pad and check lower label value. If target of an
indirect call or jmp is not `lpcll` then cpu/hart must raise an illegal
instruction exception.

This patch implements the mechanism using TCG. Target architecture branch
instruction must define the end of a TB. Using this property, during
translation of branch instruction, TB flag = FCFI_LP_EXPECTED can be set.
Translation of target TB can check if FCFI_LP_EXPECTED flag is set and a
flag (fcfi_lp_expected) can be set in DisasContext. If `lpcll` gets
translated, fcfi_lp_expected flag in DisasContext can be cleared. Else
it'll fault.

This patch also also adds flag for forward and backward cfi in
DisasContext.

Signed-off-by: Deepak Gupta <debug@rivosinc.com>
Signed-off-by: Kip Walker  <kip@rivosinc.com>
---
 target/riscv/cpu.h        |  3 +++
 target/riscv/cpu_helper.c | 12 +++++++++
 target/riscv/translate.c  | 52 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 67 insertions(+)

diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 8803ea6426..98b272bcad 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -644,6 +644,9 @@ FIELD(TB_FLAGS, VMA, 25, 1)
 /* Native debug itrigger */
 FIELD(TB_FLAGS, ITRIGGER, 26, 1)
 
+/* Zisslpcfi needs a TB flag to track indirect branches */
+FIELD(TB_FLAGS, FCFI_LP_EXPECTED, 27, 1)
+
 #ifdef TARGET_RISCV32
 #define riscv_cpu_mxl(env)  ((void)(env), MXL_RV32)
 #else
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index 63377abc2f..d15918f534 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -129,6 +129,18 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc,
         flags = FIELD_DP32(flags, TB_FLAGS, VILL, 1);
     }
 
+    if (cpu->cfg.ext_cfi) {
+        /*
+         * For Forward CFI, only the expectation of a lpcll at
+         * the start of the block is tracked (which can only happen
+         * when FCFI is enabled for the current processor mode). A jump
+         * or call at the end of the previous TB will have updated
+         * env->elp to indicate the expectation.
+         */
+        flags = FIELD_DP32(flags, TB_FLAGS, FCFI_LP_EXPECTED,
+                           env->elp != NO_LP_EXPECTED);
+    }
+
 #ifdef CONFIG_USER_ONLY
     flags |= TB_FLAGS_MSTATUS_FS;
     flags |= TB_FLAGS_MSTATUS_VS;
diff --git a/target/riscv/translate.c b/target/riscv/translate.c
index df38db7553..7d43d20fc3 100644
--- a/target/riscv/translate.c
+++ b/target/riscv/translate.c
@@ -41,6 +41,7 @@ static TCGv load_val;
 /* globals for PM CSRs */
 static TCGv pm_mask;
 static TCGv pm_base;
+static TCGOp *cfi_lp_check;
 
 #include "exec/gen-icount.h"
 
@@ -116,6 +117,10 @@ typedef struct DisasContext {
     bool itrigger;
     /* TCG of the current insn_start */
     TCGOp *insn_start;
+    /* CFI extension */
+    bool bcfi_enabled;
+    bool fcfi_enabled;
+    bool fcfi_lp_expected;
 } DisasContext;
 
 static inline bool has_ext(DisasContext *ctx, uint32_t ext)
@@ -1166,11 +1171,44 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
     ctx->pm_mask_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_MASK_ENABLED);
     ctx->pm_base_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_BASE_ENABLED);
     ctx->itrigger = FIELD_EX32(tb_flags, TB_FLAGS, ITRIGGER);
+    ctx->bcfi_enabled = cpu_get_bcfien(env);
+    ctx->fcfi_enabled = cpu_get_fcfien(env);
+    ctx->fcfi_lp_expected = FIELD_EX32(tb_flags, TB_FLAGS, FCFI_LP_EXPECTED);
     ctx->zero = tcg_constant_tl(0);
 }
 
 static void riscv_tr_tb_start(DisasContextBase *db, CPUState *cpu)
 {
+    DisasContext *ctx = container_of(db, DisasContext, base);
+
+    if (ctx->fcfi_lp_expected) {
+        /*
+         * Since we can't look ahead to confirm that the first
+         * instruction is a legal landing pad instruction, emit
+         * compare-and-branch sequence that will be fixed-up in
+         * riscv_tr_tb_stop() to either statically hit or skip an
+         * illegal instruction exception depending on whether the
+         * flag was lowered by translation of a CJLP or JLP as
+         * the first instruction in the block.
+         */
+        TCGv_i32 immediate;
+        TCGLabel *l;
+        l = gen_new_label();
+        immediate = tcg_temp_local_new_i32();
+        tcg_gen_movi_i32(immediate, 0);
+        cfi_lp_check = tcg_last_op();
+        tcg_gen_brcondi_i32(TCG_COND_EQ, immediate, 0, l);
+        tcg_temp_free_i32(immediate);
+        gen_exception_illegal(ctx);
+        gen_set_label(l);
+        /*
+         * Despite the use of gen_exception_illegal(), the rest of
+         * the TB needs to be generated. The TCG optimizer will
+         * clean things up depending on which path ends up being
+         * active.
+         */
+        ctx->base.is_jmp = DISAS_NEXT;
+    }
 }
 
 static void riscv_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu)
@@ -1225,6 +1263,7 @@ static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
 static void riscv_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
 {
     DisasContext *ctx = container_of(dcbase, DisasContext, base);
+    CPURISCVState *env = cpu->env_ptr;
 
     switch (ctx->base.is_jmp) {
     case DISAS_TOO_MANY:
@@ -1235,6 +1274,19 @@ static void riscv_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
     default:
         g_assert_not_reached();
     }
+
+    if (ctx->fcfi_lp_expected) {
+        /*
+         * If the "lp expected" flag is still up, the block needs to take an
+         * illegal instruction exception.
+         */
+        tcg_set_insn_param(cfi_lp_check, 1, tcgv_i32_arg(tcg_constant_i32(1)));
+    } else {
+        /*
+        * LP instruction requirement was met, clear up LP expected
+        */
+        env->elp = NO_LP_EXPECTED;
+    }
 }
 
 static void riscv_tr_disas_log(const DisasContextBase *dcbase,
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v1 RFC Zisslpcfi 8/9] target/riscv: Instructions encodings, implementation and handlers
       [not found] <20230209062404.3582018-1-debug@rivosinc.com>
                   ` (6 preceding siblings ...)
  2023-02-09  6:24 ` [PATCH v1 RFC Zisslpcfi 7/9] target/riscv: Tracking indirect branches (fcfi) using TCG Deepak Gupta
@ 2023-02-09  6:24 ` Deepak Gupta
  2023-02-15 10:43   ` LIU Zhiwei
  2023-02-09  6:24 ` [PATCH v1 RFC Zisslpcfi 9/9] target/riscv: diassembly support for zisslpcfi instructions Deepak Gupta
  8 siblings, 1 reply; 42+ messages in thread
From: Deepak Gupta @ 2023-02-09  6:24 UTC (permalink / raw)
  To: Palmer Dabbelt, Alistair Francis, Bin Meng
  Cc: Deepak Gupta, Kip Walker, qemu-riscv, qemu-devel

This patch implements instruction encodings for zisslpcfi instructions.
Additionally this patch implements zimops encodings as well. If Zisslpcfi
is supported by CPU but not enabled then all Zisslpcfi instructions
default to Zimops instuction behavior i.e. mov 0 to rd.

zisslpcfi defines following instructions.
- Backward control flow
    - sspush x1/x5 : Decrement shadow stack pointer and pushes x1 or x5
                     on shadow stack.
    - sspop x1/x5 : Pops from shadow stack into x1 or x5. Increments
                    shadow stack pointer.
    - ssprr : Reads current shadow stack pointer into a destination
              register.
    - sscheckra : Compares x1 with x5. Raises illegal instr fault if
                x1 != x5
    - ssamoswap : Atomically swaps value on top of shadow stack

- Forward control flow
    - lpsll, lpsml, lpsul : sets lower (9bit), mid (8bit) and upper
                            (8bit) label values in CSR_ULLP respectively.
    - lpcll, lpcml, lpcul : checks lower (9bit), mid (8bit) and upper
                            (8bit) label values with CSR_ULLP
                            respectively.
    Check label instructions raise illegal instruction fault when labels
    mismatch.

Signed-off-by: Deepak Gupta <debug@rivosinc.com>
Signed-off-by: Kip Walker  <kip@rivosinc.com>
---
 target/riscv/cpu_bits.h                       |  10 +
 target/riscv/helper.h                         |   7 +
 target/riscv/insn32.decode                    |  29 ++
 target/riscv/insn_trans/trans_rvi.c.inc       |  14 +
 target/riscv/insn_trans/trans_zimops.c.inc    |  53 +++
 target/riscv/insn_trans/trans_zisslpcfi.c.inc | 310 ++++++++++++++++++
 target/riscv/op_helper.c                      |  67 ++++
 target/riscv/translate.c                      |   2 +
 8 files changed, 492 insertions(+)
 create mode 100644 target/riscv/insn_trans/trans_zimops.c.inc
 create mode 100644 target/riscv/insn_trans/trans_zisslpcfi.c.inc

diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index 37100ec8f6..b2d527c626 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -600,6 +600,16 @@ typedef enum {
     LP_EXPECTED = 1,
 } cfi_elp;
 
+#define LPLR_UL            (((1 << 8) - 1) << 17)
+#define LPLR_ML            (((1 << 8) - 1) << 9)
+#define LPLR_LL            ((1 << 9) - 1)
+
+typedef enum {
+    FCFI_LPLL = 0,
+    FCFI_ML = 1,
+    FCFI_UL = 2,
+} cfi_label_inst;
+
 /* hstatus CSR bits */
 #define HSTATUS_VSBE         0x00000020
 #define HSTATUS_GVA          0x00000040
diff --git a/target/riscv/helper.h b/target/riscv/helper.h
index 227c7122ef..6484415612 100644
--- a/target/riscv/helper.h
+++ b/target/riscv/helper.h
@@ -97,6 +97,11 @@ DEF_HELPER_FLAGS_2(fcvt_h_l, TCG_CALL_NO_RWG, i64, env, tl)
 DEF_HELPER_FLAGS_2(fcvt_h_lu, TCG_CALL_NO_RWG, i64, env, tl)
 DEF_HELPER_FLAGS_2(fclass_h, TCG_CALL_NO_RWG_SE, tl, env, i64)
 
+/* Forward CFI label checking */
+DEF_HELPER_2(cfi_jalr, void, env, int)
+DEF_HELPER_3(cfi_check_landing_pad, void, env, int, int)
+DEF_HELPER_3(cfi_set_landing_pad, void, env, int, int)
+
 /* Special functions */
 DEF_HELPER_2(csrr, tl, env, int)
 DEF_HELPER_3(csrw, void, env, int, tl)
@@ -112,6 +117,8 @@ DEF_HELPER_1(tlb_flush, void, env)
 /* Native Debug */
 DEF_HELPER_1(itrigger_match, void, env)
 #endif
+/* helper for back cfi mismatch */
+DEF_HELPER_1(sschkra_mismatch, void, env)
 
 /* Hypervisor functions */
 #ifndef CONFIG_USER_ONLY
diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode
index b7e7613ea2..cd734f03ae 100644
--- a/target/riscv/insn32.decode
+++ b/target/riscv/insn32.decode
@@ -37,6 +37,8 @@
 %imm_u    12:s20                 !function=ex_shift_12
 %imm_bs   30:2                   !function=ex_shift_3
 %imm_rnum 20:4
+%imm_cfi9  15:9
+%imm_cfi8  15:8
 
 # Argument sets:
 &empty
@@ -163,6 +165,33 @@ csrrwi   ............     ..... 101 ..... 1110011 @csr
 csrrsi   ............     ..... 110 ..... 1110011 @csr
 csrrci   ............     ..... 111 ..... 1110011 @csr
 
+# zimops (unpriv integer may be operations) instructions with system opcode
+# These're superset of for cfi encodings. zimops_r and zimops_rr should be last
+# entry in below overlapping patterns so that it acts as final sink for overlapping patterns.
+# Any new encoding that can be used should be placed above mop.r and mop.rr
+
+# cfi instructions carved out of mop.r
+{
+  sspush    100000 0 11100     ..... 100 00000 1110011 %rs1
+  sspop     100000 0 11100     00000 100 ..... 1110011 %rd
+  ssprr     100000 0 11101     00000 100 ..... 1110011 %rd
+  zimops_r  1-00-- 0 111--     ----- 100 ..... 1110011 %rd
+}
+
+# cfi instructions carved out of mop.rr
+{
+  sschckra  100010 1 00001     00101 100 00000 1110011
+  ssamoswap 100000 1 .....     ..... 100 ..... 1110011 @r
+
+  lpsll     100000 1 0 .........    100 00000 1110011 %imm_cfi9
+  lpcll     100000 1 1 .........    100 00000 1110011 %imm_cfi9
+  lpsml     100001 1 0 0........    100 00000 1110011 %imm_cfi8
+  lpcml     100001 1 0 1........    100 00000 1110011 %imm_cfi8
+  lpsul     100010 1 1 0........    100 00000 1110011 %imm_cfi8
+  lpcul     100010 1 1 1........    100 00000 1110011 %imm_cfi8
+  zimops_rr 1-00-- 1 - ---------    100 ..... 1110011 %rd
+}
+
 # *** RV64I Base Instruction Set (in addition to RV32I) ***
 lwu      ............   ..... 110 ..... 0000011 @i
 ld       ............   ..... 011 ..... 0000011 @i
diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc
index 5c69b88d1e..4a4f1ca778 100644
--- a/target/riscv/insn_trans/trans_rvi.c.inc
+++ b/target/riscv/insn_trans/trans_rvi.c.inc
@@ -66,6 +66,20 @@ static bool trans_jalr(DisasContext *ctx, arg_jalr *a)
     }
 
     gen_set_gpri(ctx, a->rd, ctx->pc_succ_insn);
+
+    if (ctx->cfg_ptr->ext_cfi) {
+        /*
+         * Rely on a helper to check the forward CFI enable for the
+         * current process mode. The alternatives would be (1) include
+         * "fcfi enabled" in the cflags or (2) maintain a "fcfi
+         * currently enabled" in cpu_env and emit TCG code to access
+         * and test it.
+         */
+        if (a->rd == xRA || a->rd == xT0 || (a->rs1 != xRA && a->rs1 != xT0)) {
+            gen_helper_cfi_jalr(cpu_env, tcg_constant_i32(LP_EXPECTED));
+        }
+    }
+
     lookup_and_goto_ptr(ctx);
 
     if (misaligned) {
diff --git a/target/riscv/insn_trans/trans_zimops.c.inc b/target/riscv/insn_trans/trans_zimops.c.inc
new file mode 100644
index 0000000000..51748637b9
--- /dev/null
+++ b/target/riscv/insn_trans/trans_zimops.c.inc
@@ -0,0 +1,53 @@
+/*
+ * RISC-V translation routines for the Control-Flow Integrity Extension
+ *
+ * Copyright (c) 2022-2023 Rivos Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+ static bool trans_zimops_r(DisasContext *ctx, arg_zimops_r * a)
+ {
+      /* zimops not implemented, raise illegal instruction & return true */
+      if (!ctx->cfg_ptr->ext_zimops) {
+            gen_exception_illegal(ctx);
+            return true;
+      }
+
+      /*
+       * zimops implemented, simply grab destination and mov zero.
+       * return true
+       */
+      TCGv dest = dest_gpr(ctx, a->rd);
+      dest = tcg_const_tl(0);
+      gen_set_gpr(ctx, a->rd, dest);
+      return true;
+ }
+
+ static bool trans_zimops_rr(DisasContext *ctx, arg_zimops_rr * a)
+ {
+      /* zimops not implemented, raise illegal instruction & return true */
+      if (!ctx->cfg_ptr->ext_zimops) {
+            gen_exception_illegal(ctx);
+            return true;
+      }
+
+      /*
+       * zimops implemented, simply grab destination and mov zero.
+       * return true
+       */
+      TCGv dest = dest_gpr(ctx, a->rd);
+      dest = tcg_const_tl(0);
+      gen_set_gpr(ctx, a->rd, dest);
+      return true;
+ }
diff --git a/target/riscv/insn_trans/trans_zisslpcfi.c.inc b/target/riscv/insn_trans/trans_zisslpcfi.c.inc
new file mode 100644
index 0000000000..fe27bb73f6
--- /dev/null
+++ b/target/riscv/insn_trans/trans_zisslpcfi.c.inc
@@ -0,0 +1,310 @@
+/*
+ * RISC-V translation routines for the Control-Flow Integrity Extension
+ *
+ * Copyright (c) 2022-2023 Rivos Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+static MemOp mxl_memop(DisasContext *ctx)
+{
+    switch (get_xl(ctx)) {
+    case MXL_RV32:
+        return MO_TEUL;
+
+    case MXL_RV64:
+        return MO_TEUQ;
+
+    case MXL_RV128:
+        return MO_TEUO;
+
+    default:
+        g_assert_not_reached();
+    }
+}
+
+static bool trans_sspop(DisasContext *ctx, arg_sspop *a)
+{
+    /* cfi only supported on 32bit and 64bit */
+    if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) {
+        return false;
+    }
+
+    /* back cfi not enabled, should go to trans_zimops. return false */
+    if (!ctx->bcfi_enabled) {
+        return false;
+    }
+
+    /* sspop can only load into x1 or x5. Everything else defaults to zimops */
+    if (a->rd != 1 && a->rd != 5) {
+        return false;
+    }
+
+    /*
+     * get data in TCGv using get_gpr
+     * get addr in TCGv using gen_helper_csrr on CSR_SSP
+     * use some tcg subtract arithmetic (subtract by XLEN) on addr
+     * perform ss store on computed address
+     */
+
+    TCGv addr = tcg_temp_new();
+    int tmp = (get_xl(ctx) == MXL_RV64) ? 8 : 4;
+    TCGv_i32 ssp_csr = tcg_constant_i32(CSR_SSP);
+    TCGv data = get_gpr(ctx, a->rd, EXT_NONE);
+    gen_helper_csrr(addr, cpu_env, ssp_csr);
+    tcg_gen_qemu_ld_tl(data, addr, MMU_IDX_SS_ACCESS,
+                       mxl_memop(ctx) | MO_ALIGN);
+
+    /*
+     * add XLEN/bitwidth to addr, align to XLEN . How do i do that? Is below
+     * the right way
+     */
+    tcg_gen_addi_tl(addr, addr, tmp);
+    gen_set_gpr(ctx, a->rd, data);
+    gen_helper_csrw(cpu_env, ssp_csr, addr);
+
+    return true;
+}
+
+static bool trans_sspush(DisasContext *ctx, arg_sspush *a)
+{
+    /* cfi only supported on 32bit and 64bit */
+    if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) {
+        return false;
+    }
+
+    /* back cfi not enabled, should go to trans_zimops. return false */
+    if (!ctx->bcfi_enabled) {
+        return false;
+    }
+
+    /*
+     * sspush can only push from x1 or x5. Everything else defaults to zimops
+     */
+    if (a->rs1 != 1 && a->rs1 != 5) {
+        return false;
+    }
+
+    /*
+     * get data in TCGv using get_gpr
+     * get addr in TCGv using gen_helper_csrr on CSR_SSP
+     * use some tcg subtract arithmetic (subtract by XLEN) on addr
+     * perform ss store on computed address
+     */
+
+    TCGv addr = tcg_temp_new();
+    int tmp = (get_xl(ctx) == MXL_RV64) ? -8 : -4;
+    TCGv_i32 ssp_csr = tcg_constant_i32(CSR_SSP);
+    TCGv data = get_gpr(ctx, a->rs1, EXT_NONE);
+    gen_helper_csrr(addr, cpu_env, ssp_csr);
+
+    /*
+     * subtract XLEN from addr, align to XLEN . How do i do that? Is below the
+     * right way
+     */
+    tcg_gen_addi_tl(addr, addr, tmp);
+    tcg_gen_qemu_st_tl(data, addr, MMU_IDX_SS_ACCESS,
+                       mxl_memop(ctx) | MO_ALIGN);
+
+    gen_helper_csrw(cpu_env, ssp_csr, addr);
+
+    return true;
+}
+
+static bool trans_sschckra(DisasContext *ctx, arg_sschckra *a)
+{
+    /* cfi only supported on 32bit and 64bit */
+    if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) {
+        return false;
+    }
+
+    /* back cfi not enabled, should go to trans_zimops. return false */
+    if (!ctx->bcfi_enabled) {
+        return false;
+    }
+
+    gen_helper_sschkra_mismatch(cpu_env);
+
+    return true;
+}
+
+static bool trans_ssprr(DisasContext *ctx, arg_ssprr *a)
+{
+    /* cfi only supported on 32bit and 64bit */
+    if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) {
+        return false;
+    }
+
+    /* back cfi not enabled, should go to trans_zimops. return false */
+    if (!ctx->bcfi_enabled) {
+        return false;
+    }
+
+    TCGv dest = get_gpr(ctx, a->rd, EXT_NONE);
+    TCGv_i32 ssp_csr = tcg_constant_i32(CSR_SSP);
+    gen_helper_csrr(dest, cpu_env, ssp_csr);
+    gen_set_gpr(ctx, a->rd, dest);
+
+    return true;
+}
+
+static bool trans_ssamoswap(DisasContext *ctx, arg_ssamoswap *a)
+{
+    /* cfi only supported on 32bit and 64bit */
+    if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) {
+        return false;
+    }
+
+    /* back cfi not enabled, should go to trans_zimops. return false */
+    if (!ctx->bcfi_enabled) {
+        return false;
+    }
+
+    /* If cfi is enabled then, then rd must be != 0 */
+
+    if (a->rd == 0) {
+        return false;
+    }
+
+    TCGv dest = dest_gpr(ctx, a->rd);
+    TCGv src1 = get_address(ctx, a->rs1, 0);
+    TCGv src2 = get_gpr(ctx, a->rs2, EXT_NONE);
+    MemOp mop = (MO_ALIGN | ((get_xl(ctx) == MXL_RV32) ? MO_TESL : MO_TESQ));
+
+    tcg_gen_atomic_xchg_tl(dest, src1, src2, MMU_IDX_SS_ACCESS, mop);
+    gen_set_gpr(ctx, a->rd, dest);
+    return true;
+}
+
+static bool trans_lpcll(DisasContext *ctx, arg_lpcll *a)
+{
+    /* cfi only supported on 32bit and 64bit */
+    if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) {
+        return false;
+    }
+
+    /*
+     * If this is the first instruction of the TB, let the translator
+     * know the landing pad requirement was satisfied. No need to bother
+     * checking for CFI feature or enablement.
+     */
+
+    if (ctx->base.pc_next == ctx->base.pc_first) {
+        ctx->fcfi_lp_expected = false;
+        /* PC must be 4 byte aligned */
+        if (ctx->fcfi_enabled && ((ctx->base.pc_next) & 0x3)) {
+            /*
+             * misaligned, according to spec we should raise illegal instead
+             * of mis-aligned
+             */
+            gen_exception_illegal(ctx);
+        }
+    }
+
+    /* forward cfi not enabled, should go to trans_zimops. return false */
+    if (!ctx->fcfi_enabled) {
+        return false;
+    }
+
+    gen_helper_cfi_check_landing_pad(cpu_env, tcg_constant_i32(a->imm_cfi9),
+                               tcg_constant_i32(FCFI_LPLL));
+    return true;
+}
+
+static bool trans_lpcml(DisasContext *ctx, arg_lpcml *a)
+{
+    /* cfi only supported on 32bit and 64bit */
+    if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) {
+        return false;
+    }
+
+    /* forward cfi not enabled, should go to trans_zimops. return false */
+    if (!ctx->fcfi_enabled) {
+        return false;
+    }
+
+    gen_helper_cfi_check_landing_pad(cpu_env, tcg_constant_i32(a->imm_cfi8),
+                               tcg_constant_i32(FCFI_ML));
+    return true;
+}
+
+static bool trans_lpcul(DisasContext *ctx, arg_lpcul *a)
+{
+    /* cfi only supported on 32bit and 64bit */
+    if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) {
+        return false;
+    }
+
+    /* forward cfi not enabled, should go to trans_zimops. return false */
+    if (!ctx->fcfi_enabled) {
+        return false;
+    }
+
+    gen_helper_cfi_check_landing_pad(cpu_env, tcg_constant_i32(a->imm_cfi8),
+                               tcg_constant_i32(FCFI_UL));
+    return true;
+}
+
+static bool trans_lpsll(DisasContext *ctx, arg_lpsll *a)
+{
+    /* cfi only supported on 32bit and 64bit */
+    if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) {
+        return false;
+    }
+
+    /* forward cfi not enabled, should go to trans_zimops. return false */
+    if (!ctx->fcfi_enabled) {
+        return false;
+    }
+
+    gen_helper_cfi_set_landing_pad(cpu_env, tcg_constant_i32(a->imm_cfi9),
+                                   tcg_constant_i32(FCFI_LPLL));
+
+    return true;
+}
+
+static bool trans_lpsml(DisasContext *ctx, arg_lpsml *a)
+{
+    /* cfi only supported on 32bit and 64bit */
+    if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) {
+        return false;
+    }
+
+    /* forward cfi not enabled, should go to trans_zimops. return false */
+    if (!ctx->fcfi_enabled) {
+        return false;
+    }
+
+    gen_helper_cfi_set_landing_pad(cpu_env, tcg_constant_i32(a->imm_cfi8),
+                                   tcg_constant_i32(FCFI_ML));
+
+    return true;
+}
+
+static bool trans_lpsul(DisasContext *ctx, arg_lpsul *a)
+{
+    /* cfi only supported on 32bit and 64bit */
+    if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) {
+        return false;
+    }
+
+    /* forward cfi not enabled, should go to trans_zimops. return false */
+    if (!ctx->fcfi_enabled) {
+        return false;
+    }
+
+    gen_helper_cfi_set_landing_pad(cpu_env, tcg_constant_i32(a->imm_cfi8),
+                                   tcg_constant_i32(FCFI_UL));
+
+    return true;
+}
diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
index d15893aa82..c14b76aabb 100644
--- a/target/riscv/op_helper.c
+++ b/target/riscv/op_helper.c
@@ -123,6 +123,73 @@ target_ulong helper_csrrw_i128(CPURISCVState *env, int csr,
     return int128_getlo(rv);
 }
 
+void helper_sschkra_mismatch(CPURISCVState *env)
+{
+    if (env->gpr[xRA] != env->gpr[xT0]) {
+        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+    }
+}
+
+void helper_cfi_jalr(CPURISCVState *env, int elp)
+{
+    /*
+     * The translation routine doesn't know if forward CFI is enabled
+     * in the current processor mode or not. It's not worth burning a
+     * cflags bit to encode this, or tracking the current-mode-fcfi
+     * enable in a dedicated member of 'env'. Just come out to a helper
+     * for jump/call on a core with CFI.
+     */
+    if (cpu_get_fcfien(env)) {
+        env->elp = elp;
+    }
+}
+
+void helper_cfi_check_landing_pad(CPURISCVState *env, int lbl, int inst_type)
+{
+    if (cpu_get_fcfien(env)) {
+        switch (inst_type) {
+        case FCFI_LPLL:
+            /*
+             * Check for a lower label match. We already checked 4 byte
+             * alignment in tcg
+             */
+            if (lbl != get_field(env->lplr, LPLR_LL)) {
+                riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+            }
+            env->elp = NO_LP_EXPECTED;
+            break;
+        case FCFI_ML:
+            if (lbl != get_field(env->lplr,  LPLR_ML)) {
+                riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+            }
+            break;
+        case FCFI_UL:
+            if (lbl != get_field(env->lplr,  LPLR_UL)) {
+                riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+            }
+        }
+    }
+}
+
+void helper_cfi_set_landing_pad(CPURISCVState *env, int lbl, int inst_type)
+{
+    if (cpu_get_fcfien(env)) {
+        switch (inst_type) {
+        case FCFI_LPLL:
+            /* setting lower label always clears up entire field */
+            env->lplr = 0;
+            env->lplr = set_field(env->lplr, LPLR_LL, lbl);
+            break;
+        case FCFI_ML:
+            env->lplr = set_field(env->lplr, LPLR_ML, lbl);
+            break;
+        case FCFI_UL:
+            env->lplr = set_field(env->lplr, LPLR_UL, lbl);
+            break;
+        }
+    }
+}
+
 #ifndef CONFIG_USER_ONLY
 
 target_ulong helper_sret(CPURISCVState *env)
diff --git a/target/riscv/translate.c b/target/riscv/translate.c
index 7d43d20fc3..b1a965d192 100644
--- a/target/riscv/translate.c
+++ b/target/riscv/translate.c
@@ -1071,6 +1071,8 @@ static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc)
 #include "insn_trans/trans_privileged.c.inc"
 #include "insn_trans/trans_svinval.c.inc"
 #include "insn_trans/trans_xventanacondops.c.inc"
+#include "insn_trans/trans_zisslpcfi.c.inc"
+#include "insn_trans/trans_zimops.c.inc"
 
 /* Include the auto-generated decoder for 16 bit insn */
 #include "decode-insn16.c.inc"
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v1 RFC Zisslpcfi 9/9] target/riscv: diassembly support for zisslpcfi instructions
       [not found] <20230209062404.3582018-1-debug@rivosinc.com>
                   ` (7 preceding siblings ...)
  2023-02-09  6:24 ` [PATCH v1 RFC Zisslpcfi 8/9] target/riscv: Instructions encodings, implementation and handlers Deepak Gupta
@ 2023-02-09  6:24 ` Deepak Gupta
  8 siblings, 0 replies; 42+ messages in thread
From: Deepak Gupta @ 2023-02-09  6:24 UTC (permalink / raw)
  To: Palmer Dabbelt, Alistair Francis
  Cc: Deepak Gupta, Kip Walker, qemu-riscv, qemu-devel

This patch adds support to disassemble Zisslpcfi instructions.

Signed-off-by: Deepak Gupta <debug@rivosinc.com>
Signed-off-by: Kip Walker  <kip@rivosinc.com>
---
 disas/riscv.c | 127 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 126 insertions(+), 1 deletion(-)

diff --git a/disas/riscv.c b/disas/riscv.c
index d216b9c39b..d16ee617b0 100644
--- a/disas/riscv.c
+++ b/disas/riscv.c
@@ -163,6 +163,7 @@ typedef enum {
     rv_codec_v_i,
     rv_codec_vsetvli,
     rv_codec_vsetivli,
+    rv_codec_lp,
 } rv_codec;
 
 typedef enum {
@@ -935,6 +936,19 @@ typedef enum {
     rv_op_vsetvli = 766,
     rv_op_vsetivli = 767,
     rv_op_vsetvl = 768,
+    rv_op_lpsll = 769,
+    rv_op_lpcll = 770,
+    rv_op_lpsml = 771,
+    rv_op_lpcml = 772,
+    rv_op_lpsul = 773,
+    rv_op_lpcul = 774,
+    rv_op_sspush = 775,
+    rv_op_sspop = 776,
+    rv_op_ssprr = 777,
+    rv_op_ssamoswap = 778,
+    rv_op_sschkra = 779,
+    rv_op_zimops_r = 780,
+    rv_op_zimops_rr = 781,
 } rv_op;
 
 /* structures */
@@ -1011,6 +1025,7 @@ static const char rv_vreg_name_sym[32][4] = {
 #define rv_fmt_pred_succ              "O\tp,s"
 #define rv_fmt_rs1_rs2                "O\t1,2"
 #define rv_fmt_rd_imm                 "O\t0,i"
+#define rv_fmt_imm                    "O\ti"
 #define rv_fmt_rd_offset              "O\t0,o"
 #define rv_fmt_rd_rs1_rs2             "O\t0,1,2"
 #define rv_fmt_frd_rs1                "O\t3,1"
@@ -2065,7 +2080,20 @@ const rv_opcode_data opcode_data[] = {
     { "vsext.vf8", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vsext_vf8, rv_op_vsext_vf8, 0 },
     { "vsetvli", rv_codec_vsetvli, rv_fmt_vsetvli, NULL, rv_op_vsetvli, rv_op_vsetvli, 0 },
     { "vsetivli", rv_codec_vsetivli, rv_fmt_vsetivli, NULL, rv_op_vsetivli, rv_op_vsetivli, 0 },
-    { "vsetvl", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, rv_op_vsetvl, rv_op_vsetvl, 0 }
+    { "vsetvl", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, rv_op_vsetvl, rv_op_vsetvl, 0 },
+    { "lpsll", rv_codec_lp, rv_fmt_imm, NULL, 0, 0, 0 },
+    { "lpcll", rv_codec_lp, rv_fmt_imm, NULL, 0, 0, 0 },
+    { "lpsml", rv_codec_lp, rv_fmt_imm, NULL, 0, 0, 0 },
+    { "lpcml", rv_codec_lp, rv_fmt_imm, NULL, 0, 0, 0 },
+    { "lpsul", rv_codec_lp, rv_fmt_imm, NULL, 0, 0, 0 },
+    { "lpcul", rv_codec_lp, rv_fmt_imm, NULL, 0, 0, 0 },
+    { "sspush", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 },
+    { "sspop", rv_codec_r, rv_fmt_rd, NULL, 0, 0, 0 },
+    { "ssprr", rv_codec_r, rv_fmt_rd, NULL, 0, 0, 0 },
+    { "ssamoswap", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "sschkra", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
+    { "zimops_r", rv_codec_r, rv_fmt_rd, NULL, 0, 0, 0 },
+    { "zimops_rr", rv_codec_r, rv_fmt_rd, NULL, 0, 0, 0 }
 };
 
 /* CSR names */
@@ -2084,6 +2112,8 @@ static const char *csr_name(int csrno)
     case 0x000a: return "vxrm";
     case 0x000f: return "vcsr";
     case 0x0015: return "seed";
+    case 0x0006: return "lplr";
+    case 0x0020: return "ssp";
     case 0x0040: return "uscratch";
     case 0x0041: return "uepc";
     case 0x0042: return "ucause";
@@ -3554,6 +3584,87 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
             case 1: op = rv_op_csrrw; break;
             case 2: op = rv_op_csrrs; break;
             case 3: op = rv_op_csrrc; break;
+            case 4:
+                /* if matches mop_r mask */
+                if ((((inst >> 15) & 0b10110011110000000) ==
+                        0b10000001110000000)) {
+                    if ((inst >> 25) == 0b1000000) {
+                        switch ((inst >> 20) & 0b11) {
+                        case 0: /* sspush and sspop */
+                            if (((inst >> 15) & 0b11111) &&
+                                !((inst >> 7) & 0b11111))
+                                op = rv_op_sspush;
+                            if (!((inst >> 15) & 0b11111) &&
+                                ((inst >> 7) & 0b11111))
+                                op = rv_op_sspop;
+                            break;
+
+                        case 1: /* ssprr */
+                            if (!((inst >> 15) & 0b11111) &&
+                                ((inst >> 7) & 0b11111))
+                                op = rv_op_ssprr;
+                            break;
+
+                        default:
+                            op = rv_op_zimops_r;
+                            break;
+                        }
+                    } else {
+                        op = rv_op_zimops_r;
+                    }
+                } else if (((inst >> 15) & 0b10110010000000000) ==
+                            0b10000010000000000) { /* if matches mop_rr mask */
+                    switch (inst >> 28) {
+                    case 0b1000:
+                        switch ((inst >> 7) & 0b11111) {
+                        case 0b00000:
+                            /* collect 5 bits */
+                            switch (((inst >> 23) & 0b11111)) {
+                            case 23:
+                                op = rv_op_lpcul;
+                                break;
+                            case 22:
+                                op = rv_op_lpsul;
+                                break;
+                            case 13:
+                                op = rv_op_lpcml;
+                                break;
+                            case 12:
+                                op = rv_op_lpsml;
+                                break;
+                            case 6:
+                            case 7:
+                                op = rv_op_lpcll;
+                                break;
+                            case 4:
+                            case 5:
+                                op = rv_op_lpsll;
+                                break;
+                            default:
+                                if (inst ==
+                                    0b10001010000100101100000001110011) {
+                                    op = rv_op_sschkra;
+                                } else {
+                                    op = rv_op_zimops_rr;
+                                }
+                                break;
+                            }
+                            break;
+                        default:
+                            if ((inst >> 26) == 0b100000) {
+                                op = rv_op_ssamoswap;
+                            } else {
+                                op = rv_op_zimops_rr;
+                            }
+                            break;
+                        }
+                        break;
+                    default:
+                        op = rv_op_zimops_rr;
+                        break;
+                    }
+                }
+                break;
             case 5: op = rv_op_csrrwi; break;
             case 6: op = rv_op_csrrsi; break;
             case 7: op = rv_op_csrrci; break;
@@ -3883,6 +3994,17 @@ static uint32_t operand_vm(rv_inst inst)
     return (inst << 38) >> 63;
 }
 
+static uint32_t operand_lpl(rv_inst inst)
+{
+    uint32_t label_width = 9;
+
+    if ((inst >> 26) & 0b11) {
+        label_width = 8;
+    }
+
+    return (inst >> 15) & ((1 << label_width) - 1);
+}
+
 /* decode operands */
 
 static void decode_inst_operands(rv_decode *dec, rv_isa isa)
@@ -4199,6 +4321,9 @@ static void decode_inst_operands(rv_decode *dec, rv_isa isa)
         dec->imm = operand_vimm(inst);
         dec->vzimm = operand_vzimm10(inst);
         break;
+    case rv_codec_lp:
+        dec->imm = operand_lpl(inst);
+        break;
     };
 }
 
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v1 RFC Zisslpcfi 4/9] target/riscv: helper functions for forward and backward cfi
  2023-02-09  6:29 [PATCH v1 RFC Zisslpcfi 0/9] zimops and zisslpcfi extension to riscv Deepak Gupta
@ 2023-02-09  6:29 ` Deepak Gupta
  0 siblings, 0 replies; 42+ messages in thread
From: Deepak Gupta @ 2023-02-09  6:29 UTC (permalink / raw)
  To: qemu-devel, Palmer Dabbelt, Alistair Francis, Bin Meng
  Cc: Deepak Gupta, Kip Walker, qemu-riscv

Implementation for forward cfi and backward cfi needs helper function
to determine if currently fcfi and bcfi are enabled. Enable depends on
privilege mode and settings in sstatus/menvcfg/henvcfg/mseccfg CSRs.

Signed-off-by: Deepak Gupta <debug@rivosinc.com>
Signed-off-by: Kip Walker  <kip@rivosinc.com>
---
 target/riscv/cpu.h        |  2 ++
 target/riscv/cpu_helper.c | 51 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 53 insertions(+)

diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 18db61a06a..d14ea4f91d 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -568,6 +568,8 @@ bool riscv_cpu_virt_enabled(CPURISCVState *env);
 void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable);
 bool riscv_cpu_two_stage_lookup(int mmu_idx);
 int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch);
+bool cpu_get_fcfien(CPURISCVState *env);
+bool cpu_get_bcfien(CPURISCVState *env);
 hwaddr riscv_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
 G_NORETURN void  riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
                                                MMUAccessType access_type, int mmu_idx,
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index 9a28816521..a397023840 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -30,6 +30,7 @@
 #include "sysemu/cpu-timers.h"
 #include "cpu_bits.h"
 #include "debug.h"
+#include "pmp.h"
 
 int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch)
 {
@@ -40,6 +41,56 @@ int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch)
 #endif
 }
 
+bool cpu_get_fcfien(CPURISCVState *env)
+{
+#ifdef CONFIG_USER_ONLY
+    return false;
+#else
+    /* no cfi extension, return false */
+    if (!env_archcpu(env)->cfg.ext_cfi) {
+        return false;
+    }
+
+    switch (env->priv) {
+    case PRV_U:
+        return (env->mstatus & MSTATUS_UFCFIEN) ? true : false;
+    case PRV_S:
+        return (env->menvcfg & MENVCFG_SFCFIEN) ? true : false;
+    case PRV_M:
+        return (env->mseccfg & MSECCFG_MFCFIEN) ? true : false;
+    default:
+        g_assert_not_reached();
+    }
+#endif
+}
+
+bool cpu_get_bcfien(CPURISCVState *env)
+{
+#ifdef CONFIG_USER_ONLY
+    return false;
+#else
+    /* no cfi extension, return false */
+    if (!env_archcpu(env)->cfg.ext_cfi) {
+        return false;
+    }
+
+    switch (env->priv) {
+    case PRV_U:
+        return (env->mstatus & MSTATUS_UBCFIEN) ? true : false;
+
+        /*
+         * no gating for back cfi in M/S mode. back cfi is always on for
+         * M/S mode
+         */
+    case PRV_S:
+    case PRV_M:
+        return true;
+    default:
+        g_assert_not_reached();
+    }
+#endif
+}
+
 void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc,
                           target_ulong *cs_base, uint32_t *pflags)
 {
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* Re: [PATCH v1 RFC Zisslpcfi 1/9] target/riscv: adding zimops and zisslpcfi extension to RISCV cpu config
  2023-02-09  6:23 ` [PATCH v1 RFC Zisslpcfi 1/9] target/riscv: adding zimops and zisslpcfi extension to RISCV cpu config Deepak Gupta
@ 2023-02-11  3:19   ` weiwei
  2023-02-13  3:15     ` Deepak Gupta
  2023-02-15  2:52   ` LIU Zhiwei
  1 sibling, 1 reply; 42+ messages in thread
From: weiwei @ 2023-02-11  3:19 UTC (permalink / raw)
  To: Deepak Gupta, Palmer Dabbelt, Alistair Francis, Bin Meng
  Cc: Kip Walker, qemu-riscv, qemu-devel, liweiwei


On 2023/2/9 14:23, Deepak Gupta wrote:
> Introducing riscv `zisslpcfi` extension to riscv target. `zisslpcfi`
> extension provides hardware assistance to riscv hart to enable control
> flow integrity (CFI) for software.
>
> `zisslpcfi` extension expects hart to implement `zimops`. `zimops` stands
> for "unprivileged integer maybe operations". `zimops` carve out certain
> reserved opcodes encodings from integer spec to "may be operations"
> encodings. `zimops` opcode encodings simply move 0 to rd.
> `zisslpcfi` claims some of the `zimops` encodings and use them for shadow
> stack management or indirect branch tracking. Any future extension can
> also claim `zimops` encodings.
>
> This patch also adds a dependency check for `zimops` to be enabled if
> `zisslpcfi` is enabled on the hart.
>
> Signed-off-by: Deepak Gupta <debug@rivosinc.com>
> Signed-off-by: Kip Walker  <kip@rivosinc.com>
> ---
>   target/riscv/cpu.c | 13 +++++++++++++
>   target/riscv/cpu.h |  2 ++
>   2 files changed, 15 insertions(+)
>
> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> index cc75ca7667..6b4e90eb91 100644
> --- a/target/riscv/cpu.c
> +++ b/target/riscv/cpu.c
> @@ -110,6 +110,8 @@ static const struct isa_ext_data isa_edata_arr[] = {
>       ISA_EXT_DATA_ENTRY(svnapot, true, PRIV_VERSION_1_12_0, ext_svnapot),
>       ISA_EXT_DATA_ENTRY(svpbmt, true, PRIV_VERSION_1_12_0, ext_svpbmt),
>       ISA_EXT_DATA_ENTRY(xventanacondops, true, PRIV_VERSION_1_12_0, ext_XVentanaCondOps),
> +    ISA_EXT_DATA_ENTRY(zimops, true, PRIV_VERSION_1_12_0, ext_zimops),
> +    ISA_EXT_DATA_ENTRY(zisslpcfi, true, PRIV_VERSION_1_12_0, ext_cfi),

By convention, it  should be ext_zisslpcfi  .

>   };
>   
>   static bool isa_ext_is_enabled(RISCVCPU *cpu,
> @@ -792,6 +794,11 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
>               return;
>           }
>   
> +        if (cpu->cfg.ext_cfi && !cpu->cfg.ext_zimops) {
> +            error_setg(errp, "Zisslpcfi extension requires Zimops extension");
> +            return;
> +        }
> +

If  Zisslpcfi implicitly means Zimops is implemented as commented in 
following code, I think we should just enable zimops  when zisslpcfi is 
enabled.

>           /* Set the ISA extensions, checks should have happened above */
>           if (cpu->cfg.ext_zdinx || cpu->cfg.ext_zhinx ||
>               cpu->cfg.ext_zhinxmin) {
> @@ -1102,6 +1109,12 @@ static Property riscv_cpu_properties[] = {
>   #ifndef CONFIG_USER_ONLY
>       DEFINE_PROP_UINT64("resetvec", RISCVCPU, env.resetvec, DEFAULT_RSTVEC),
>   #endif
> +    /*
> +     * Zisslpcfi CFI extension, Zisslpcfi implicitly means Zimops is
> +     * implemented
> +     */
> +    DEFINE_PROP_BOOL("zisslpcfi", RISCVCPU, cfg.ext_cfi, true),
> +    DEFINE_PROP_BOOL("zimops", RISCVCPU, cfg.ext_zimops, true),

These properties can not expose to users before all its functions are 
ready. And it need add 'x-' prefix as experimental extensions currently.

Regards,

Weiwei Li

>   
>       DEFINE_PROP_BOOL("short-isa-string", RISCVCPU, cfg.short_isa_string, false),
>   
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index f5609b62a2..9a923760b2 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -471,6 +471,8 @@ struct RISCVCPUConfig {
>       uint32_t mvendorid;
>       uint64_t marchid;
>       uint64_t mimpid;
> +    bool ext_zimops;
> +    bool ext_cfi;
>   
>       /* Vendor-specific custom extensions */
>       bool ext_XVentanaCondOps;



^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v1 RFC Zisslpcfi 2/9] target/riscv: zisslpcfi CSR, bit positions and other definitions
  2023-02-09  6:23 ` [PATCH v1 RFC Zisslpcfi 2/9] target/riscv: zisslpcfi CSR, bit positions and other definitions Deepak Gupta
@ 2023-02-11  3:32   ` weiwei
  2023-02-13  3:21     ` Deepak Gupta
  2023-02-15  3:31   ` LIU Zhiwei
  1 sibling, 1 reply; 42+ messages in thread
From: weiwei @ 2023-02-11  3:32 UTC (permalink / raw)
  To: Deepak Gupta, Palmer Dabbelt, Alistair Francis, Bin Meng
  Cc: Kip Walker, qemu-riscv, qemu-devel, liweiwei


On 2023/2/9 14:23, Deepak Gupta wrote:
> `zisslpcfi` extension adds two new CSRs. CSR_SSP and CSR_LPLR.
> - CSR_SSP: This CSR holds shadow stack pointer for current privilege mode
>             CSR_SSP is accessible in all modes. Each mode must establish
>             it's own CSR_SSP.
>
> - CSR_LPLR: This CSR holds label value set at the callsite by compiler.
>              On call target label check instructions are emitted by
>              compiler which check label value against value present in
>              CSR_LPRL.
>
> Enabling of `zisslpcfi` is controlled via menvcfg (for S/HS/VS/U/VU) and
> henvcfg (for VS/VU) at bit position 60.
>
> Each mode has enable/disable bits for forward cfi. Backward cfi doesn't
> have separate enable/disable bits for S and M mode. User forward cfi and
> user backward cfi enable/disable bits are in mstatus/sstatus CSR.
> Supervisor forward cfi enable/disable bit are in menvcfg and henvcfg CSR.
> Machine mode forward cfi enable/disable bit is in mseccfg CSR.
>
> If forward cfi enabled, all indirect branches must land on a landing pad
> instruction (`lpcll`, introduced in later commits). CPU/hart tracks this
> internally using a landing pad tracker called `elp` short for `expecting
> landing pad`. An interrupt can occur between an indirect branch and
> target. If such an event occurs `elp` is saved away in mstatus/sstatus
> CSR
>
> Signed-off-by: Deepak Gupta <debug@rivosinc.com>
> Signed-off-by: Kip Walker  <kip@rivosinc.com>
> ---
>   target/riscv/cpu.h      |  5 +++++
>   target/riscv/cpu_bits.h | 25 +++++++++++++++++++++++++
>   target/riscv/pmp.h      |  3 ++-
>   3 files changed, 32 insertions(+), 1 deletion(-)
>
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index 9a923760b2..18db61a06a 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -181,6 +181,11 @@ struct CPUArchState {
>   
>       uint32_t features;
>   
> +    /* CFI Extension user mode registers and state */
> +    uint32_t     lplr;
> +    target_ulong ssp;
> +    cfi_elp      elp;
> +

cfi_elp is not defined in current or previous patch.

>   #ifdef CONFIG_USER_ONLY
>       uint32_t elf_flags;
>   #endif
> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
> index 8b0d7e20ea..1663ba5775 100644
> --- a/target/riscv/cpu_bits.h
> +++ b/target/riscv/cpu_bits.h
> @@ -39,6 +39,10 @@
>   
>   /* Control and Status Registers */
>   
> +/* CFI CSRs */
> +#define CSR_LPLR            0x006
> +#define CSR_SSP             0x020
> +
>   /* User Trap Setup */
>   #define CSR_USTATUS         0x000
>   #define CSR_UIE             0x004
> @@ -542,6 +546,10 @@
>   #define MSTATUS_TVM         0x00100000 /* since: priv-1.10 */
>   #define MSTATUS_TW          0x00200000 /* since: priv-1.10 */
>   #define MSTATUS_TSR         0x00400000 /* since: priv-1.10 */
> +#define MSTATUS_UFCFIEN     0x00800000 /* Zisslpcfi-0.1 */
> +#define MSTATUS_UBCFIEN     0x01000000 /* Zisslpcfi-0.1 */
> +#define MSTATUS_SPELP       0x02000000 /* Zisslpcfi-0.1 */
> +#define MSTATUS_MPELP       0x04000000 /* Zisslpcfi-0.1 */
>   #define MSTATUS_GVA         0x4000000000ULL
>   #define MSTATUS_MPV         0x8000000000ULL
>   
> @@ -572,12 +580,21 @@ typedef enum {
>   #define SSTATUS_XS          0x00018000
>   #define SSTATUS_SUM         0x00040000 /* since: priv-1.10 */
>   #define SSTATUS_MXR         0x00080000
> +#define SSTATUS_UFCFIEN     MSTATUS_UFCFIEN /* Zisslpcfi-0.1 */
> +#define SSTATUS_UBCFIEN     MSTATUS_UBCFIEN /* Zisslpcfi-0.1 */
> +#define SSTATUS_SPELP       MSTATUS_SPELP   /* Zisslpcfi-0.1 */
>   
>   #define SSTATUS64_UXL       0x0000000300000000ULL
>   
>   #define SSTATUS32_SD        0x80000000
>   #define SSTATUS64_SD        0x8000000000000000ULL
>   
> +#define CFISTATUS_M_MASK    (MSTATUS_UFCFIEN | MSTATUS_UBCFIEN | \
> +                             MSTATUS_MPELP | MSTATUS_SPELP)
> +
> +#define CFISTATUS_S_MASK    (SSTATUS_UFCFIEN | SSTATUS_UBCFIEN | \
> +                             SSTATUS_SPELP)
> +
>   /* hstatus CSR bits */
>   #define HSTATUS_VSBE         0x00000020
>   #define HSTATUS_GVA          0x00000040
> @@ -747,10 +764,14 @@ typedef enum RISCVException {
>   #define MENVCFG_CBIE                       (3UL << 4)
>   #define MENVCFG_CBCFE                      BIT(6)
>   #define MENVCFG_CBZE                       BIT(7)
> +#define MENVCFG_SFCFIEN                    BIT(59)
> +#define MENVCFG_CFI                        BIT(60)
We should use 1ULL << 59/60 here.
>   #define MENVCFG_PBMTE                      (1ULL << 62)
>   #define MENVCFG_STCE                       (1ULL << 63)
>   
>   /* For RV32 */
> +#define MENVCFGH_SFCFIEN                   BIT(27)
> +#define MENVCFGH_CFI                       BIT(28)
>   #define MENVCFGH_PBMTE                     BIT(30)
>   #define MENVCFGH_STCE                      BIT(31)
>   
> @@ -763,10 +784,14 @@ typedef enum RISCVException {
>   #define HENVCFG_CBIE                       MENVCFG_CBIE
>   #define HENVCFG_CBCFE                      MENVCFG_CBCFE
>   #define HENVCFG_CBZE                       MENVCFG_CBZE
> +#define HENVCFG_SFCFIEN                    MENVCFG_SFCFIEN
> +#define HENVCFG_CFI                        MENVCFG_CFI
>   #define HENVCFG_PBMTE                      MENVCFG_PBMTE
>   #define HENVCFG_STCE                       MENVCFG_STCE
>   
>   /* For RV32 */
> +#define HENVCFGH_SFCFIEN                    MENVCFGH_SFCFIEN
> +#define HENVCFGH_CFI                        MENVCFGH_CFI
>   #define HENVCFGH_PBMTE                      MENVCFGH_PBMTE
>   #define HENVCFGH_STCE                       MENVCFGH_STCE
>   
> diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h
> index da32c61c85..f5bfc4955b 100644
> --- a/target/riscv/pmp.h
> +++ b/target/riscv/pmp.h
> @@ -43,7 +43,8 @@ typedef enum {
>       MSECCFG_MMWP  = 1 << 1,
>       MSECCFG_RLB   = 1 << 2,
>       MSECCFG_USEED = 1 << 8,
> -    MSECCFG_SSEED = 1 << 9
> +    MSECCFG_SSEED = 1 << 9,
> +    MSECCFG_MFCFIEN =  1 << 10

It's unnecessary to use multiple space after '='.

Regards,

Weiwei Li

>   } mseccfg_field_t;
>   
>   typedef struct {



^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v1 RFC Zisslpcfi 1/9] target/riscv: adding zimops and zisslpcfi extension to RISCV cpu config
  2023-02-11  3:19   ` weiwei
@ 2023-02-13  3:15     ` Deepak Gupta
  0 siblings, 0 replies; 42+ messages in thread
From: Deepak Gupta @ 2023-02-13  3:15 UTC (permalink / raw)
  To: weiwei
  Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, Kip Walker,
	qemu-riscv, qemu-devel

On Sat, Feb 11, 2023 at 11:19:11AM +0800, weiwei wrote:
>
>On 2023/2/9 14:23, Deepak Gupta wrote:
>>Introducing riscv `zisslpcfi` extension to riscv target. `zisslpcfi`
>>extension provides hardware assistance to riscv hart to enable control
>>flow integrity (CFI) for software.
>>
>>`zisslpcfi` extension expects hart to implement `zimops`. `zimops` stands
>>for "unprivileged integer maybe operations". `zimops` carve out certain
>>reserved opcodes encodings from integer spec to "may be operations"
>>encodings. `zimops` opcode encodings simply move 0 to rd.
>>`zisslpcfi` claims some of the `zimops` encodings and use them for shadow
>>stack management or indirect branch tracking. Any future extension can
>>also claim `zimops` encodings.
>>
>>This patch also adds a dependency check for `zimops` to be enabled if
>>`zisslpcfi` is enabled on the hart.
>>
>>Signed-off-by: Deepak Gupta <debug@rivosinc.com>
>>Signed-off-by: Kip Walker  <kip@rivosinc.com>
>>---
>>  target/riscv/cpu.c | 13 +++++++++++++
>>  target/riscv/cpu.h |  2 ++
>>  2 files changed, 15 insertions(+)
>>
>>diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
>>index cc75ca7667..6b4e90eb91 100644
>>--- a/target/riscv/cpu.c
>>+++ b/target/riscv/cpu.c
>>@@ -110,6 +110,8 @@ static const struct isa_ext_data isa_edata_arr[] = {
>>      ISA_EXT_DATA_ENTRY(svnapot, true, PRIV_VERSION_1_12_0, ext_svnapot),
>>      ISA_EXT_DATA_ENTRY(svpbmt, true, PRIV_VERSION_1_12_0, ext_svpbmt),
>>      ISA_EXT_DATA_ENTRY(xventanacondops, true, PRIV_VERSION_1_12_0, ext_XVentanaCondOps),
>>+    ISA_EXT_DATA_ENTRY(zimops, true, PRIV_VERSION_1_12_0, ext_zimops),
>>+    ISA_EXT_DATA_ENTRY(zisslpcfi, true, PRIV_VERSION_1_12_0, ext_cfi),
>
>By convention, it  should be ext_zisslpcfi  .

Noted. Will fix it.

>
>>  };
>>  static bool isa_ext_is_enabled(RISCVCPU *cpu,
>>@@ -792,6 +794,11 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
>>              return;
>>          }
>>+        if (cpu->cfg.ext_cfi && !cpu->cfg.ext_zimops) {
>>+            error_setg(errp, "Zisslpcfi extension requires Zimops extension");
>>+            return;
>>+        }
>>+
>
>If  Zisslpcfi implicitly means Zimops is implemented as commented in 
>following code, I think we should just enable zimops  when zisslpcfi 
>is enabled.
>
Hmm... That's a good idea (at-least for qemu implementation)
Only catch is this
    - Since zimops does move 0 to rd. That's still an operation that's happening on 
      destination. If none of the extensions are implemented, it might be good to have
      just zimops enabled *just* to make sure binary is not breaking anything (by moving
      0 to destination)
>>          /* Set the ISA extensions, checks should have happened above */
>>          if (cpu->cfg.ext_zdinx || cpu->cfg.ext_zhinx ||
>>              cpu->cfg.ext_zhinxmin) {
>>@@ -1102,6 +1109,12 @@ static Property riscv_cpu_properties[] = {
>>  #ifndef CONFIG_USER_ONLY
>>      DEFINE_PROP_UINT64("resetvec", RISCVCPU, env.resetvec, DEFAULT_RSTVEC),
>>  #endif
>>+    /*
>>+     * Zisslpcfi CFI extension, Zisslpcfi implicitly means Zimops is
>>+     * implemented
>>+     */
>>+    DEFINE_PROP_BOOL("zisslpcfi", RISCVCPU, cfg.ext_cfi, true),
>>+    DEFINE_PROP_BOOL("zimops", RISCVCPU, cfg.ext_zimops, true),
>
>These properties can not expose to users before all its functions are 
>ready. And it need add 'x-' prefix as experimental extensions 
>currently.

Noted will revise it.

>
>Regards,
>
>Weiwei Li
>
>>      DEFINE_PROP_BOOL("short-isa-string", RISCVCPU, cfg.short_isa_string, false),
>>diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
>>index f5609b62a2..9a923760b2 100644
>>--- a/target/riscv/cpu.h
>>+++ b/target/riscv/cpu.h
>>@@ -471,6 +471,8 @@ struct RISCVCPUConfig {
>>      uint32_t mvendorid;
>>      uint64_t marchid;
>>      uint64_t mimpid;
>>+    bool ext_zimops;
>>+    bool ext_cfi;
>>      /* Vendor-specific custom extensions */
>>      bool ext_XVentanaCondOps;
>


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v1 RFC Zisslpcfi 2/9] target/riscv: zisslpcfi CSR, bit positions and other definitions
  2023-02-11  3:32   ` weiwei
@ 2023-02-13  3:21     ` Deepak Gupta
  0 siblings, 0 replies; 42+ messages in thread
From: Deepak Gupta @ 2023-02-13  3:21 UTC (permalink / raw)
  To: weiwei
  Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, Kip Walker,
	qemu-riscv, qemu-devel

On Sat, Feb 11, 2023 at 11:32:17AM +0800, weiwei wrote:
>
>On 2023/2/9 14:23, Deepak Gupta wrote:
>>`zisslpcfi` extension adds two new CSRs. CSR_SSP and CSR_LPLR.
>>- CSR_SSP: This CSR holds shadow stack pointer for current privilege mode
>>            CSR_SSP is accessible in all modes. Each mode must establish
>>            it's own CSR_SSP.
>>
>>- CSR_LPLR: This CSR holds label value set at the callsite by compiler.
>>             On call target label check instructions are emitted by
>>             compiler which check label value against value present in
>>             CSR_LPRL.
>>
>>Enabling of `zisslpcfi` is controlled via menvcfg (for S/HS/VS/U/VU) and
>>henvcfg (for VS/VU) at bit position 60.
>>
>>Each mode has enable/disable bits for forward cfi. Backward cfi doesn't
>>have separate enable/disable bits for S and M mode. User forward cfi and
>>user backward cfi enable/disable bits are in mstatus/sstatus CSR.
>>Supervisor forward cfi enable/disable bit are in menvcfg and henvcfg CSR.
>>Machine mode forward cfi enable/disable bit is in mseccfg CSR.
>>
>>If forward cfi enabled, all indirect branches must land on a landing pad
>>instruction (`lpcll`, introduced in later commits). CPU/hart tracks this
>>internally using a landing pad tracker called `elp` short for `expecting
>>landing pad`. An interrupt can occur between an indirect branch and
>>target. If such an event occurs `elp` is saved away in mstatus/sstatus
>>CSR
>>
>>Signed-off-by: Deepak Gupta <debug@rivosinc.com>
>>Signed-off-by: Kip Walker  <kip@rivosinc.com>
>>---
>>  target/riscv/cpu.h      |  5 +++++
>>  target/riscv/cpu_bits.h | 25 +++++++++++++++++++++++++
>>  target/riscv/pmp.h      |  3 ++-
>>  3 files changed, 32 insertions(+), 1 deletion(-)
>>
>>diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
>>index 9a923760b2..18db61a06a 100644
>>--- a/target/riscv/cpu.h
>>+++ b/target/riscv/cpu.h
>>@@ -181,6 +181,11 @@ struct CPUArchState {
>>      uint32_t features;
>>+    /* CFI Extension user mode registers and state */
>>+    uint32_t     lplr;
>>+    target_ulong ssp;
>>+    cfi_elp      elp;
>>+
>
>cfi_elp is not defined in current or previous patch.
>
thanks. sorry my mistake.
good catch. this needs to go in patch #5.

>>  #ifdef CONFIG_USER_ONLY
>>      uint32_t elf_flags;
>>  #endif
>>diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
>>index 8b0d7e20ea..1663ba5775 100644
>>--- a/target/riscv/cpu_bits.h
>>+++ b/target/riscv/cpu_bits.h
>>@@ -39,6 +39,10 @@
>>  /* Control and Status Registers */
>>+/* CFI CSRs */
>>+#define CSR_LPLR            0x006
>>+#define CSR_SSP             0x020
>>+
>>  /* User Trap Setup */
>>  #define CSR_USTATUS         0x000
>>  #define CSR_UIE             0x004
>>@@ -542,6 +546,10 @@
>>  #define MSTATUS_TVM         0x00100000 /* since: priv-1.10 */
>>  #define MSTATUS_TW          0x00200000 /* since: priv-1.10 */
>>  #define MSTATUS_TSR         0x00400000 /* since: priv-1.10 */
>>+#define MSTATUS_UFCFIEN     0x00800000 /* Zisslpcfi-0.1 */
>>+#define MSTATUS_UBCFIEN     0x01000000 /* Zisslpcfi-0.1 */
>>+#define MSTATUS_SPELP       0x02000000 /* Zisslpcfi-0.1 */
>>+#define MSTATUS_MPELP       0x04000000 /* Zisslpcfi-0.1 */
>>  #define MSTATUS_GVA         0x4000000000ULL
>>  #define MSTATUS_MPV         0x8000000000ULL
>>@@ -572,12 +580,21 @@ typedef enum {
>>  #define SSTATUS_XS          0x00018000
>>  #define SSTATUS_SUM         0x00040000 /* since: priv-1.10 */
>>  #define SSTATUS_MXR         0x00080000
>>+#define SSTATUS_UFCFIEN     MSTATUS_UFCFIEN /* Zisslpcfi-0.1 */
>>+#define SSTATUS_UBCFIEN     MSTATUS_UBCFIEN /* Zisslpcfi-0.1 */
>>+#define SSTATUS_SPELP       MSTATUS_SPELP   /* Zisslpcfi-0.1 */
>>  #define SSTATUS64_UXL       0x0000000300000000ULL
>>  #define SSTATUS32_SD        0x80000000
>>  #define SSTATUS64_SD        0x8000000000000000ULL
>>+#define CFISTATUS_M_MASK    (MSTATUS_UFCFIEN | MSTATUS_UBCFIEN | \
>>+                             MSTATUS_MPELP | MSTATUS_SPELP)
>>+
>>+#define CFISTATUS_S_MASK    (SSTATUS_UFCFIEN | SSTATUS_UBCFIEN | \
>>+                             SSTATUS_SPELP)
>>+
>>  /* hstatus CSR bits */
>>  #define HSTATUS_VSBE         0x00000020
>>  #define HSTATUS_GVA          0x00000040
>>@@ -747,10 +764,14 @@ typedef enum RISCVException {
>>  #define MENVCFG_CBIE                       (3UL << 4)
>>  #define MENVCFG_CBCFE                      BIT(6)
>>  #define MENVCFG_CBZE                       BIT(7)
>>+#define MENVCFG_SFCFIEN                    BIT(59)
>>+#define MENVCFG_CFI                        BIT(60)
>We should use 1ULL << 59/60 here.
Noted. Will fix.

>>  #define MENVCFG_PBMTE                      (1ULL << 62)
>>  #define MENVCFG_STCE                       (1ULL << 63)
>>  /* For RV32 */
>>+#define MENVCFGH_SFCFIEN                   BIT(27)
>>+#define MENVCFGH_CFI                       BIT(28)
>>  #define MENVCFGH_PBMTE                     BIT(30)
>>  #define MENVCFGH_STCE                      BIT(31)
>>@@ -763,10 +784,14 @@ typedef enum RISCVException {
>>  #define HENVCFG_CBIE                       MENVCFG_CBIE
>>  #define HENVCFG_CBCFE                      MENVCFG_CBCFE
>>  #define HENVCFG_CBZE                       MENVCFG_CBZE
>>+#define HENVCFG_SFCFIEN                    MENVCFG_SFCFIEN
>>+#define HENVCFG_CFI                        MENVCFG_CFI
>>  #define HENVCFG_PBMTE                      MENVCFG_PBMTE
>>  #define HENVCFG_STCE                       MENVCFG_STCE
>>  /* For RV32 */
>>+#define HENVCFGH_SFCFIEN                    MENVCFGH_SFCFIEN
>>+#define HENVCFGH_CFI                        MENVCFGH_CFI
>>  #define HENVCFGH_PBMTE                      MENVCFGH_PBMTE
>>  #define HENVCFGH_STCE                       MENVCFGH_STCE
>>diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h
>>index da32c61c85..f5bfc4955b 100644
>>--- a/target/riscv/pmp.h
>>+++ b/target/riscv/pmp.h
>>@@ -43,7 +43,8 @@ typedef enum {
>>      MSECCFG_MMWP  = 1 << 1,
>>      MSECCFG_RLB   = 1 << 2,
>>      MSECCFG_USEED = 1 << 8,
>>-    MSECCFG_SSEED = 1 << 9
>>+    MSECCFG_SSEED = 1 << 9,
>>+    MSECCFG_MFCFIEN =  1 << 10
>
>It's unnecessary to use multiple space after '='.
>
Somehow this escaped. Will fix it.

>Regards,
>
>Weiwei Li
>
>>  } mseccfg_field_t;
>>  typedef struct {
>


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v1 RFC Zisslpcfi 1/9] target/riscv: adding zimops and zisslpcfi extension to RISCV cpu config
  2023-02-09  6:23 ` [PATCH v1 RFC Zisslpcfi 1/9] target/riscv: adding zimops and zisslpcfi extension to RISCV cpu config Deepak Gupta
  2023-02-11  3:19   ` weiwei
@ 2023-02-15  2:52   ` LIU Zhiwei
  2023-02-15 20:47     ` Deepak Gupta
  1 sibling, 1 reply; 42+ messages in thread
From: LIU Zhiwei @ 2023-02-15  2:52 UTC (permalink / raw)
  To: Deepak Gupta, Palmer Dabbelt, Alistair Francis, Bin Meng,
	liweiwei, dbarboza
  Cc: Kip Walker, qemu-riscv, qemu-devel


On 2023/2/9 14:23, Deepak Gupta wrote:
> Introducing riscv `zisslpcfi` extension to riscv target. `zisslpcfi`
> extension provides hardware assistance to riscv hart to enable control
> flow integrity (CFI) for software.
>
> `zisslpcfi` extension expects hart to implement `zimops`. `zimops` stands
> for "unprivileged integer maybe operations". `zimops` carve out certain
> reserved opcodes encodings from integer spec to "may be operations"
> encodings. `zimops` opcode encodings simply move 0 to rd.
> `zisslpcfi` claims some of the `zimops` encodings and use them for shadow
> stack management or indirect branch tracking. Any future extension can
> also claim `zimops` encodings.

Does  the zimops has a independent specification? If so, you should give 
a link to this
specification.

>
> This patch also adds a dependency check for `zimops` to be enabled if
> `zisslpcfi` is enabled on the hart.

You should don't add two extensions in one patch. I think you should add 
them one by one.
And add the zimop first.  In my opinion, you should implement the whole 
zimop extension before
adding any patch for zisslpcfi, including the implementation of mop.rr 
and mop.r.

>
> Signed-off-by: Deepak Gupta <debug@rivosinc.com>
> Signed-off-by: Kip Walker  <kip@rivosinc.com>
> ---
>   target/riscv/cpu.c | 13 +++++++++++++
>   target/riscv/cpu.h |  2 ++
>   2 files changed, 15 insertions(+)
>
> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> index cc75ca7667..6b4e90eb91 100644
> --- a/target/riscv/cpu.c
> +++ b/target/riscv/cpu.c
> @@ -110,6 +110,8 @@ static const struct isa_ext_data isa_edata_arr[] = {
>       ISA_EXT_DATA_ENTRY(svnapot, true, PRIV_VERSION_1_12_0, ext_svnapot),
>       ISA_EXT_DATA_ENTRY(svpbmt, true, PRIV_VERSION_1_12_0, ext_svpbmt),
>       ISA_EXT_DATA_ENTRY(xventanacondops, true, PRIV_VERSION_1_12_0, ext_XVentanaCondOps),
> +    ISA_EXT_DATA_ENTRY(zimops, true, PRIV_VERSION_1_12_0, ext_zimops),
> +    ISA_EXT_DATA_ENTRY(zisslpcfi, true, PRIV_VERSION_1_12_0, ext_cfi),
Add them one by one.
>   };
>   
>   static bool isa_ext_is_enabled(RISCVCPU *cpu,
> @@ -792,6 +794,11 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
>               return;
>           }
>   
> +        if (cpu->cfg.ext_cfi && !cpu->cfg.ext_zimops) {
> +            error_setg(errp, "Zisslpcfi extension requires Zimops extension");
> +            return;
> +        }
> +
Seems reasonable for me.
>           /* Set the ISA extensions, checks should have happened above */
>           if (cpu->cfg.ext_zdinx || cpu->cfg.ext_zhinx ||
>               cpu->cfg.ext_zhinxmin) {
> @@ -1102,6 +1109,12 @@ static Property riscv_cpu_properties[] = {
>   #ifndef CONFIG_USER_ONLY
>       DEFINE_PROP_UINT64("resetvec", RISCVCPU, env.resetvec, DEFAULT_RSTVEC),
>   #endif
> +    /*
> +     * Zisslpcfi CFI extension, Zisslpcfi implicitly means Zimops is
> +     * implemented
> +     */
> +    DEFINE_PROP_BOOL("zisslpcfi", RISCVCPU, cfg.ext_cfi, true),
> +    DEFINE_PROP_BOOL("zimops", RISCVCPU, cfg.ext_zimops, true),

Default value should be false.

Zhiwei

>   
>       DEFINE_PROP_BOOL("short-isa-string", RISCVCPU, cfg.short_isa_string, false),
>   
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index f5609b62a2..9a923760b2 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -471,6 +471,8 @@ struct RISCVCPUConfig {
>       uint32_t mvendorid;
>       uint64_t marchid;
>       uint64_t mimpid;
> +    bool ext_zimops;
> +    bool ext_cfi;
>   
>       /* Vendor-specific custom extensions */
>       bool ext_XVentanaCondOps;


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v1 RFC Zisslpcfi 2/9] target/riscv: zisslpcfi CSR, bit positions and other definitions
  2023-02-09  6:23 ` [PATCH v1 RFC Zisslpcfi 2/9] target/riscv: zisslpcfi CSR, bit positions and other definitions Deepak Gupta
  2023-02-11  3:32   ` weiwei
@ 2023-02-15  3:31   ` LIU Zhiwei
  2023-02-15 20:42     ` Deepak Gupta
  1 sibling, 1 reply; 42+ messages in thread
From: LIU Zhiwei @ 2023-02-15  3:31 UTC (permalink / raw)
  To: Deepak Gupta, Palmer Dabbelt, Alistair Francis, Bin Meng,
	liweiwei, dbarboza
  Cc: Kip Walker, qemu-riscv, qemu-devel


On 2023/2/9 14:23, Deepak Gupta wrote:
> `zisslpcfi` extension adds two new CSRs. CSR_SSP and CSR_LPLR.
> - CSR_SSP: This CSR holds shadow stack pointer for current privilege mode
>             CSR_SSP is accessible in all modes. Each mode must establish
>             it's own CSR_SSP.
>
> - CSR_LPLR: This CSR holds label value set at the callsite by compiler.
>              On call target label check instructions are emitted by
>              compiler which check label value against value present in
>              CSR_LPRL.
>
> Enabling of `zisslpcfi` is controlled via menvcfg (for S/HS/VS/U/VU) and
> henvcfg (for VS/VU) at bit position 60.
>
> Each mode has enable/disable bits for forward cfi. Backward cfi doesn't
> have separate enable/disable bits for S and M mode. User forward cfi and
> user backward cfi enable/disable bits are in mstatus/sstatus CSR.
> Supervisor forward cfi enable/disable bit are in menvcfg and henvcfg CSR.
> Machine mode forward cfi enable/disable bit is in mseccfg CSR.
>
> If forward cfi enabled, all indirect branches must land on a landing pad
> instruction (`lpcll`, introduced in later commits). CPU/hart tracks this
> internally using a landing pad tracker called `elp` short for `expecting
> landing pad`. An interrupt can occur between an indirect branch and
> target. If such an event occurs `elp` is saved away in mstatus/sstatus
> CSR
>
> Signed-off-by: Deepak Gupta <debug@rivosinc.com>
> Signed-off-by: Kip Walker  <kip@rivosinc.com>
> ---
>   target/riscv/cpu.h      |  5 +++++
>   target/riscv/cpu_bits.h | 25 +++++++++++++++++++++++++
>   target/riscv/pmp.h      |  3 ++-
>   3 files changed, 32 insertions(+), 1 deletion(-)
>
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index 9a923760b2..18db61a06a 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -181,6 +181,11 @@ struct CPUArchState {
>   
>       uint32_t features;
>   
> +    /* CFI Extension user mode registers and state */
> +    uint32_t     lplr;
> +    target_ulong ssp;
> +    cfi_elp      elp;

I think you are coding according the sections of the specification. 
However,  when upstream code,
don't add declaration or definition if you don't use it in this patch.

This patch should be split into patches where use these definitions.

> +
>   #ifdef CONFIG_USER_ONLY
>       uint32_t elf_flags;
>   #endif
> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
> index 8b0d7e20ea..1663ba5775 100644
> --- a/target/riscv/cpu_bits.h
> +++ b/target/riscv/cpu_bits.h
> @@ -39,6 +39,10 @@
>   
>   /* Control and Status Registers */
>   
> +/* CFI CSRs */
> +#define CSR_LPLR            0x006
I didn't see the CSR encoding  number from the link in cover-letter.
> +#define CSR_SSP             0x020
> +
>   /* User Trap Setup */
>   #define CSR_USTATUS         0x000
>   #define CSR_UIE             0x004
> @@ -542,6 +546,10 @@
>   #define MSTATUS_TVM         0x00100000 /* since: priv-1.10 */
>   #define MSTATUS_TW          0x00200000 /* since: priv-1.10 */
>   #define MSTATUS_TSR         0x00400000 /* since: priv-1.10 */
> +#define MSTATUS_UFCFIEN     0x00800000 /* Zisslpcfi-0.1 */
> +#define MSTATUS_UBCFIEN     0x01000000 /* Zisslpcfi-0.1 */
> +#define MSTATUS_SPELP       0x02000000 /* Zisslpcfi-0.1 */
> +#define MSTATUS_MPELP       0x04000000 /* Zisslpcfi-0.1 */
>   #define MSTATUS_GVA         0x4000000000ULL
>   #define MSTATUS_MPV         0x8000000000ULL
>   
> @@ -572,12 +580,21 @@ typedef enum {
>   #define SSTATUS_XS          0x00018000
>   #define SSTATUS_SUM         0x00040000 /* since: priv-1.10 */
>   #define SSTATUS_MXR         0x00080000
> +#define SSTATUS_UFCFIEN     MSTATUS_UFCFIEN /* Zisslpcfi-0.1 */
> +#define SSTATUS_UBCFIEN     MSTATUS_UBCFIEN /* Zisslpcfi-0.1 */
> +#define SSTATUS_SPELP       MSTATUS_SPELP   /* Zisslpcfi-0.1 */
>   
>   #define SSTATUS64_UXL       0x0000000300000000ULL
>   
>   #define SSTATUS32_SD        0x80000000
>   #define SSTATUS64_SD        0x8000000000000000ULL
>   
> +#define CFISTATUS_M_MASK    (MSTATUS_UFCFIEN | MSTATUS_UBCFIEN | \
> +                             MSTATUS_MPELP | MSTATUS_SPELP)
> +
> +#define CFISTATUS_S_MASK    (SSTATUS_UFCFIEN | SSTATUS_UBCFIEN | \
> +                             SSTATUS_SPELP)
Why not the VSSTATUS?
> +
>   /* hstatus CSR bits */
>   #define HSTATUS_VSBE         0x00000020
>   #define HSTATUS_GVA          0x00000040
> @@ -747,10 +764,14 @@ typedef enum RISCVException {
>   #define MENVCFG_CBIE                       (3UL << 4)
>   #define MENVCFG_CBCFE                      BIT(6)
>   #define MENVCFG_CBZE                       BIT(7)
> +#define MENVCFG_SFCFIEN                    BIT(59)
> +#define MENVCFG_CFI                        BIT(60)

MENVCFG_CFIE according to the specification.  The definitions in other 
places  should also use X_CFIE.

The same comment here with Weiwei, or you can use BIT_ULL.

Zhiwei

>   #define MENVCFG_PBMTE                      (1ULL << 62)
>   #define MENVCFG_STCE                       (1ULL << 63)
>   
>   /* For RV32 */
> +#define MENVCFGH_SFCFIEN                   BIT(27)
> +#define MENVCFGH_CFI                       BIT(28)
>   #define MENVCFGH_PBMTE                     BIT(30)
>   #define MENVCFGH_STCE                      BIT(31)
>   
> @@ -763,10 +784,14 @@ typedef enum RISCVException {
>   #define HENVCFG_CBIE                       MENVCFG_CBIE
>   #define HENVCFG_CBCFE                      MENVCFG_CBCFE
>   #define HENVCFG_CBZE                       MENVCFG_CBZE
> +#define HENVCFG_SFCFIEN                    MENVCFG_SFCFIEN
> +#define HENVCFG_CFI                        MENVCFG_CFI
>   #define HENVCFG_PBMTE                      MENVCFG_PBMTE
>   #define HENVCFG_STCE                       MENVCFG_STCE
>   
>   /* For RV32 */
> +#define HENVCFGH_SFCFIEN                    MENVCFGH_SFCFIEN
> +#define HENVCFGH_CFI                        MENVCFGH_CFI
>   #define HENVCFGH_PBMTE                      MENVCFGH_PBMTE
>   #define HENVCFGH_STCE                       MENVCFGH_STCE
>   
> diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h
> index da32c61c85..f5bfc4955b 100644
> --- a/target/riscv/pmp.h
> +++ b/target/riscv/pmp.h
> @@ -43,7 +43,8 @@ typedef enum {
>       MSECCFG_MMWP  = 1 << 1,
>       MSECCFG_RLB   = 1 << 2,
>       MSECCFG_USEED = 1 << 8,
> -    MSECCFG_SSEED = 1 << 9
> +    MSECCFG_SSEED = 1 << 9,
> +    MSECCFG_MFCFIEN =  1 << 10
>   } mseccfg_field_t;
>   
>   typedef struct {


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v1 RFC Zisslpcfi 3/9] target/riscv: implements CSRs and new bits in existing CSRs in zisslpcfi
  2023-02-09  6:23 ` [PATCH v1 RFC Zisslpcfi 3/9] target/riscv: implements CSRs and new bits in existing CSRs in zisslpcfi Deepak Gupta
@ 2023-02-15  5:47   ` LIU Zhiwei
  2023-02-15  6:24     ` LIU Zhiwei
  2023-02-15 23:33     ` Deepak Gupta
  0 siblings, 2 replies; 42+ messages in thread
From: LIU Zhiwei @ 2023-02-15  5:47 UTC (permalink / raw)
  To: Deepak Gupta, Palmer Dabbelt, Alistair Francis, Bin Meng,
	liweiwei, dbarboza
  Cc: Kip Walker, qemu-riscv, qemu-devel


On 2023/2/9 14:23, Deepak Gupta wrote:
> CSR_SSP and CSR_LPLR are new CSR additions to cpu/hart. This patch allows
> access to these CSRs. A predicate routine handles access to these CSR as
> per specification.
>
> This patch also implments new bit definitions in menvcfg/henvcfg/mstatus/
> sstatus CSRs to master enabled cfi and enable forward cfi in S and M mode.
> mstatus CSR holds forward and backward cfi enabling for U mode.
>
> There is no enabling bit for backward cfi in S and M mode. It is always
> enabled if extension is implemented by CPU.
>
> Signed-off-by: Deepak Gupta <debug@rivosinc.com>
> Signed-off-by: Kip Walker  <kip@rivosinc.com>
> ---
>   target/riscv/csr.c | 137 ++++++++++++++++++++++++++++++++++++++++++++-
>   target/riscv/pmp.c |   9 +++
>   2 files changed, 145 insertions(+), 1 deletion(-)
>
> diff --git a/target/riscv/csr.c b/target/riscv/csr.c
> index 0db2c233e5..24e208ebed 100644
> --- a/target/riscv/csr.c
> +++ b/target/riscv/csr.c
> @@ -163,6 +163,50 @@ static RISCVException ctr32(CPURISCVState *env, int csrno)
>       return ctr(env, csrno);
>   }
>   
> +static RISCVException cfi(CPURISCVState *env, int csrno)
> +{
> +    /* no cfi extension */
> +    if (!env_archcpu(env)->cfg.ext_cfi) {
> +        return RISCV_EXCP_ILLEGAL_INST;
> +    }
> +    /*
> +     * CONFIG_USER_MODE always allow access for now. Better for user mode only
> +     * functionality
> +     */
> +#if !defined(CONFIG_USER_ONLY)
> +    /* current priv not M */
> +    if (env->priv != PRV_M) {
> +        /* menvcfg says no CFI */
> +        if (!get_field(env->menvcfg, MENVCFG_CFI)) {
> +            return RISCV_EXCP_ILLEGAL_INST;
> +        }
> +
> +        /* V = 1 and henvcfg says no CFI. raise virtual instr fault */
> +        if (riscv_cpu_virt_enabled(env) &&
> +            !get_field(env->henvcfg, HENVCFG_CFI)) {
> +            return RISCV_EXCP_VIRT_INSTRUCTION_FAULT;
> +        }
> +
> +        /*
> +         * LPLR and SSP are not accessible to U mode if disabled via status
> +         * CSR
> +         */
> +        if (env->priv == PRV_U) {
> +            if (csrno == CSR_LPLR &&
> +                !get_field(env->mstatus, MSTATUS_UFCFIEN)) {
> +                return RISCV_EXCP_ILLEGAL_INST;
> +            }
> +            if (csrno == CSR_SSP &&
> +                !get_field(env->mstatus, MSTATUS_UBCFIEN)) {
> +                return RISCV_EXCP_ILLEGAL_INST;
> +            }
> +        }
> +    }
> +#endif
> +
> +    return RISCV_EXCP_NONE;
> +}
> +
>   #if !defined(CONFIG_USER_ONLY)
>   static RISCVException mctr(CPURISCVState *env, int csrno)
>   {
> @@ -485,6 +529,32 @@ static RISCVException seed(CPURISCVState *env, int csrno)
>   #endif
>   }
>   
> +/* Zisslpcfi CSR_LPLR read/write */
> +static int read_lplr(CPURISCVState *env, int csrno, target_ulong *val)
> +{
> +    *val = env->lplr;
> +    return RISCV_EXCP_NONE;
> +}
> +
> +static int write_lplr(CPURISCVState *env, int csrno, target_ulong val)
> +{
> +    env->lplr = val & (LPLR_UL | LPLR_ML | LPLR_LL);
> +    return RISCV_EXCP_NONE;
> +}
> +
> +/* Zisslpcfi CSR_SSP read/write */
> +static int read_ssp(CPURISCVState *env, int csrno, target_ulong *val)
> +{
> +    *val = env->ssp;
> +    return RISCV_EXCP_NONE;
> +}
> +
> +static int write_ssp(CPURISCVState *env, int csrno, target_ulong val)
> +{
> +    env->ssp = val;
> +    return RISCV_EXCP_NONE;
> +}
> +
>   /* User Floating-Point CSRs */
>   static RISCVException read_fflags(CPURISCVState *env, int csrno,
>                                     target_ulong *val)
> @@ -1227,7 +1297,7 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno,
>   
>       /* flush tlb on mstatus fields that affect VM */
>       if ((val ^ mstatus) & (MSTATUS_MXR | MSTATUS_MPP | MSTATUS_MPV |
> -            MSTATUS_MPRV | MSTATUS_SUM)) {
> +            MSTATUS_MPRV | MSTATUS_SUM | MSTATUS_UFCFIEN | MSTATUS_UBCFIEN)) {

These two fields should be guarded by the check of ext_cfi.

And MSTATUS_UBCFIEN field change don't need flush tlb.

I didn't get why we should flush tlb for forward cfi. For background, 
there are some enhancement for the PTE and PMP, we may need do some
memory adjustments. But forward cfi just adds some instructions. Why we 
should flush tlb? Does the tlb can't be used any more?

>           tlb_flush(env_cpu(env));
>       }
>       mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE |
> @@ -1250,6 +1320,11 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno,
>           }
>       }
>   
> +    /* If cfi extension is available, then apply cfi status mask */
> +    if (env_archcpu(env)->cfg.ext_cfi) {
> +        mask |= CFISTATUS_M_MASK;
> +    }
> +
>       mstatus = (mstatus & ~mask) | (val & mask);
>   
>       if (xl > MXL_RV32) {
> @@ -1880,9 +1955,17 @@ static RISCVException write_menvcfg(CPURISCVState *env, int csrno,
>                                     target_ulong val)
>   {
>       uint64_t mask = MENVCFG_FIOM | MENVCFG_CBIE | MENVCFG_CBCFE | MENVCFG_CBZE;
> +    uint64_t cfi_mask = MENVCFG_CFI | MENVCFG_SFCFIEN;
>   
>       if (riscv_cpu_mxl(env) == MXL_RV64) {
>           mask |= MENVCFG_PBMTE | MENVCFG_STCE;
> +        if (env_archcpu(env)->cfg.ext_cfi) {
> +            mask |= cfi_mask;
> +            /* If any cfi enabling bit changes in menvcfg, flush tlb */
> +            if ((val ^ env->menvcfg) & cfi_mask) {
> +                tlb_flush(env_cpu(env));
Don't flush tlb for MENVCFG_SFCFIE field changes.
> +            }
> +        }
>       }
>       env->menvcfg = (env->menvcfg & ~mask) | (val & mask);
>   
> @@ -1900,8 +1983,17 @@ static RISCVException write_menvcfgh(CPURISCVState *env, int csrno,
>                                     target_ulong val)
>   {
>       uint64_t mask = MENVCFG_PBMTE | MENVCFG_STCE;
> +    uint64_t cfi_mask = MENVCFG_CFI | MENVCFG_SFCFIEN;
MENVCFG_SFCFIE
>       uint64_t valh = (uint64_t)val << 32;
>   
> +    if (env_archcpu(env)->cfg.ext_cfi) {
> +            mask |= cfi_mask;
> +            /* If any cfi enabling bit changes in menvcfg, flush tlb */
> +            if ((val ^ env->menvcfg) & cfi_mask) {
> +                tlb_flush(env_cpu(env));
> +            }
If SFCFIE field change, we should not flush the tlb.
> +    }
> +
>       env->menvcfg = (env->menvcfg & ~mask) | (valh & mask);
>   
>       return RISCV_EXCP_NONE;
> @@ -1954,6 +2046,7 @@ static RISCVException write_henvcfg(CPURISCVState *env, int csrno,
>                                     target_ulong val)
>   {
>       uint64_t mask = HENVCFG_FIOM | HENVCFG_CBIE | HENVCFG_CBCFE | HENVCFG_CBZE;
> +    uint64_t cfi_mask = HENVCFG_CFI | HENVCFG_SFCFIEN;

HENVCFG_SFCFIE

>       RISCVException ret;
>   
>       ret = smstateen_acc_ok(env, 0, SMSTATEEN0_HSENVCFG);
> @@ -1963,6 +2056,18 @@ static RISCVException write_henvcfg(CPURISCVState *env, int csrno,
>   
>       if (riscv_cpu_mxl(env) == MXL_RV64) {
>           mask |= HENVCFG_PBMTE | HENVCFG_STCE;
> +        /*
> +         * If cfi available and menvcfg.CFI = 1, then apply cfi mask for
> +         * henvcfg
> +         */
> +        if (env_archcpu(env)->cfg.ext_cfi &&
> +            get_field(env->menvcfg, MENVCFG_CFI)) {
> +            mask |= cfi_mask;
> +            /* If any cfi enabling bit changes in henvcfg, flush tlb */
> +            if ((val ^ env->henvcfg) & cfi_mask) {
> +                tlb_flush(env_cpu(env));
> +            }
If SFCFIE field change, we should not flush the tlb.
> +        }
>       }
>   
>       env->henvcfg = (env->henvcfg & ~mask) | (val & mask);
> @@ -1988,9 +2093,19 @@ static RISCVException write_henvcfgh(CPURISCVState *env, int csrno,
>                                     target_ulong val)
>   {
>       uint64_t mask = HENVCFG_PBMTE | HENVCFG_STCE;
> +    uint64_t cfi_mask = HENVCFG_CFI | HENVCFG_SFCFIEN;
>       uint64_t valh = (uint64_t)val << 32;
>       RISCVException ret;
>   
> +    if (env_archcpu(env)->cfg.ext_cfi &&
> +        get_field(env->menvcfg, MENVCFG_CFI)) {
> +        mask |= cfi_mask;
> +        /* If any cfi enabling bit changes in henvcfg, flush tlb */
> +        if ((val ^ env->henvcfg) & cfi_mask) {
> +            tlb_flush(env_cpu(env));
> +        }
> +    }
> +
>       ret = smstateen_acc_ok(env, 0, SMSTATEEN0_HSENVCFG);
>       if (ret != RISCV_EXCP_NONE) {
>           return ret;
> @@ -2270,6 +2385,11 @@ static RISCVException read_sstatus_i128(CPURISCVState *env, int csrno,
>           mask |= SSTATUS64_UXL;
>       }
>   
> +    if ((env_archcpu(env)->cfg.ext_cfi) &&
> +         get_field(env->menvcfg, MENVCFG_CFI)) {
> +        mask |= CFISTATUS_S_MASK;
> +    }
> +
>       *val = int128_make128(sstatus, add_status_sd(MXL_RV128, sstatus));
>       return RISCV_EXCP_NONE;
>   }
> @@ -2281,6 +2401,11 @@ static RISCVException read_sstatus(CPURISCVState *env, int csrno,
>       if (env->xl != MXL_RV32 || env->debugger) {
>           mask |= SSTATUS64_UXL;
>       }
> +
> +    if ((env_archcpu(env)->cfg.ext_cfi) &&
> +         get_field(env->menvcfg, MENVCFG_CFI)) {
> +        mask |= CFISTATUS_S_MASK;
> +    }
>       /* TODO: Use SXL not MXL. */
>       *val = add_status_sd(riscv_cpu_mxl(env), env->mstatus & mask);
>       return RISCV_EXCP_NONE;
> @@ -2296,6 +2421,12 @@ static RISCVException write_sstatus(CPURISCVState *env, int csrno,
>               mask |= SSTATUS64_UXL;
>           }
>       }
> +
> +    /* If cfi available and menvcfg.CFI = 1, apply CFI mask for sstatus */
> +    if ((env_archcpu(env)->cfg.ext_cfi) &&
> +         get_field(env->menvcfg, MENVCFG_CFI)) {
> +        mask |= CFISTATUS_S_MASK;
> +    }
>       target_ulong newval = (env->mstatus & ~mask) | (val & mask);
>       return write_mstatus(env, CSR_MSTATUS, newval);
>   }
> @@ -4001,6 +4132,10 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
>       /* Crypto Extension */
>       [CSR_SEED] = { "seed", seed, NULL, NULL, rmw_seed },
>   
> +    /* User mode CFI CSR */
> +    [CSR_LPLR] = { "lplr", cfi, read_lplr, write_lplr },
> +    [CSR_SSP]  = { "ssp", cfi, read_ssp, write_ssp },
> +
>   #if !defined(CONFIG_USER_ONLY)
>       /* Machine Timers and Counters */
>       [CSR_MCYCLE]    = { "mcycle",    any,   read_hpmcounter,
> diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c
> index d1126a6066..89745d46cd 100644
> --- a/target/riscv/pmp.c
> +++ b/target/riscv/pmp.c
> @@ -579,6 +579,15 @@ void mseccfg_csr_write(CPURISCVState *env, target_ulong val)
>       /* Sticky bits */
>       val |= (env->mseccfg & (MSECCFG_MMWP | MSECCFG_MML));
>   
> +    /* M-mode forward cfi to be enabled if cfi extension is implemented */
> +    if (env_archcpu(env)->cfg.ext_cfi) {
> +        val |= (val & MSECCFG_MFCFIEN);
This statement does nothing. Is it a typo?
> +        /* If forward cfi in mseccfg is being toggled, flush tlb */
> +        if ((env->mseccfg ^ val) & MSECCFG_MFCFIEN) {
> +                tlb_flush(env_cpu(env));
> +        }

No need flush tlb.

Zhiwei

> +    }
> +
>       env->mseccfg = val;
>   }
>   


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v1 RFC Zisslpcfi 5/9] target/riscv: state save and restore of zisslppcfi state
  2023-02-09  6:24 ` [PATCH v1 RFC Zisslpcfi 5/9] target/riscv: state save and restore of zisslppcfi state Deepak Gupta
@ 2023-02-15  6:10   ` LIU Zhiwei
  2023-02-15 23:13     ` Deepak Gupta
  0 siblings, 1 reply; 42+ messages in thread
From: LIU Zhiwei @ 2023-02-15  6:10 UTC (permalink / raw)
  To: Deepak Gupta, Palmer Dabbelt, Alistair Francis, Bin Meng,
	liweiwei, dbarboza
  Cc: Kip Walker, qemu-riscv, qemu-devel


On 2023/2/9 14:24, Deepak Gupta wrote:
> zisslpcfi's forward cfi if enabled on a hart, enables tracking of
> indirect branches. CPU/hart internally keeps a state `elp` short
> for expecting landing pad instruction. This state goes into
> LP_EXPECTED on an indirect branch. But an interrupt/exception can occur
> before target instruction is executed. In such a case this state must be
> preserved so that it can be restored later. zisslpcfi saves elp state in
> `sstatus` CSR.

And mstatus CSR.

Otherwise,

Reviewed-by: LIU Zhiwei <zhiwei_liu@linux.alibaba.com>

> This patch saves elp state in sstatus CSR on trap delivery
> while restores from sstatus CSR on trap return.
>
> Additionally state in sstatus CSR must have save and restore zisslpcfi
> state on exiting from hypervisor and entering into hypervisor.
>
> Signed-off-by: Deepak Gupta <debug@rivosinc.com>
> Signed-off-by: Kip Walker  <kip@rivosinc.com>
> ---
>   target/riscv/cpu_bits.h   |  5 +++++
>   target/riscv/cpu_helper.c | 26 ++++++++++++++++++++++++++
>   target/riscv/op_helper.c  | 12 ++++++++++++
>   3 files changed, 43 insertions(+)
>
> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
> index 1663ba5775..37100ec8f6 100644
> --- a/target/riscv/cpu_bits.h
> +++ b/target/riscv/cpu_bits.h
> @@ -594,6 +594,11 @@ typedef enum {
>   
>   #define CFISTATUS_S_MASK    (SSTATUS_UFCFIEN | SSTATUS_UBCFIEN | \
>                                SSTATUS_SPELP)
> +/* enum for branch tracking state in cpu/hart */
> +typedef enum {
> +    NO_LP_EXPECTED = 0,
> +    LP_EXPECTED = 1,
> +} cfi_elp;
>   
>   /* hstatus CSR bits */
>   #define HSTATUS_VSBE         0x00000020
> diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> index a397023840..fc188683c9 100644
> --- a/target/riscv/cpu_helper.c
> +++ b/target/riscv/cpu_helper.c
> @@ -534,6 +534,16 @@ void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env)
>       if (riscv_has_ext(env, RVF)) {
>           mstatus_mask |= MSTATUS_FS;
>       }
> +
> +    /*
> +     * If cfi extension available, menvcfg.CFI = 1 and henvcfg.CFI = 1,
> +     * then apply CFI mask on mstatus
> +     */
> +    if (env_archcpu(env)->cfg.ext_cfi &&
> +        get_field(env->menvcfg, MENVCFG_CFI) &&
> +        get_field(env->henvcfg, HENVCFG_CFI)) {
> +        mstatus_mask |= CFISTATUS_S_MASK;
> +    }
>       bool current_virt = riscv_cpu_virt_enabled(env);
>   
>       g_assert(riscv_has_ext(env, RVH));
> @@ -1723,6 +1733,10 @@ void riscv_cpu_do_interrupt(CPUState *cs)
>       if (env->priv <= PRV_S &&
>               cause < TARGET_LONG_BITS && ((deleg >> cause) & 1)) {
>           /* handle the trap in S-mode */
> +        /* save elp status */
> +        if (cpu_get_fcfien(env)) {
> +            env->mstatus = set_field(env->mstatus, MSTATUS_SPELP, env->elp);
> +        }
>           if (riscv_has_ext(env, RVH)) {
>               uint64_t hdeleg = async ? env->hideleg : env->hedeleg;
>   
> @@ -1772,6 +1786,10 @@ void riscv_cpu_do_interrupt(CPUState *cs)
>           riscv_cpu_set_mode(env, PRV_S);
>       } else {
>           /* handle the trap in M-mode */
> +        /* save elp status */
> +        if (cpu_get_fcfien(env)) {
> +            env->mstatus = set_field(env->mstatus, MSTATUS_MPELP, env->elp);
> +        }
>           if (riscv_has_ext(env, RVH)) {
>               if (riscv_cpu_virt_enabled(env)) {
>                   riscv_cpu_swap_hypervisor_regs(env);
> @@ -1803,6 +1821,14 @@ void riscv_cpu_do_interrupt(CPUState *cs)
>           riscv_cpu_set_mode(env, PRV_M);
>       }
>   
> +    /*
> +     * Interrupt/exception/trap delivery is asynchronous event and as per
> +     * Zisslpcfi spec CPU should clear up the ELP state. If cfi extension is
> +     * available, clear ELP state.
> +     */
> +    if (cpu->cfg.ext_cfi) {
> +        env->elp = NO_LP_EXPECTED;
> +    }
>       /* NOTE: it is not necessary to yield load reservations here. It is only
>        * necessary for an SC from "another hart" to cause a load reservation
>        * to be yielded. Refer to the memory consistency model section of the
> diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
> index 878bcb03b8..d15893aa82 100644
> --- a/target/riscv/op_helper.c
> +++ b/target/riscv/op_helper.c
> @@ -176,6 +176,12 @@ target_ulong helper_sret(CPURISCVState *env)
>           riscv_cpu_set_virt_enabled(env, prev_virt);
>       }
>   
> +    /* If forward cfi enabled for target, restore elp status */
> +    if (cpu_get_fcfien(env)) {
> +        env->elp = get_field(env->mstatus, MSTATUS_SPELP);
> +        env->mstatus = set_field(env->mstatus, MSTATUS_SPELP, 0);
> +    }
> +
>       riscv_cpu_set_mode(env, prev_priv);
>   
>       return retpc;
> @@ -220,6 +226,12 @@ target_ulong helper_mret(CPURISCVState *env)
>           riscv_cpu_set_virt_enabled(env, prev_virt);
>       }
>   
> +    /* If forward cfi enabled for target, restore elp status */
> +    if (cpu_get_fcfien(env)) {
> +        env->elp = get_field(env->mstatus, MSTATUS_MPELP);
> +        env->mstatus = set_field(env->mstatus, MSTATUS_MPELP, 0);
> +    }
> +
>       return retpc;
>   }
>   


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v1 RFC Zisslpcfi 3/9] target/riscv: implements CSRs and new bits in existing CSRs in zisslpcfi
  2023-02-15  5:47   ` LIU Zhiwei
@ 2023-02-15  6:24     ` LIU Zhiwei
  2023-02-15 23:42       ` Deepak Gupta
  2023-02-15 23:33     ` Deepak Gupta
  1 sibling, 1 reply; 42+ messages in thread
From: LIU Zhiwei @ 2023-02-15  6:24 UTC (permalink / raw)
  To: Deepak Gupta, Palmer Dabbelt, Alistair Francis, Bin Meng,
	liweiwei, dbarboza
  Cc: Kip Walker, qemu-riscv, qemu-devel

I don't find the modification for read_mstatus.

Zhiwei

On 2023/2/15 13:47, LIU Zhiwei wrote:
>
> On 2023/2/9 14:23, Deepak Gupta wrote:
>> CSR_SSP and CSR_LPLR are new CSR additions to cpu/hart. This patch 
>> allows
>> access to these CSRs. A predicate routine handles access to these CSR as
>> per specification.
>>
>> This patch also implments new bit definitions in 
>> menvcfg/henvcfg/mstatus/
>> sstatus CSRs to master enabled cfi and enable forward cfi in S and M 
>> mode.
>> mstatus CSR holds forward and backward cfi enabling for U mode.
>>
>> There is no enabling bit for backward cfi in S and M mode. It is always
>> enabled if extension is implemented by CPU.
>>
>> Signed-off-by: Deepak Gupta <debug@rivosinc.com>
>> Signed-off-by: Kip Walker  <kip@rivosinc.com>
>> ---
>>   target/riscv/csr.c | 137 ++++++++++++++++++++++++++++++++++++++++++++-
>>   target/riscv/pmp.c |   9 +++
>>   2 files changed, 145 insertions(+), 1 deletion(-)
>>
>> diff --git a/target/riscv/csr.c b/target/riscv/csr.c
>> index 0db2c233e5..24e208ebed 100644
>> --- a/target/riscv/csr.c
>> +++ b/target/riscv/csr.c
>> @@ -163,6 +163,50 @@ static RISCVException ctr32(CPURISCVState *env, 
>> int csrno)
>>       return ctr(env, csrno);
>>   }
>>   +static RISCVException cfi(CPURISCVState *env, int csrno)
>> +{
>> +    /* no cfi extension */
>> +    if (!env_archcpu(env)->cfg.ext_cfi) {
>> +        return RISCV_EXCP_ILLEGAL_INST;
>> +    }
>> +    /*
>> +     * CONFIG_USER_MODE always allow access for now. Better for user 
>> mode only
>> +     * functionality
>> +     */
>> +#if !defined(CONFIG_USER_ONLY)
>> +    /* current priv not M */
>> +    if (env->priv != PRV_M) {
>> +        /* menvcfg says no CFI */
>> +        if (!get_field(env->menvcfg, MENVCFG_CFI)) {
>> +            return RISCV_EXCP_ILLEGAL_INST;
>> +        }
>> +
>> +        /* V = 1 and henvcfg says no CFI. raise virtual instr fault */
>> +        if (riscv_cpu_virt_enabled(env) &&
>> +            !get_field(env->henvcfg, HENVCFG_CFI)) {
>> +            return RISCV_EXCP_VIRT_INSTRUCTION_FAULT;
>> +        }
>> +
>> +        /*
>> +         * LPLR and SSP are not accessible to U mode if disabled via 
>> status
>> +         * CSR
>> +         */
>> +        if (env->priv == PRV_U) {
>> +            if (csrno == CSR_LPLR &&
>> +                !get_field(env->mstatus, MSTATUS_UFCFIEN)) {
>> +                return RISCV_EXCP_ILLEGAL_INST;
>> +            }
>> +            if (csrno == CSR_SSP &&
>> +                !get_field(env->mstatus, MSTATUS_UBCFIEN)) {
>> +                return RISCV_EXCP_ILLEGAL_INST;
>> +            }
>> +        }
>> +    }
>> +#endif
>> +
>> +    return RISCV_EXCP_NONE;
>> +}
>> +
>>   #if !defined(CONFIG_USER_ONLY)
>>   static RISCVException mctr(CPURISCVState *env, int csrno)
>>   {
>> @@ -485,6 +529,32 @@ static RISCVException seed(CPURISCVState *env, 
>> int csrno)
>>   #endif
>>   }
>>   +/* Zisslpcfi CSR_LPLR read/write */
>> +static int read_lplr(CPURISCVState *env, int csrno, target_ulong *val)
>> +{
>> +    *val = env->lplr;
>> +    return RISCV_EXCP_NONE;
>> +}
>> +
>> +static int write_lplr(CPURISCVState *env, int csrno, target_ulong val)
>> +{
>> +    env->lplr = val & (LPLR_UL | LPLR_ML | LPLR_LL);
>> +    return RISCV_EXCP_NONE;
>> +}
>> +
>> +/* Zisslpcfi CSR_SSP read/write */
>> +static int read_ssp(CPURISCVState *env, int csrno, target_ulong *val)
>> +{
>> +    *val = env->ssp;
>> +    return RISCV_EXCP_NONE;
>> +}
>> +
>> +static int write_ssp(CPURISCVState *env, int csrno, target_ulong val)
>> +{
>> +    env->ssp = val;
>> +    return RISCV_EXCP_NONE;
>> +}
>> +
>>   /* User Floating-Point CSRs */
>>   static RISCVException read_fflags(CPURISCVState *env, int csrno,
>>                                     target_ulong *val)
>> @@ -1227,7 +1297,7 @@ static RISCVException 
>> write_mstatus(CPURISCVState *env, int csrno,
>>         /* flush tlb on mstatus fields that affect VM */
>>       if ((val ^ mstatus) & (MSTATUS_MXR | MSTATUS_MPP | MSTATUS_MPV |
>> -            MSTATUS_MPRV | MSTATUS_SUM)) {
>> +            MSTATUS_MPRV | MSTATUS_SUM | MSTATUS_UFCFIEN | 
>> MSTATUS_UBCFIEN)) {
>
> These two fields should be guarded by the check of ext_cfi.
>
> And MSTATUS_UBCFIEN field change don't need flush tlb.
>
> I didn't get why we should flush tlb for forward cfi. For background, 
> there are some enhancement for the PTE and PMP, we may need do some
> memory adjustments. But forward cfi just adds some instructions. Why 
> we should flush tlb? Does the tlb can't be used any more?
>
>>           tlb_flush(env_cpu(env));
>>       }
>>       mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE |
>> @@ -1250,6 +1320,11 @@ static RISCVException 
>> write_mstatus(CPURISCVState *env, int csrno,
>>           }
>>       }
>>   +    /* If cfi extension is available, then apply cfi status mask */
>> +    if (env_archcpu(env)->cfg.ext_cfi) {
>> +        mask |= CFISTATUS_M_MASK;
>> +    }
>> +
>>       mstatus = (mstatus & ~mask) | (val & mask);
>>         if (xl > MXL_RV32) {
>> @@ -1880,9 +1955,17 @@ static RISCVException 
>> write_menvcfg(CPURISCVState *env, int csrno,
>>                                     target_ulong val)
>>   {
>>       uint64_t mask = MENVCFG_FIOM | MENVCFG_CBIE | MENVCFG_CBCFE | 
>> MENVCFG_CBZE;
>> +    uint64_t cfi_mask = MENVCFG_CFI | MENVCFG_SFCFIEN;
>>         if (riscv_cpu_mxl(env) == MXL_RV64) {
>>           mask |= MENVCFG_PBMTE | MENVCFG_STCE;
>> +        if (env_archcpu(env)->cfg.ext_cfi) {
>> +            mask |= cfi_mask;
>> +            /* If any cfi enabling bit changes in menvcfg, flush tlb */
>> +            if ((val ^ env->menvcfg) & cfi_mask) {
>> +                tlb_flush(env_cpu(env));
> Don't flush tlb for MENVCFG_SFCFIE field changes.
>> +            }
>> +        }
>>       }
>>       env->menvcfg = (env->menvcfg & ~mask) | (val & mask);
>>   @@ -1900,8 +1983,17 @@ static RISCVException 
>> write_menvcfgh(CPURISCVState *env, int csrno,
>>                                     target_ulong val)
>>   {
>>       uint64_t mask = MENVCFG_PBMTE | MENVCFG_STCE;
>> +    uint64_t cfi_mask = MENVCFG_CFI | MENVCFG_SFCFIEN;
> MENVCFG_SFCFIE
>>       uint64_t valh = (uint64_t)val << 32;
>>   +    if (env_archcpu(env)->cfg.ext_cfi) {
>> +            mask |= cfi_mask;
>> +            /* If any cfi enabling bit changes in menvcfg, flush tlb */
>> +            if ((val ^ env->menvcfg) & cfi_mask) {
>> +                tlb_flush(env_cpu(env));
>> +            }
> If SFCFIE field change, we should not flush the tlb.
>> +    }
>> +
>>       env->menvcfg = (env->menvcfg & ~mask) | (valh & mask);
>>         return RISCV_EXCP_NONE;
>> @@ -1954,6 +2046,7 @@ static RISCVException 
>> write_henvcfg(CPURISCVState *env, int csrno,
>>                                     target_ulong val)
>>   {
>>       uint64_t mask = HENVCFG_FIOM | HENVCFG_CBIE | HENVCFG_CBCFE | 
>> HENVCFG_CBZE;
>> +    uint64_t cfi_mask = HENVCFG_CFI | HENVCFG_SFCFIEN;
>
> HENVCFG_SFCFIE
>
>>       RISCVException ret;
>>         ret = smstateen_acc_ok(env, 0, SMSTATEEN0_HSENVCFG);
>> @@ -1963,6 +2056,18 @@ static RISCVException 
>> write_henvcfg(CPURISCVState *env, int csrno,
>>         if (riscv_cpu_mxl(env) == MXL_RV64) {
>>           mask |= HENVCFG_PBMTE | HENVCFG_STCE;
>> +        /*
>> +         * If cfi available and menvcfg.CFI = 1, then apply cfi mask 
>> for
>> +         * henvcfg
>> +         */
>> +        if (env_archcpu(env)->cfg.ext_cfi &&
>> +            get_field(env->menvcfg, MENVCFG_CFI)) {
>> +            mask |= cfi_mask;
>> +            /* If any cfi enabling bit changes in henvcfg, flush tlb */
>> +            if ((val ^ env->henvcfg) & cfi_mask) {
>> +                tlb_flush(env_cpu(env));
>> +            }
> If SFCFIE field change, we should not flush the tlb.
>> +        }
>>       }
>>         env->henvcfg = (env->henvcfg & ~mask) | (val & mask);
>> @@ -1988,9 +2093,19 @@ static RISCVException 
>> write_henvcfgh(CPURISCVState *env, int csrno,
>>                                     target_ulong val)
>>   {
>>       uint64_t mask = HENVCFG_PBMTE | HENVCFG_STCE;
>> +    uint64_t cfi_mask = HENVCFG_CFI | HENVCFG_SFCFIEN;
>>       uint64_t valh = (uint64_t)val << 32;
>>       RISCVException ret;
>>   +    if (env_archcpu(env)->cfg.ext_cfi &&
>> +        get_field(env->menvcfg, MENVCFG_CFI)) {
>> +        mask |= cfi_mask;
>> +        /* If any cfi enabling bit changes in henvcfg, flush tlb */
>> +        if ((val ^ env->henvcfg) & cfi_mask) {
>> +            tlb_flush(env_cpu(env));
>> +        }
>> +    }
>> +
>>       ret = smstateen_acc_ok(env, 0, SMSTATEEN0_HSENVCFG);
>>       if (ret != RISCV_EXCP_NONE) {
>>           return ret;
>> @@ -2270,6 +2385,11 @@ static RISCVException 
>> read_sstatus_i128(CPURISCVState *env, int csrno,
>>           mask |= SSTATUS64_UXL;
>>       }
>>   +    if ((env_archcpu(env)->cfg.ext_cfi) &&
>> +         get_field(env->menvcfg, MENVCFG_CFI)) {
>> +        mask |= CFISTATUS_S_MASK;
>> +    }
>> +
>>       *val = int128_make128(sstatus, add_status_sd(MXL_RV128, sstatus));
>>       return RISCV_EXCP_NONE;
>>   }
>> @@ -2281,6 +2401,11 @@ static RISCVException 
>> read_sstatus(CPURISCVState *env, int csrno,
>>       if (env->xl != MXL_RV32 || env->debugger) {
>>           mask |= SSTATUS64_UXL;
>>       }
>> +
>> +    if ((env_archcpu(env)->cfg.ext_cfi) &&
>> +         get_field(env->menvcfg, MENVCFG_CFI)) {
>> +        mask |= CFISTATUS_S_MASK;
>> +    }
>>       /* TODO: Use SXL not MXL. */
>>       *val = add_status_sd(riscv_cpu_mxl(env), env->mstatus & mask);
>>       return RISCV_EXCP_NONE;
>> @@ -2296,6 +2421,12 @@ static RISCVException 
>> write_sstatus(CPURISCVState *env, int csrno,
>>               mask |= SSTATUS64_UXL;
>>           }
>>       }
>> +
>> +    /* If cfi available and menvcfg.CFI = 1, apply CFI mask for 
>> sstatus */
>> +    if ((env_archcpu(env)->cfg.ext_cfi) &&
>> +         get_field(env->menvcfg, MENVCFG_CFI)) {
>> +        mask |= CFISTATUS_S_MASK;
>> +    }
>>       target_ulong newval = (env->mstatus & ~mask) | (val & mask);
>>       return write_mstatus(env, CSR_MSTATUS, newval);
>>   }
>> @@ -4001,6 +4132,10 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
>>       /* Crypto Extension */
>>       [CSR_SEED] = { "seed", seed, NULL, NULL, rmw_seed },
>>   +    /* User mode CFI CSR */
>> +    [CSR_LPLR] = { "lplr", cfi, read_lplr, write_lplr },
>> +    [CSR_SSP]  = { "ssp", cfi, read_ssp, write_ssp },
>> +
>>   #if !defined(CONFIG_USER_ONLY)
>>       /* Machine Timers and Counters */
>>       [CSR_MCYCLE]    = { "mcycle",    any,   read_hpmcounter,
>> diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c
>> index d1126a6066..89745d46cd 100644
>> --- a/target/riscv/pmp.c
>> +++ b/target/riscv/pmp.c
>> @@ -579,6 +579,15 @@ void mseccfg_csr_write(CPURISCVState *env, 
>> target_ulong val)
>>       /* Sticky bits */
>>       val |= (env->mseccfg & (MSECCFG_MMWP | MSECCFG_MML));
>>   +    /* M-mode forward cfi to be enabled if cfi extension is 
>> implemented */
>> +    if (env_archcpu(env)->cfg.ext_cfi) {
>> +        val |= (val & MSECCFG_MFCFIEN);
> This statement does nothing. Is it a typo?
>> +        /* If forward cfi in mseccfg is being toggled, flush tlb */
>> +        if ((env->mseccfg ^ val) & MSECCFG_MFCFIEN) {
>> +                tlb_flush(env_cpu(env));
>> +        }
>
> No need flush tlb.
>
> Zhiwei
>
>> +    }
>> +
>>       env->mseccfg = val;
>>   }


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v1 RFC Zisslpcfi 4/9] target/riscv: helper functions for forward and backward cfi
  2023-02-09  6:23 ` [PATCH v1 RFC Zisslpcfi 4/9] target/riscv: helper functions for forward and backward cfi Deepak Gupta
@ 2023-02-15  6:26   ` LIU Zhiwei
  2023-02-15 23:35     ` Deepak Gupta
  0 siblings, 1 reply; 42+ messages in thread
From: LIU Zhiwei @ 2023-02-15  6:26 UTC (permalink / raw)
  To: Deepak Gupta, Palmer Dabbelt, Alistair Francis, Bin Meng,
	liweiwei, dbarboza
  Cc: Kip Walker, qemu-riscv, qemu-devel


On 2023/2/9 14:23, Deepak Gupta wrote:
> Implementation for forward cfi and backward cfi needs helper function
> to determine if currently fcfi and bcfi are enabled. Enable depends on
> privilege mode and settings in sstatus/menvcfg/henvcfg/mseccfg CSRs.
>
> Signed-off-by: Deepak Gupta <debug@rivosinc.com>
> Signed-off-by: Kip Walker  <kip@rivosinc.com>
> ---
>   target/riscv/cpu.h        |  2 ++
>   target/riscv/cpu_helper.c | 51 +++++++++++++++++++++++++++++++++++++++
>   2 files changed, 53 insertions(+)
>
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index 18db61a06a..d14ea4f91d 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -568,6 +568,8 @@ bool riscv_cpu_virt_enabled(CPURISCVState *env);
>   void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable);
>   bool riscv_cpu_two_stage_lookup(int mmu_idx);
>   int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch);
> +bool cpu_get_fcfien(CPURISCVState *env);
> +bool cpu_get_bcfien(CPURISCVState *env);
>   hwaddr riscv_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
>   G_NORETURN void  riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
>                                                  MMUAccessType access_type, int mmu_idx,
> diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> index 9a28816521..a397023840 100644
> --- a/target/riscv/cpu_helper.c
> +++ b/target/riscv/cpu_helper.c
> @@ -30,6 +30,7 @@
>   #include "sysemu/cpu-timers.h"
>   #include "cpu_bits.h"
>   #include "debug.h"
> +#include "pmp.h"
>   
>   int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch)
>   {
> @@ -40,6 +41,56 @@ int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch)
>   #endif
>   }
>   
> +bool cpu_get_fcfien(CPURISCVState *env)
> +{
> +#ifdef CONFIG_USER_ONLY
> +    return false;
> +#else
> +    /* no cfi extension, return false */
> +    if (!env_archcpu(env)->cfg.ext_cfi) {
> +        return false;
> +    }
> +
> +    switch (env->priv) {
> +    case PRV_U:
> +        return (env->mstatus & MSTATUS_UFCFIEN) ? true : false;

It's not right. We should also check for menvcfg.cfie. The same to other 
checks in S mode or U mode.

Zhiwei

> +    case PRV_S:
> +        return (env->menvcfg & MENVCFG_SFCFIEN) ? true : false;
> +    case PRV_M:
> +        return (env->mseccfg & MSECCFG_MFCFIEN) ? true : false;
> +    default:
> +        g_assert_not_reached();
> +    }
> +#endif
> +}
> +
> +bool cpu_get_bcfien(CPURISCVState *env)
> +{
> +#ifdef CONFIG_USER_ONLY
> +    return false;
> +#else
> +    /* no cfi extension, return false */
> +    if (!env_archcpu(env)->cfg.ext_cfi) {
> +        return false;
> +    }
> +
> +    switch (env->priv) {
> +    case PRV_U:
> +        return (env->mstatus & MSTATUS_UBCFIEN) ? true : false;
> +
> +        /*
> +         * no gating for back cfi in M/S mode. back cfi is always on for
> +         * M/S mode
> +         */
> +    case PRV_S:
> +    case PRV_M:
> +        return true;
> +    default:
> +        g_assert_not_reached();
> +    }
> +#endif
> +}
> +
>   void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc,
>                             target_ulong *cs_base, uint32_t *pflags)
>   {


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v1 RFC Zisslpcfi 6/9] target/riscv: MMU changes for back cfi's shadow stack
  2023-02-09  6:24 ` [PATCH v1 RFC Zisslpcfi 6/9] target/riscv: MMU changes for back cfi's shadow stack Deepak Gupta
@ 2023-02-15  8:43   ` LIU Zhiwei
  2023-02-15 23:57     ` Deepak Gupta
  0 siblings, 1 reply; 42+ messages in thread
From: LIU Zhiwei @ 2023-02-15  8:43 UTC (permalink / raw)
  To: Deepak Gupta, Palmer Dabbelt, Alistair Francis, Bin Meng,
	liweiwei, dbarboza
  Cc: Kip Walker, qemu-riscv, qemu-devel


On 2023/2/9 14:24, Deepak Gupta wrote:
> zisslpcfi protects returns(back cfi) using shadow stack. If compiled with
> enabled compiler, function prologs will have `sspush ra` instruction to
> push return address on shadow stack and function epilogs will have
> `sspop t0; sschckra` instruction sequences. `sspop t0` will pop the
> value from top of the shadow stack in t0. `sschckra` will compare `t0`
> and `x1` and if they don't match then hart will raise an illegal
> instruction exception.
>
> Shadow stack is read-only memory except stores can be performed via
> `sspush` and `ssamoswap` instructions. This requires new PTE encoding for
> shadow stack. zisslpcfi uses R=0, W=1, X=0 (an existing reserved encoding
> ) to encode a shadow stack. If backward cfi is not enabled for current
> mode, shadow stack PTE encodings remain reserved. Regular stores to
> shadow stack raise AMO/store access fault. Shadow stack loads/stores on
> regular memory raise load access/store access fault.
>
> This patch creates a new MMU TLB index for shadow stack and flushes TLB
> for shadow stack on privileges changes. This patch doesn't implement
> `Smepmp` related enforcement on shadow stack pmp entry. Reason being qemu
> doesn't have `Smepmp` implementation yet.
I don't know that the Smepmp means here. QEMU has supported the epmp.
>   `Smepmp` enforcement should come
> whenever it is implemented.
>
> Signed-off-by: Deepak Gupta <debug@rivosinc.com>
> Signed-off-by: Kip Walker  <kip@rivosinc.com>
> ---
>   target/riscv/cpu-param.h  |   1 +
>   target/riscv/cpu.c        |   2 +
>   target/riscv/cpu.h        |   3 ++
>   target/riscv/cpu_helper.c | 107 +++++++++++++++++++++++++++++++-------
>   4 files changed, 94 insertions(+), 19 deletions(-)
>
> diff --git a/target/riscv/cpu-param.h b/target/riscv/cpu-param.h
> index ebaf26d26d..a1e379beb7 100644
> --- a/target/riscv/cpu-param.h
> +++ b/target/riscv/cpu-param.h
> @@ -25,6 +25,7 @@
>    *  - M mode 0b011
>    *  - U mode HLV/HLVX/HSV 0b100
>    *  - S mode HLV/HLVX/HSV 0b101
> + *  - BCFI shadow stack   0b110
>    *  - M mode HLV/HLVX/HSV 0b111
>    */
>   #define NB_MMU_MODES 8
> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> index 6b4e90eb91..14cfb93288 100644
> --- a/target/riscv/cpu.c
> +++ b/target/riscv/cpu.c
> @@ -584,6 +584,8 @@ static void riscv_cpu_reset_hold(Object *obj)
>       }
>       /* mmte is supposed to have pm.current hardwired to 1 */
>       env->mmte |= (PM_EXT_INITIAL | MMTE_M_PM_CURRENT);
> +    /* Initialize ss_priv to current priv. */
> +    env->ss_priv = env->priv;
>   #endif
>       env->xl = riscv_cpu_mxl(env);
>       riscv_cpu_update_mask(env);
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index d14ea4f91d..8803ea6426 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -379,6 +379,7 @@ struct CPUArchState {
>       uint64_t sstateen[SMSTATEEN_MAX_COUNT];
>       target_ulong senvcfg;
>       uint64_t henvcfg;
> +    target_ulong ss_priv;
>   #endif
>       target_ulong cur_pmmask;
>       target_ulong cur_pmbase;
> @@ -617,6 +618,8 @@ void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong);
>   #define TB_FLAGS_PRIV_HYP_ACCESS_MASK   (1 << 2)
>   #define TB_FLAGS_MSTATUS_FS MSTATUS_FS
>   #define TB_FLAGS_MSTATUS_VS MSTATUS_VS
> +/* TLB MMU index for shadow stack accesses */
> +#define MMU_IDX_SS_ACCESS    6
>   
>   #include "exec/cpu-all.h"
>   
> diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> index fc188683c9..63377abc2f 100644
> --- a/target/riscv/cpu_helper.c
> +++ b/target/riscv/cpu_helper.c
> @@ -657,7 +657,8 @@ void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable)
>   
>   bool riscv_cpu_two_stage_lookup(int mmu_idx)
>   {
> -    return mmu_idx & TB_FLAGS_PRIV_HYP_ACCESS_MASK;
> +    return (mmu_idx & TB_FLAGS_PRIV_HYP_ACCESS_MASK) &&
> +           (mmu_idx != MMU_IDX_SS_ACCESS);
>   }
>   
>   int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts)
> @@ -745,6 +746,38 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv)
>        * preemptive context switch. As a result, do both.
>        */
>       env->load_res = -1;
> +
> +    if (cpu_get_bcfien(env) && (env->priv != env->ss_priv)) {
> +        /*
> +         * If backward CFI is enabled in the new privilege state, the
> +         * shadow stack TLB needs to be flushed - unless the most recent
> +         * use of the SS TLB was for the same privilege mode.
> +         */
> +        tlb_flush_by_mmuidx(env_cpu(env), 1 << MMU_IDX_SS_ACCESS);
> +        /*
> +         * Ignoring env->virt here since currently every time it flips,
> +         * all TLBs are flushed anyway.
> +         */
> +        env->ss_priv = env->priv;
> +    }
> +}
> +
> +typedef enum {
> +    SSTACK_NO,          /* Access is not for a shadow stack instruction */
> +    SSTACK_YES,         /* Access is for a shadow stack instruction */
> +    SSTACK_DC           /* Don't care about SS attribute in PMP */
> +} SStackPmpMode;
> +
> +static bool legal_sstack_access(int access_type, bool sstack_inst,
> +                                bool sstack_attribute)
> +{
> +    /*
> +     * Read/write/execution permissions are checked as usual. Shadow
> +     * stack enforcement is just that (1) instruction type must match
> +     * the attribute unless (2) a non-SS load to an SS region.
> +     */
> +    return (sstack_inst == sstack_attribute) ||
> +        ((access_type == MMU_DATA_LOAD) && sstack_attribute);
>   }
>   
>   /*
> @@ -764,7 +797,7 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv)
>   static int get_physical_address_pmp(CPURISCVState *env, int *prot,
>                                       target_ulong *tlb_size, hwaddr addr,
>                                       int size, MMUAccessType access_type,
> -                                    int mode)
> +                                    int mode, SStackPmpMode sstack)
Why this parameter if you don't use it?
>   {
>       pmp_priv_t pmp_priv;
>       int pmp_index = -1;
> @@ -812,13 +845,16 @@ static int get_physical_address_pmp(CPURISCVState *env, int *prot,
>    *               Second stage is used for hypervisor guest translation
>    * @two_stage: Are we going to perform two stage translation
>    * @is_debug: Is this access from a debugger or the monitor?
> + * @sstack: Is this access for a shadow stack? Passed by reference so
> +            it can be forced to SSTACK_DC when the SS check is completed
> +            based on a PTE - so the PMP SS attribute will be ignored.
>    */
>   static int get_physical_address(CPURISCVState *env, hwaddr *physical,
>                                   int *prot, target_ulong addr,
>                                   target_ulong *fault_pte_addr,
>                                   int access_type, int mmu_idx,
>                                   bool first_stage, bool two_stage,
> -                                bool is_debug)
> +                                bool is_debug, SStackPmpMode *sstack)
>   {
>       /* NOTE: the env->pc value visible here will not be
>        * correct, but the value visible to the exception handler
> @@ -830,6 +866,7 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical,
>       hwaddr ppn;
>       RISCVCPU *cpu = env_archcpu(env);
>       int napot_bits = 0;
> +    bool is_sstack = (sstack != NULL) && (*sstack == SSTACK_YES);
>       target_ulong napot_mask;
>   
>       /*
> @@ -851,6 +888,8 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical,
>           if (get_field(env->mstatus, MSTATUS_MPRV)) {
>               mode = get_field(env->mstatus, MSTATUS_MPP);
>           }
> +    } else if (mmu_idx == MMU_IDX_SS_ACCESS) {
> +        mode = env->priv;
>       }
>   
>       if (first_stage == false) {
> @@ -966,7 +1005,7 @@ restart:
>               int vbase_ret = get_physical_address(env, &vbase, &vbase_prot,
>                                                    base, NULL, MMU_DATA_LOAD,
>                                                    mmu_idx, false, true,
> -                                                 is_debug);
> +                                                 is_debug, NULL);
>   
>               if (vbase_ret != TRANSLATE_SUCCESS) {
>                   if (fault_pte_addr) {
> @@ -983,7 +1022,7 @@ restart:
>           int pmp_prot;
>           int pmp_ret = get_physical_address_pmp(env, &pmp_prot, NULL, pte_addr,
>                                                  sizeof(target_ulong),
> -                                               MMU_DATA_LOAD, PRV_S);
> +                                               MMU_DATA_LOAD, PRV_S, SSTACK_NO);
>           if (pmp_ret != TRANSLATE_SUCCESS) {
>               return TRANSLATE_PMP_FAIL;
>           }
> @@ -1010,6 +1049,18 @@ restart:
>               }
>           }
>   
> +        /*
> +         * When backward CFI is enabled, the R=0, W=1, X=0 reserved encoding
> +         * is used to mark Shadow Stack (SS) pages. If back CFI enabled, allow
> +         * normal loads on SS pages, regular stores raise store access fault
> +         * and avoid hitting the reserved-encoding case. Only shadow stack
> +         * stores are allowed on SS pages. Shadow stack loads and stores on
> +         * regular memory (non-SS) raise load and store/AMO access fault.
> +         * Second stage translations don't participate in Shadow Stack.
> +         */
> +        bool sstack_page = (cpu_get_bcfien(env) && first_stage &&
> +                            ((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W));
> +
>           if (!(pte & PTE_V)) {
>               /* Invalid PTE */
>               return TRANSLATE_FAIL;
> @@ -1021,7 +1072,7 @@ restart:
>                   return TRANSLATE_FAIL;
>               }
>               base = ppn << PGSHIFT;
> -        } else if ((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W) {
> +        } else if (((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W) && !sstack_page) {
>               /* Reserved leaf PTE flags: PTE_W */
>               return TRANSLATE_FAIL;
>           } else if ((pte & (PTE_R | PTE_W | PTE_X)) == (PTE_W | PTE_X)) {
> @@ -1038,16 +1089,21 @@ restart:
>           } else if (ppn & ((1ULL << ptshift) - 1)) {
>               /* Misaligned PPN */
>               return TRANSLATE_FAIL;
> -        } else if (access_type == MMU_DATA_LOAD && !((pte & PTE_R) ||
> -                   ((pte & PTE_X) && mxr))) {
> +        } else if (access_type == MMU_DATA_LOAD && !(((pte & PTE_R) ||
> +                   sstack_page) || ((pte & PTE_X) && mxr))) {
>               /* Read access check failed */
>               return TRANSLATE_FAIL;
> -        } else if (access_type == MMU_DATA_STORE && !(pte & PTE_W)) {
> +        } else if ((access_type == MMU_DATA_STORE && !is_sstack) &&
> +                   !(pte & PTE_W)) {
Why limit to !is_sstack? Even is_sstack, we should make sure

(access_type == MMU_DATA_STORE && !(pte & PTE_W)

fails.
>               /* Write access check failed */
>               return TRANSLATE_FAIL;
>           } else if (access_type == MMU_INST_FETCH && !(pte & PTE_X)) {
>               /* Fetch access check failed */
>               return TRANSLATE_FAIL;
> +        } else if (!legal_sstack_access(access_type, is_sstack,
> +                                        sstack_page)) {
> +            /* Illegal combo of instruction type and page attribute */
> +            return TRANSLATE_PMP_FAIL;
Not sure about this. Does the cfi escape the pmp check?
>           } else {
>               /* if necessary, set accessed and dirty bits. */
>               target_ulong updated_pte = pte | PTE_A |
> @@ -1107,18 +1163,27 @@ restart:
>                            ) << PGSHIFT) | (addr & ~TARGET_PAGE_MASK);
>   
>               /* set permissions on the TLB entry */
> -            if ((pte & PTE_R) || ((pte & PTE_X) && mxr)) {
> +            if ((pte & PTE_R) || ((pte & PTE_X) && mxr) || sstack_page) {

I see that we should add the PAGE_READ for sstack_page, such as for a 
no-SS load.

Zhiwei

>                   *prot |= PAGE_READ;
>               }
>               if ((pte & PTE_X)) {
>                   *prot |= PAGE_EXEC;
>               }
> -            /* add write permission on stores or if the page is already dirty,
> -               so that we TLB miss on later writes to update the dirty bit */
> +            /*
> +             * add write permission on stores or if the page is already dirty,
> +             * so that we TLB miss on later writes to update the dirty bit
> +             */
>               if ((pte & PTE_W) &&
>                       (access_type == MMU_DATA_STORE || (pte & PTE_D))) {
>                   *prot |= PAGE_WRITE;
>               }
> +            if (sstack) {
> +                /*
> +                 * Tell the caller to skip the SS bit in the PMP since we
> +                 * resolved the attributes via the page table.
> +                 */
> +                *sstack = SSTACK_DC;
> +            }
>               return TRANSLATE_SUCCESS;
>           }
>       }
> @@ -1190,13 +1255,13 @@ hwaddr riscv_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
>       int mmu_idx = cpu_mmu_index(&cpu->env, false);
>   
>       if (get_physical_address(env, &phys_addr, &prot, addr, NULL, 0, mmu_idx,
> -                             true, riscv_cpu_virt_enabled(env), true)) {
> +                             true, riscv_cpu_virt_enabled(env), true, NULL)) {
>           return -1;
>       }
>   
>       if (riscv_cpu_virt_enabled(env)) {
>           if (get_physical_address(env, &phys_addr, &prot, phys_addr, NULL,
> -                                 0, mmu_idx, false, true, true)) {
> +                                 0, mmu_idx, false, true, true, NULL)) {
>               return -1;
>           }
>       }
> @@ -1291,6 +1356,8 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
>       bool two_stage_indirect_error = false;
>       int ret = TRANSLATE_FAIL;
>       int mode = mmu_idx;
> +    bool sstack = (mmu_idx == MMU_IDX_SS_ACCESS);
> +    SStackPmpMode ssmode = sstack ? SSTACK_YES : SSTACK_NO;
>       /* default TLB page size */
>       target_ulong tlb_size = TARGET_PAGE_SIZE;
>   
> @@ -1318,7 +1385,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
>           /* Two stage lookup */
>           ret = get_physical_address(env, &pa, &prot, address,
>                                      &env->guest_phys_fault_addr, access_type,
> -                                   mmu_idx, true, true, false);
> +                                   mmu_idx, true, true, false, &ssmode);
>   
>           /*
>            * A G-stage exception may be triggered during two state lookup.
> @@ -1342,7 +1409,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
>   
>               ret = get_physical_address(env, &pa, &prot2, im_address, NULL,
>                                          access_type, mmu_idx, false, true,
> -                                       false);
> +                                       false, NULL);
>   
>               qemu_log_mask(CPU_LOG_MMU,
>                       "%s 2nd-stage address=%" VADDR_PRIx " ret %d physical "
> @@ -1353,7 +1420,8 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
>   
>               if (ret == TRANSLATE_SUCCESS) {
>                   ret = get_physical_address_pmp(env, &prot_pmp, &tlb_size, pa,
> -                                               size, access_type, mode);
> +                                               size, access_type, mode,
> +                                               SSTACK_NO);
>   
>                   qemu_log_mask(CPU_LOG_MMU,
>                                 "%s PMP address=" HWADDR_FMT_plx " ret %d prot"
> @@ -1377,7 +1445,8 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
>       } else {
>           /* Single stage lookup */
>           ret = get_physical_address(env, &pa, &prot, address, NULL,
> -                                   access_type, mmu_idx, true, false, false);
> +                                   access_type, mmu_idx, true, false,
> +                                   false, &ssmode);
>   
>           qemu_log_mask(CPU_LOG_MMU,
>                         "%s address=%" VADDR_PRIx " ret %d physical "
> @@ -1386,7 +1455,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
>   
>           if (ret == TRANSLATE_SUCCESS) {
>               ret = get_physical_address_pmp(env, &prot_pmp, &tlb_size, pa,
> -                                           size, access_type, mode);
> +                                           size, access_type, mode, ssmode);
>   
>               qemu_log_mask(CPU_LOG_MMU,
>                             "%s PMP address=" HWADDR_FMT_plx " ret %d prot"


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v1 RFC Zisslpcfi 7/9] target/riscv: Tracking indirect branches (fcfi) using TCG
  2023-02-09  6:24 ` [PATCH v1 RFC Zisslpcfi 7/9] target/riscv: Tracking indirect branches (fcfi) using TCG Deepak Gupta
@ 2023-02-15  8:55   ` LIU Zhiwei
  2023-02-16  0:02     ` Deepak Gupta
  2023-02-16  6:05   ` Richard Henderson
  1 sibling, 1 reply; 42+ messages in thread
From: LIU Zhiwei @ 2023-02-15  8:55 UTC (permalink / raw)
  To: Deepak Gupta, Palmer Dabbelt, Alistair Francis, Bin Meng,
	liweiwei, dbarboza
  Cc: Kip Walker, qemu-riscv, qemu-devel


On 2023/2/9 14:24, Deepak Gupta wrote:
> zisslpcfi protects forward control flow (if enabled) by enforcing all
> indirect call and jmp must land on a landing pad instruction `lpcll`
> short for landing pad and check lower label value. If target of an
> indirect call or jmp is not `lpcll` then cpu/hart must raise an illegal
> instruction exception.
>
> This patch implements the mechanism using TCG. Target architecture branch
> instruction must define the end of a TB. Using this property, during
> translation of branch instruction, TB flag = FCFI_LP_EXPECTED can be set.
> Translation of target TB can check if FCFI_LP_EXPECTED flag is set and a
> flag (fcfi_lp_expected) can be set in DisasContext. If `lpcll` gets
> translated, fcfi_lp_expected flag in DisasContext can be cleared. Else
> it'll fault.
>
> This patch also also adds flag for forward and backward cfi in
> DisasContext.
>
> Signed-off-by: Deepak Gupta <debug@rivosinc.com>
> Signed-off-by: Kip Walker  <kip@rivosinc.com>
> ---
>   target/riscv/cpu.h        |  3 +++
>   target/riscv/cpu_helper.c | 12 +++++++++
>   target/riscv/translate.c  | 52 +++++++++++++++++++++++++++++++++++++++
>   3 files changed, 67 insertions(+)
>
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index 8803ea6426..98b272bcad 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -644,6 +644,9 @@ FIELD(TB_FLAGS, VMA, 25, 1)
>   /* Native debug itrigger */
>   FIELD(TB_FLAGS, ITRIGGER, 26, 1)
>   
> +/* Zisslpcfi needs a TB flag to track indirect branches */
> +FIELD(TB_FLAGS, FCFI_LP_EXPECTED, 27, 1)
> +
>   #ifdef TARGET_RISCV32
>   #define riscv_cpu_mxl(env)  ((void)(env), MXL_RV32)
>   #else
> diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> index 63377abc2f..d15918f534 100644
> --- a/target/riscv/cpu_helper.c
> +++ b/target/riscv/cpu_helper.c
> @@ -129,6 +129,18 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc,
>           flags = FIELD_DP32(flags, TB_FLAGS, VILL, 1);
>       }
>   
> +    if (cpu->cfg.ext_cfi) {
> +        /*
> +         * For Forward CFI, only the expectation of a lpcll at
> +         * the start of the block is tracked (which can only happen
> +         * when FCFI is enabled for the current processor mode). A jump
> +         * or call at the end of the previous TB will have updated
> +         * env->elp to indicate the expectation.
> +         */
> +        flags = FIELD_DP32(flags, TB_FLAGS, FCFI_LP_EXPECTED,
> +                           env->elp != NO_LP_EXPECTED);
> +    }
> +
>   #ifdef CONFIG_USER_ONLY
>       flags |= TB_FLAGS_MSTATUS_FS;
>       flags |= TB_FLAGS_MSTATUS_VS;
> diff --git a/target/riscv/translate.c b/target/riscv/translate.c
> index df38db7553..7d43d20fc3 100644
> --- a/target/riscv/translate.c
> +++ b/target/riscv/translate.c
> @@ -41,6 +41,7 @@ static TCGv load_val;
>   /* globals for PM CSRs */
>   static TCGv pm_mask;
>   static TCGv pm_base;
> +static TCGOp *cfi_lp_check;
>   
>   #include "exec/gen-icount.h"
>   
> @@ -116,6 +117,10 @@ typedef struct DisasContext {
>       bool itrigger;
>       /* TCG of the current insn_start */
>       TCGOp *insn_start;
> +    /* CFI extension */
> +    bool bcfi_enabled;
> +    bool fcfi_enabled;
> +    bool fcfi_lp_expected;
>   } DisasContext;
>   
>   static inline bool has_ext(DisasContext *ctx, uint32_t ext)
> @@ -1166,11 +1171,44 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
>       ctx->pm_mask_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_MASK_ENABLED);
>       ctx->pm_base_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_BASE_ENABLED);
>       ctx->itrigger = FIELD_EX32(tb_flags, TB_FLAGS, ITRIGGER);
> +    ctx->bcfi_enabled = cpu_get_bcfien(env);
> +    ctx->fcfi_enabled = cpu_get_fcfien(env);
This is wrong.  If you ctx->bcfi_enabled in the translation and don't 
put it in a tb flags field, the translated tb will
be misused.


Zhiwei

> +    ctx->fcfi_lp_expected = FIELD_EX32(tb_flags, TB_FLAGS, FCFI_LP_EXPECTED);
>       ctx->zero = tcg_constant_tl(0);
>   }
>   
>   static void riscv_tr_tb_start(DisasContextBase *db, CPUState *cpu)
>   {
> +    DisasContext *ctx = container_of(db, DisasContext, base);
> +
> +    if (ctx->fcfi_lp_expected) {
> +        /*
> +         * Since we can't look ahead to confirm that the first
> +         * instruction is a legal landing pad instruction, emit
> +         * compare-and-branch sequence that will be fixed-up in
> +         * riscv_tr_tb_stop() to either statically hit or skip an
> +         * illegal instruction exception depending on whether the
> +         * flag was lowered by translation of a CJLP or JLP as
> +         * the first instruction in the block.
> +         */
> +        TCGv_i32 immediate;
> +        TCGLabel *l;
> +        l = gen_new_label();
> +        immediate = tcg_temp_local_new_i32();
> +        tcg_gen_movi_i32(immediate, 0);
> +        cfi_lp_check = tcg_last_op();
> +        tcg_gen_brcondi_i32(TCG_COND_EQ, immediate, 0, l);
> +        tcg_temp_free_i32(immediate);
> +        gen_exception_illegal(ctx);
> +        gen_set_label(l);
> +        /*
> +         * Despite the use of gen_exception_illegal(), the rest of
> +         * the TB needs to be generated. The TCG optimizer will
> +         * clean things up depending on which path ends up being
> +         * active.
> +         */
> +        ctx->base.is_jmp = DISAS_NEXT;
> +    }
>   }
>   
>   static void riscv_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu)
> @@ -1225,6 +1263,7 @@ static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
>   static void riscv_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
>   {
>       DisasContext *ctx = container_of(dcbase, DisasContext, base);
> +    CPURISCVState *env = cpu->env_ptr;
>   
>       switch (ctx->base.is_jmp) {
>       case DISAS_TOO_MANY:
> @@ -1235,6 +1274,19 @@ static void riscv_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
>       default:
>           g_assert_not_reached();
>       }
> +
> +    if (ctx->fcfi_lp_expected) {
> +        /*
> +         * If the "lp expected" flag is still up, the block needs to take an
> +         * illegal instruction exception.
> +         */
> +        tcg_set_insn_param(cfi_lp_check, 1, tcgv_i32_arg(tcg_constant_i32(1)));
> +    } else {
> +        /*
> +        * LP instruction requirement was met, clear up LP expected
> +        */
> +        env->elp = NO_LP_EXPECTED;
> +    }
>   }
>   
>   static void riscv_tr_disas_log(const DisasContextBase *dcbase,


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v1 RFC Zisslpcfi 8/9] target/riscv: Instructions encodings, implementation and handlers
  2023-02-09  6:24 ` [PATCH v1 RFC Zisslpcfi 8/9] target/riscv: Instructions encodings, implementation and handlers Deepak Gupta
@ 2023-02-15 10:43   ` LIU Zhiwei
  0 siblings, 0 replies; 42+ messages in thread
From: LIU Zhiwei @ 2023-02-15 10:43 UTC (permalink / raw)
  To: Deepak Gupta, Palmer Dabbelt, Alistair Francis, Bin Meng,
	liweiwei, dbarboza
  Cc: Kip Walker, qemu-riscv, qemu-devel


On 2023/2/9 14:24, Deepak Gupta wrote:
> This patch implements instruction encodings for zisslpcfi instructions.
> Additionally this patch implements zimops encodings as well. If Zisslpcfi
> is supported by CPU but not enabled then all Zisslpcfi instructions
> default to Zimops instuction behavior i.e. mov 0 to rd.
>
> zisslpcfi defines following instructions.
> - Backward control flow
>      - sspush x1/x5 : Decrement shadow stack pointer and pushes x1 or x5
>                       on shadow stack.
>      - sspop x1/x5 : Pops from shadow stack into x1 or x5. Increments
>                      shadow stack pointer.
>      - ssprr : Reads current shadow stack pointer into a destination
>                register.
>      - sscheckra : Compares x1 with x5. Raises illegal instr fault if
>                  x1 != x5
>      - ssamoswap : Atomically swaps value on top of shadow stack
>
> - Forward control flow
>      - lpsll, lpsml, lpsul : sets lower (9bit), mid (8bit) and upper
>                              (8bit) label values in CSR_ULLP respectively.
>      - lpcll, lpcml, lpcul : checks lower (9bit), mid (8bit) and upper
>                              (8bit) label values with CSR_ULLP
>                              respectively.
>      Check label instructions raise illegal instruction fault when labels
>      mismatch.
>
> Signed-off-by: Deepak Gupta <debug@rivosinc.com>
> Signed-off-by: Kip Walker  <kip@rivosinc.com>
> ---
>   target/riscv/cpu_bits.h                       |  10 +
>   target/riscv/helper.h                         |   7 +
>   target/riscv/insn32.decode                    |  29 ++
>   target/riscv/insn_trans/trans_rvi.c.inc       |  14 +
>   target/riscv/insn_trans/trans_zimops.c.inc    |  53 +++
>   target/riscv/insn_trans/trans_zisslpcfi.c.inc | 310 ++++++++++++++++++
>   target/riscv/op_helper.c                      |  67 ++++
>   target/riscv/translate.c                      |   2 +
>   8 files changed, 492 insertions(+)
>   create mode 100644 target/riscv/insn_trans/trans_zimops.c.inc
>   create mode 100644 target/riscv/insn_trans/trans_zisslpcfi.c.inc
>
> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
> index 37100ec8f6..b2d527c626 100644
> --- a/target/riscv/cpu_bits.h
> +++ b/target/riscv/cpu_bits.h
> @@ -600,6 +600,16 @@ typedef enum {
>       LP_EXPECTED = 1,
>   } cfi_elp;
>   
> +#define LPLR_UL            (((1 << 8) - 1) << 17)
> +#define LPLR_ML            (((1 << 8) - 1) << 9)
> +#define LPLR_LL            ((1 << 9) - 1)
> +
> +typedef enum {
> +    FCFI_LPLL = 0,
> +    FCFI_ML = 1,
> +    FCFI_UL = 2,
> +} cfi_label_inst;
> +
>   /* hstatus CSR bits */
>   #define HSTATUS_VSBE         0x00000020
>   #define HSTATUS_GVA          0x00000040
> diff --git a/target/riscv/helper.h b/target/riscv/helper.h
> index 227c7122ef..6484415612 100644
> --- a/target/riscv/helper.h
> +++ b/target/riscv/helper.h
> @@ -97,6 +97,11 @@ DEF_HELPER_FLAGS_2(fcvt_h_l, TCG_CALL_NO_RWG, i64, env, tl)
>   DEF_HELPER_FLAGS_2(fcvt_h_lu, TCG_CALL_NO_RWG, i64, env, tl)
>   DEF_HELPER_FLAGS_2(fclass_h, TCG_CALL_NO_RWG_SE, tl, env, i64)
>   
> +/* Forward CFI label checking */
> +DEF_HELPER_2(cfi_jalr, void, env, int)
> +DEF_HELPER_3(cfi_check_landing_pad, void, env, int, int)
> +DEF_HELPER_3(cfi_set_landing_pad, void, env, int, int)
> +
>   /* Special functions */
>   DEF_HELPER_2(csrr, tl, env, int)
>   DEF_HELPER_3(csrw, void, env, int, tl)
> @@ -112,6 +117,8 @@ DEF_HELPER_1(tlb_flush, void, env)
>   /* Native Debug */
>   DEF_HELPER_1(itrigger_match, void, env)
>   #endif
> +/* helper for back cfi mismatch */
> +DEF_HELPER_1(sschkra_mismatch, void, env)
>   
>   /* Hypervisor functions */
>   #ifndef CONFIG_USER_ONLY
> diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode
> index b7e7613ea2..cd734f03ae 100644
> --- a/target/riscv/insn32.decode
> +++ b/target/riscv/insn32.decode
> @@ -37,6 +37,8 @@
>   %imm_u    12:s20                 !function=ex_shift_12
>   %imm_bs   30:2                   !function=ex_shift_3
>   %imm_rnum 20:4
> +%imm_cfi9  15:9
> +%imm_cfi8  15:8
>   
>   # Argument sets:
>   &empty
> @@ -163,6 +165,33 @@ csrrwi   ............     ..... 101 ..... 1110011 @csr
>   csrrsi   ............     ..... 110 ..... 1110011 @csr
>   csrrci   ............     ..... 111 ..... 1110011 @csr
>   
> +# zimops (unpriv integer may be operations) instructions with system opcode
> +# These're superset of for cfi encodings. zimops_r and zimops_rr should be last
> +# entry in below overlapping patterns so that it acts as final sink for overlapping patterns.
> +# Any new encoding that can be used should be placed above mop.r and mop.rr
> +
> +# cfi instructions carved out of mop.r
> +{
> +  sspush    100000 0 11100     ..... 100 00000 1110011 %rs1
> +  sspop     100000 0 11100     00000 100 ..... 1110011 %rd
> +  ssprr     100000 0 11101     00000 100 ..... 1110011 %rd
> +  zimops_r  1-00-- 0 111--     ----- 100 ..... 1110011 %rd
> +}
> +
> +# cfi instructions carved out of mop.rr
> +{
> +  sschckra  100010 1 00001     00101 100 00000 1110011
> +  ssamoswap 100000 1 .....     ..... 100 ..... 1110011 @r
> +
> +  lpsll     100000 1 0 .........    100 00000 1110011 %imm_cfi9
> +  lpcll     100000 1 1 .........    100 00000 1110011 %imm_cfi9
> +  lpsml     100001 1 0 0........    100 00000 1110011 %imm_cfi8
> +  lpcml     100001 1 0 1........    100 00000 1110011 %imm_cfi8
> +  lpsul     100010 1 1 0........    100 00000 1110011 %imm_cfi8
> +  lpcul     100010 1 1 1........    100 00000 1110011 %imm_cfi8
> +  zimops_rr 1-00-- 1 - ---------    100 ..... 1110011 %rd
> +}
> +
>   # *** RV64I Base Instruction Set (in addition to RV32I) ***
>   lwu      ............   ..... 110 ..... 0000011 @i
>   ld       ............   ..... 011 ..... 0000011 @i
> diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc
> index 5c69b88d1e..4a4f1ca778 100644
> --- a/target/riscv/insn_trans/trans_rvi.c.inc
> +++ b/target/riscv/insn_trans/trans_rvi.c.inc
> @@ -66,6 +66,20 @@ static bool trans_jalr(DisasContext *ctx, arg_jalr *a)
>       }
>   
>       gen_set_gpri(ctx, a->rd, ctx->pc_succ_insn);
> +
> +    if (ctx->cfg_ptr->ext_cfi) {
> +        /*
> +         * Rely on a helper to check the forward CFI enable for the
> +         * current process mode. The alternatives would be (1) include
> +         * "fcfi enabled" in the cflags or (2) maintain a "fcfi
> +         * currently enabled" in cpu_env and emit TCG code to access
> +         * and test it.
> +         */
> +        if (a->rd == xRA || a->rd == xT0 || (a->rs1 != xRA && a->rs1 != xT0)) {
> +            gen_helper_cfi_jalr(cpu_env, tcg_constant_i32(LP_EXPECTED));
> +        }
> +    }
> +
>       lookup_and_goto_ptr(ctx);
>   
>       if (misaligned) {
> diff --git a/target/riscv/insn_trans/trans_zimops.c.inc b/target/riscv/insn_trans/trans_zimops.c.inc
> new file mode 100644
> index 0000000000..51748637b9
> --- /dev/null
> +++ b/target/riscv/insn_trans/trans_zimops.c.inc
> @@ -0,0 +1,53 @@
> +/*
> + * RISC-V translation routines for the Control-Flow Integrity Extension
> + *
> + * Copyright (c) 2022-2023 Rivos Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> + static bool trans_zimops_r(DisasContext *ctx, arg_zimops_r * a)
> + {
> +      /* zimops not implemented, raise illegal instruction & return true */
> +      if (!ctx->cfg_ptr->ext_zimops) {
> +            gen_exception_illegal(ctx);
> +            return true;
> +      }
> +
> +      /*
> +       * zimops implemented, simply grab destination and mov zero.
> +       * return true
> +       */
> +      TCGv dest = dest_gpr(ctx, a->rd);
> +      dest = tcg_const_tl(0);
> +      gen_set_gpr(ctx, a->rd, dest);
These three statements can be reduced to gen_set_gpr(ctx, a->rd, ctx->zero)
> +      return true;
> + }
> +
> + static bool trans_zimops_rr(DisasContext *ctx, arg_zimops_rr * a)
> + {
> +      /* zimops not implemented, raise illegal instruction & return true */
> +      if (!ctx->cfg_ptr->ext_zimops) {
> +            gen_exception_illegal(ctx);
> +            return true;
> +      }
> +
> +      /*
> +       * zimops implemented, simply grab destination and mov zero.
> +       * return true
> +       */
> +      TCGv dest = dest_gpr(ctx, a->rd);
> +      dest = tcg_const_tl(0);
> +      gen_set_gpr(ctx, a->rd, dest);
Same here.
> +      return true;
> + }
> diff --git a/target/riscv/insn_trans/trans_zisslpcfi.c.inc b/target/riscv/insn_trans/trans_zisslpcfi.c.inc
> new file mode 100644
> index 0000000000..fe27bb73f6
> --- /dev/null
> +++ b/target/riscv/insn_trans/trans_zisslpcfi.c.inc
> @@ -0,0 +1,310 @@
> +/*
> + * RISC-V translation routines for the Control-Flow Integrity Extension
> + *
> + * Copyright (c) 2022-2023 Rivos Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +static MemOp mxl_memop(DisasContext *ctx)
> +{
> +    switch (get_xl(ctx)) {
> +    case MXL_RV32:
> +        return MO_TEUL;
> +
> +    case MXL_RV64:
> +        return MO_TEUQ;
> +
> +    case MXL_RV128:
> +        return MO_TEUO;
> +
> +    default:
> +        g_assert_not_reached();
> +    }
> +}
> +
> +static bool trans_sspop(DisasContext *ctx, arg_sspop *a)
> +{
> +    /* cfi only supported on 32bit and 64bit */
> +    if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) {
> +        return false;
> +    }
> +
> +    /* back cfi not enabled, should go to trans_zimops. return false */
> +    if (!ctx->bcfi_enabled) {
> +        return false;
> +    }
> +
> +    /* sspop can only load into x1 or x5. Everything else defaults to zimops */
> +    if (a->rd != 1 && a->rd != 5) {
> +        return false;
> +    }
> +
rd can't be x0.
> +    /*
> +     * get data in TCGv using get_gpr
> +     * get addr in TCGv using gen_helper_csrr on CSR_SSP
> +     * use some tcg subtract arithmetic (subtract by XLEN) on addr
> +     * perform ss store on computed address
> +     */
> +
> +    TCGv addr = tcg_temp_new();
> +    int tmp = (get_xl(ctx) == MXL_RV64) ? 8 : 4;
> +    TCGv_i32 ssp_csr = tcg_constant_i32(CSR_SSP);
> +    TCGv data = get_gpr(ctx, a->rd, EXT_NONE);
> +    gen_helper_csrr(addr, cpu_env, ssp_csr);
tcg_gen_ld_tl can directly load env->ssp
> +    tcg_gen_qemu_ld_tl(data, addr, MMU_IDX_SS_ACCESS,
> +                       mxl_memop(ctx) | MO_ALIGN);
> +
> +    /*
> +     * add XLEN/bitwidth to addr, align to XLEN . How do i do that? Is below
> +     * the right way
> +     */
> +    tcg_gen_addi_tl(addr, addr, tmp);
> +    gen_set_gpr(ctx, a->rd, data);
> +    gen_helper_csrw(cpu_env, ssp_csr, addr);
tcg_gen_st_tl
> +

Leak  the addr tmp.

Zhiwei

> +    return true;
> +}
> +
> +static bool trans_sspush(DisasContext *ctx, arg_sspush *a)
> +{
> +    /* cfi only supported on 32bit and 64bit */
> +    if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) {
> +        return false;
> +    }
> +
> +    /* back cfi not enabled, should go to trans_zimops. return false */
> +    if (!ctx->bcfi_enabled) {
> +        return false;
> +    }
> +
> +    /*
> +     * sspush can only push from x1 or x5. Everything else defaults to zimops
> +     */
> +    if (a->rs1 != 1 && a->rs1 != 5) {
> +        return false;
> +    }
> +
rs1 != 0
> +    /*
> +     * get data in TCGv using get_gpr
> +     * get addr in TCGv using gen_helper_csrr on CSR_SSP
> +     * use some tcg subtract arithmetic (subtract by XLEN) on addr
> +     * perform ss store on computed address
> +     */
> +
> +    TCGv addr = tcg_temp_new();
> +    int tmp = (get_xl(ctx) == MXL_RV64) ? -8 : -4;
> +    TCGv_i32 ssp_csr = tcg_constant_i32(CSR_SSP);
> +    TCGv data = get_gpr(ctx, a->rs1, EXT_NONE);
> +    gen_helper_csrr(addr, cpu_env, ssp_csr);
> +
> +    /*
> +     * subtract XLEN from addr, align to XLEN . How do i do that? Is below the
> +     * right way
> +     */
> +    tcg_gen_addi_tl(addr, addr, tmp);
> +    tcg_gen_qemu_st_tl(data, addr, MMU_IDX_SS_ACCESS,
> +                       mxl_memop(ctx) | MO_ALIGN);
> +
> +    gen_helper_csrw(cpu_env, ssp_csr, addr);
> +
> +    return true;
> +}
> +
> +static bool trans_sschckra(DisasContext *ctx, arg_sschckra *a)
> +{
> +    /* cfi only supported on 32bit and 64bit */
> +    if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) {
> +        return false;
> +    }
> +
> +    /* back cfi not enabled, should go to trans_zimops. return false */
> +    if (!ctx->bcfi_enabled) {
> +        return false;
> +    }
> +
> +    gen_helper_sschkra_mismatch(cpu_env);
> +
> +    return true;
> +}
> +
> +static bool trans_ssprr(DisasContext *ctx, arg_ssprr *a)
> +{
> +    /* cfi only supported on 32bit and 64bit */
> +    if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) {
> +        return false;
> +    }
> +
> +    /* back cfi not enabled, should go to trans_zimops. return false */
> +    if (!ctx->bcfi_enabled) {
> +        return false;
> +    }
> +
> +    TCGv dest = get_gpr(ctx, a->rd, EXT_NONE);
> +    TCGv_i32 ssp_csr = tcg_constant_i32(CSR_SSP);
> +    gen_helper_csrr(dest, cpu_env, ssp_csr);
> +    gen_set_gpr(ctx, a->rd, dest);
> +
> +    return true;
> +}
> +
> +static bool trans_ssamoswap(DisasContext *ctx, arg_ssamoswap *a)
> +{
> +    /* cfi only supported on 32bit and 64bit */
> +    if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) {
> +        return false;
> +    }
> +
> +    /* back cfi not enabled, should go to trans_zimops. return false */
> +    if (!ctx->bcfi_enabled) {
> +        return false;
> +    }
> +
> +    /* If cfi is enabled then, then rd must be != 0 */
> +
> +    if (a->rd == 0) {
> +        return false;
> +    }
> +
> +    TCGv dest = dest_gpr(ctx, a->rd);
> +    TCGv src1 = get_address(ctx, a->rs1, 0);
> +    TCGv src2 = get_gpr(ctx, a->rs2, EXT_NONE);
> +    MemOp mop = (MO_ALIGN | ((get_xl(ctx) == MXL_RV32) ? MO_TESL : MO_TESQ));
> +
> +    tcg_gen_atomic_xchg_tl(dest, src1, src2, MMU_IDX_SS_ACCESS, mop);
> +    gen_set_gpr(ctx, a->rd, dest);
> +    return true;
> +}
> +
> +static bool trans_lpcll(DisasContext *ctx, arg_lpcll *a)
> +{
> +    /* cfi only supported on 32bit and 64bit */
> +    if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) {
> +        return false;
> +    }
> +
> +    /*
> +     * If this is the first instruction of the TB, let the translator
> +     * know the landing pad requirement was satisfied. No need to bother
> +     * checking for CFI feature or enablement.
> +     */
> +
> +    if (ctx->base.pc_next == ctx->base.pc_first) {
> +        ctx->fcfi_lp_expected = false;
> +        /* PC must be 4 byte aligned */
> +        if (ctx->fcfi_enabled && ((ctx->base.pc_next) & 0x3)) {
> +            /*
> +             * misaligned, according to spec we should raise illegal instead
> +             * of mis-aligned
> +             */
> +            gen_exception_illegal(ctx);
> +        }
> +    }
> +
> +    /* forward cfi not enabled, should go to trans_zimops. return false */
> +    if (!ctx->fcfi_enabled) {
> +        return false;
> +    }
> +
> +    gen_helper_cfi_check_landing_pad(cpu_env, tcg_constant_i32(a->imm_cfi9),
> +                               tcg_constant_i32(FCFI_LPLL));
> +    return true;
> +}
> +
> +static bool trans_lpcml(DisasContext *ctx, arg_lpcml *a)
> +{
> +    /* cfi only supported on 32bit and 64bit */
> +    if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) {
> +        return false;
> +    }
> +
> +    /* forward cfi not enabled, should go to trans_zimops. return false */
> +    if (!ctx->fcfi_enabled) {
> +        return false;
> +    }
> +
> +    gen_helper_cfi_check_landing_pad(cpu_env, tcg_constant_i32(a->imm_cfi8),
> +                               tcg_constant_i32(FCFI_ML));
> +    return true;
> +}
> +
> +static bool trans_lpcul(DisasContext *ctx, arg_lpcul *a)
> +{
> +    /* cfi only supported on 32bit and 64bit */
> +    if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) {
> +        return false;
> +    }
> +
> +    /* forward cfi not enabled, should go to trans_zimops. return false */
> +    if (!ctx->fcfi_enabled) {
> +        return false;
> +    }
> +
> +    gen_helper_cfi_check_landing_pad(cpu_env, tcg_constant_i32(a->imm_cfi8),
> +                               tcg_constant_i32(FCFI_UL));
> +    return true;
> +}
> +
> +static bool trans_lpsll(DisasContext *ctx, arg_lpsll *a)
> +{
> +    /* cfi only supported on 32bit and 64bit */
> +    if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) {
> +        return false;
> +    }
> +
> +    /* forward cfi not enabled, should go to trans_zimops. return false */
> +    if (!ctx->fcfi_enabled) {
> +        return false;
> +    }
> +
> +    gen_helper_cfi_set_landing_pad(cpu_env, tcg_constant_i32(a->imm_cfi9),
> +                                   tcg_constant_i32(FCFI_LPLL));
> +
> +    return true;
> +}
> +
> +static bool trans_lpsml(DisasContext *ctx, arg_lpsml *a)
> +{
> +    /* cfi only supported on 32bit and 64bit */
> +    if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) {
> +        return false;
> +    }
> +
> +    /* forward cfi not enabled, should go to trans_zimops. return false */
> +    if (!ctx->fcfi_enabled) {
> +        return false;
> +    }
> +
> +    gen_helper_cfi_set_landing_pad(cpu_env, tcg_constant_i32(a->imm_cfi8),
> +                                   tcg_constant_i32(FCFI_ML));
> +
> +    return true;
> +}
> +
> +static bool trans_lpsul(DisasContext *ctx, arg_lpsul *a)
> +{
> +    /* cfi only supported on 32bit and 64bit */
> +    if (get_xl(ctx) != MXL_RV32 && get_xl(ctx) != MXL_RV64) {
> +        return false;
> +    }
> +
> +    /* forward cfi not enabled, should go to trans_zimops. return false */
> +    if (!ctx->fcfi_enabled) {
> +        return false;
> +    }
> +
> +    gen_helper_cfi_set_landing_pad(cpu_env, tcg_constant_i32(a->imm_cfi8),
> +                                   tcg_constant_i32(FCFI_UL));
> +
> +    return true;
> +}
> diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
> index d15893aa82..c14b76aabb 100644
> --- a/target/riscv/op_helper.c
> +++ b/target/riscv/op_helper.c
> @@ -123,6 +123,73 @@ target_ulong helper_csrrw_i128(CPURISCVState *env, int csr,
>       return int128_getlo(rv);
>   }
>   
> +void helper_sschkra_mismatch(CPURISCVState *env)
> +{
> +    if (env->gpr[xRA] != env->gpr[xT0]) {
> +        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> +    }
> +}
> +
> +void helper_cfi_jalr(CPURISCVState *env, int elp)
> +{
> +    /*
> +     * The translation routine doesn't know if forward CFI is enabled
> +     * in the current processor mode or not. It's not worth burning a
> +     * cflags bit to encode this, or tracking the current-mode-fcfi
> +     * enable in a dedicated member of 'env'. Just come out to a helper
> +     * for jump/call on a core with CFI.
> +     */
> +    if (cpu_get_fcfien(env)) {
> +        env->elp = elp;
> +    }
> +}
> +
> +void helper_cfi_check_landing_pad(CPURISCVState *env, int lbl, int inst_type)
> +{
> +    if (cpu_get_fcfien(env)) {
> +        switch (inst_type) {
> +        case FCFI_LPLL:
> +            /*
> +             * Check for a lower label match. We already checked 4 byte
> +             * alignment in tcg
> +             */
> +            if (lbl != get_field(env->lplr, LPLR_LL)) {
> +                riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> +            }
> +            env->elp = NO_LP_EXPECTED;
> +            break;
> +        case FCFI_ML:
> +            if (lbl != get_field(env->lplr,  LPLR_ML)) {
> +                riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> +            }
> +            break;
> +        case FCFI_UL:
> +            if (lbl != get_field(env->lplr,  LPLR_UL)) {
> +                riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
> +            }
> +        }
> +    }
> +}
> +
> +void helper_cfi_set_landing_pad(CPURISCVState *env, int lbl, int inst_type)
> +{
> +    if (cpu_get_fcfien(env)) {
> +        switch (inst_type) {
> +        case FCFI_LPLL:
> +            /* setting lower label always clears up entire field */
> +            env->lplr = 0;
> +            env->lplr = set_field(env->lplr, LPLR_LL, lbl);
> +            break;
> +        case FCFI_ML:
> +            env->lplr = set_field(env->lplr, LPLR_ML, lbl);
> +            break;
> +        case FCFI_UL:
> +            env->lplr = set_field(env->lplr, LPLR_UL, lbl);
> +            break;
> +        }
> +    }
> +}
> +
>   #ifndef CONFIG_USER_ONLY
>   
>   target_ulong helper_sret(CPURISCVState *env)
> diff --git a/target/riscv/translate.c b/target/riscv/translate.c
> index 7d43d20fc3..b1a965d192 100644
> --- a/target/riscv/translate.c
> +++ b/target/riscv/translate.c
> @@ -1071,6 +1071,8 @@ static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc)
>   #include "insn_trans/trans_privileged.c.inc"
>   #include "insn_trans/trans_svinval.c.inc"
>   #include "insn_trans/trans_xventanacondops.c.inc"
> +#include "insn_trans/trans_zisslpcfi.c.inc"
> +#include "insn_trans/trans_zimops.c.inc"
>   
>   /* Include the auto-generated decoder for 16 bit insn */
>   #include "decode-insn16.c.inc"


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v1 RFC Zisslpcfi 2/9] target/riscv: zisslpcfi CSR, bit positions and other definitions
  2023-02-15  3:31   ` LIU Zhiwei
@ 2023-02-15 20:42     ` Deepak Gupta
  0 siblings, 0 replies; 42+ messages in thread
From: Deepak Gupta @ 2023-02-15 20:42 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, liweiwei, dbarboza,
	Kip Walker, qemu-riscv, qemu-devel

On Tue, Feb 14, 2023 at 7:31 PM LIU Zhiwei <zhiwei_liu@linux.alibaba.com> wrote:
>
>
> On 2023/2/9 14:23, Deepak Gupta wrote:
> > `zisslpcfi` extension adds two new CSRs. CSR_SSP and CSR_LPLR.
> > - CSR_SSP: This CSR holds shadow stack pointer for current privilege mode
> >             CSR_SSP is accessible in all modes. Each mode must establish
> >             it's own CSR_SSP.
> >
> > - CSR_LPLR: This CSR holds label value set at the callsite by compiler.
> >              On call target label check instructions are emitted by
> >              compiler which check label value against value present in
> >              CSR_LPRL.
> >
> > Enabling of `zisslpcfi` is controlled via menvcfg (for S/HS/VS/U/VU) and
> > henvcfg (for VS/VU) at bit position 60.
> >
> > Each mode has enable/disable bits for forward cfi. Backward cfi doesn't
> > have separate enable/disable bits for S and M mode. User forward cfi and
> > user backward cfi enable/disable bits are in mstatus/sstatus CSR.
> > Supervisor forward cfi enable/disable bit are in menvcfg and henvcfg CSR.
> > Machine mode forward cfi enable/disable bit is in mseccfg CSR.
> >
> > If forward cfi enabled, all indirect branches must land on a landing pad
> > instruction (`lpcll`, introduced in later commits). CPU/hart tracks this
> > internally using a landing pad tracker called `elp` short for `expecting
> > landing pad`. An interrupt can occur between an indirect branch and
> > target. If such an event occurs `elp` is saved away in mstatus/sstatus
> > CSR
> >
> > Signed-off-by: Deepak Gupta <debug@rivosinc.com>
> > Signed-off-by: Kip Walker  <kip@rivosinc.com>
> > ---
> >   target/riscv/cpu.h      |  5 +++++
> >   target/riscv/cpu_bits.h | 25 +++++++++++++++++++++++++
> >   target/riscv/pmp.h      |  3 ++-
> >   3 files changed, 32 insertions(+), 1 deletion(-)
> >
> > diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> > index 9a923760b2..18db61a06a 100644
> > --- a/target/riscv/cpu.h
> > +++ b/target/riscv/cpu.h
> > @@ -181,6 +181,11 @@ struct CPUArchState {
> >
> >       uint32_t features;
> >
> > +    /* CFI Extension user mode registers and state */
> > +    uint32_t     lplr;
> > +    target_ulong ssp;
> > +    cfi_elp      elp;
>
> I think you are coding according to the sections of the specification.

Yes, pretty much.

> However,  when upstream code,
> don't add declaration or definition if you don't use it in this patch.
>
> This patch should be split into patches where use these definitions.

Noted.
>
> > +
> >   #ifdef CONFIG_USER_ONLY
> >       uint32_t elf_flags;
> >   #endif
> > diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
> > index 8b0d7e20ea..1663ba5775 100644
> > --- a/target/riscv/cpu_bits.h
> > +++ b/target/riscv/cpu_bits.h
> > @@ -39,6 +39,10 @@
> >
> >   /* Control and Status Registers */
> >
> > +/* CFI CSRs */
> > +#define CSR_LPLR            0x006
> I didn't see the CSR encoding  number from the link in cover-letter.

Yes, allocation is still in process. I'll ask ved (primary spec
author) to update it.

> > +#define CSR_SSP             0x020
> > +
> >   /* User Trap Setup */
> >   #define CSR_USTATUS         0x000
> >   #define CSR_UIE             0x004
> > @@ -542,6 +546,10 @@
> >   #define MSTATUS_TVM         0x00100000 /* since: priv-1.10 */
> >   #define MSTATUS_TW          0x00200000 /* since: priv-1.10 */
> >   #define MSTATUS_TSR         0x00400000 /* since: priv-1.10 */
> > +#define MSTATUS_UFCFIEN     0x00800000 /* Zisslpcfi-0.1 */
> > +#define MSTATUS_UBCFIEN     0x01000000 /* Zisslpcfi-0.1 */
> > +#define MSTATUS_SPELP       0x02000000 /* Zisslpcfi-0.1 */
> > +#define MSTATUS_MPELP       0x04000000 /* Zisslpcfi-0.1 */
> >   #define MSTATUS_GVA         0x4000000000ULL
> >   #define MSTATUS_MPV         0x8000000000ULL
> >
> > @@ -572,12 +580,21 @@ typedef enum {
> >   #define SSTATUS_XS          0x00018000
> >   #define SSTATUS_SUM         0x00040000 /* since: priv-1.10 */
> >   #define SSTATUS_MXR         0x00080000
> > +#define SSTATUS_UFCFIEN     MSTATUS_UFCFIEN /* Zisslpcfi-0.1 */
> > +#define SSTATUS_UBCFIEN     MSTATUS_UBCFIEN /* Zisslpcfi-0.1 */
> > +#define SSTATUS_SPELP       MSTATUS_SPELP   /* Zisslpcfi-0.1 */
> >
> >   #define SSTATUS64_UXL       0x0000000300000000ULL
> >
> >   #define SSTATUS32_SD        0x80000000
> >   #define SSTATUS64_SD        0x8000000000000000ULL
> >
> > +#define CFISTATUS_M_MASK    (MSTATUS_UFCFIEN | MSTATUS_UBCFIEN | \
> > +                             MSTATUS_MPELP | MSTATUS_SPELP)
> > +
> > +#define CFISTATUS_S_MASK    (SSTATUS_UFCFIEN | SSTATUS_UBCFIEN | \
> > +                             SSTATUS_SPELP)
> Why not the VSSTATUS?

It's the same mask for VSSTATUS, so pretty much using the same.

> > +
> >   /* hstatus CSR bits */
> >   #define HSTATUS_VSBE         0x00000020
> >   #define HSTATUS_GVA          0x00000040
> > @@ -747,10 +764,14 @@ typedef enum RISCVException {
> >   #define MENVCFG_CBIE                       (3UL << 4)
> >   #define MENVCFG_CBCFE                      BIT(6)
> >   #define MENVCFG_CBZE                       BIT(7)
> > +#define MENVCFG_SFCFIEN                    BIT(59)
> > +#define MENVCFG_CFI                        BIT(60)
>
> MENVCFG_CFIE according to the specification.  The definitions in other
> places  should also use X_CFIE.

Yes, it's a recent change in spec. I missed picking it up on impl.

>
> The same comment here with Weiwei, or you can use BIT_ULL.
>

Noted.

> Zhiwei
>
> >   #define MENVCFG_PBMTE                      (1ULL << 62)
> >   #define MENVCFG_STCE                       (1ULL << 63)
> >
> >   /* For RV32 */
> > +#define MENVCFGH_SFCFIEN                   BIT(27)
> > +#define MENVCFGH_CFI                       BIT(28)
> >   #define MENVCFGH_PBMTE                     BIT(30)
> >   #define MENVCFGH_STCE                      BIT(31)
> >
> > @@ -763,10 +784,14 @@ typedef enum RISCVException {
> >   #define HENVCFG_CBIE                       MENVCFG_CBIE
> >   #define HENVCFG_CBCFE                      MENVCFG_CBCFE
> >   #define HENVCFG_CBZE                       MENVCFG_CBZE
> > +#define HENVCFG_SFCFIEN                    MENVCFG_SFCFIEN
> > +#define HENVCFG_CFI                        MENVCFG_CFI
> >   #define HENVCFG_PBMTE                      MENVCFG_PBMTE
> >   #define HENVCFG_STCE                       MENVCFG_STCE
> >
> >   /* For RV32 */
> > +#define HENVCFGH_SFCFIEN                    MENVCFGH_SFCFIEN
> > +#define HENVCFGH_CFI                        MENVCFGH_CFI
> >   #define HENVCFGH_PBMTE                      MENVCFGH_PBMTE
> >   #define HENVCFGH_STCE                       MENVCFGH_STCE
> >
> > diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h
> > index da32c61c85..f5bfc4955b 100644
> > --- a/target/riscv/pmp.h
> > +++ b/target/riscv/pmp.h
> > @@ -43,7 +43,8 @@ typedef enum {
> >       MSECCFG_MMWP  = 1 << 1,
> >       MSECCFG_RLB   = 1 << 2,
> >       MSECCFG_USEED = 1 << 8,
> > -    MSECCFG_SSEED = 1 << 9
> > +    MSECCFG_SSEED = 1 << 9,
> > +    MSECCFG_MFCFIEN =  1 << 10
> >   } mseccfg_field_t;
> >
> >   typedef struct {


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v1 RFC Zisslpcfi 1/9] target/riscv: adding zimops and zisslpcfi extension to RISCV cpu config
  2023-02-15  2:52   ` LIU Zhiwei
@ 2023-02-15 20:47     ` Deepak Gupta
  2023-02-16  1:46       ` LIU Zhiwei
  0 siblings, 1 reply; 42+ messages in thread
From: Deepak Gupta @ 2023-02-15 20:47 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, liweiwei, dbarboza,
	Kip Walker, qemu-riscv, qemu-devel

On Tue, Feb 14, 2023 at 6:52 PM LIU Zhiwei <zhiwei_liu@linux.alibaba.com> wrote:
>
>
> On 2023/2/9 14:23, Deepak Gupta wrote:
> > Introducing riscv `zisslpcfi` extension to riscv target. `zisslpcfi`
> > extension provides hardware assistance to riscv hart to enable control
> > flow integrity (CFI) for software.
> >
> > `zisslpcfi` extension expects hart to implement `zimops`. `zimops` stands
> > for "unprivileged integer maybe operations". `zimops` carve out certain
> > reserved opcodes encodings from integer spec to "may be operations"
> > encodings. `zimops` opcode encodings simply move 0 to rd.
> > `zisslpcfi` claims some of the `zimops` encodings and use them for shadow
> > stack management or indirect branch tracking. Any future extension can
> > also claim `zimops` encodings.
>
> Does  the zimops has a independent specification? If so, you should give
> a link to this
> specification.

Actual formal documentation is still a work in progress.
I am hoping to provide a reference to it in my next iteration.

>
> >
> > This patch also adds a dependency check for `zimops` to be enabled if
> > `zisslpcfi` is enabled on the hart.
>
> You should don't add two extensions in one patch. I think you should add
> them one by one.
> And add the zimop first.  In my opinion, you should implement the whole
> zimop extension before
> adding any patch for zisslpcfi, including the implementation of mop.rr
> and mop.r.

Noted will make sure of that and will send two different patch series then.

>
> >
> > Signed-off-by: Deepak Gupta <debug@rivosinc.com>
> > Signed-off-by: Kip Walker  <kip@rivosinc.com>
> > ---
> >   target/riscv/cpu.c | 13 +++++++++++++
> >   target/riscv/cpu.h |  2 ++
> >   2 files changed, 15 insertions(+)
> >
> > diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> > index cc75ca7667..6b4e90eb91 100644
> > --- a/target/riscv/cpu.c
> > +++ b/target/riscv/cpu.c
> > @@ -110,6 +110,8 @@ static const struct isa_ext_data isa_edata_arr[] = {
> >       ISA_EXT_DATA_ENTRY(svnapot, true, PRIV_VERSION_1_12_0, ext_svnapot),
> >       ISA_EXT_DATA_ENTRY(svpbmt, true, PRIV_VERSION_1_12_0, ext_svpbmt),
> >       ISA_EXT_DATA_ENTRY(xventanacondops, true, PRIV_VERSION_1_12_0, ext_XVentanaCondOps),
> > +    ISA_EXT_DATA_ENTRY(zimops, true, PRIV_VERSION_1_12_0, ext_zimops),
> > +    ISA_EXT_DATA_ENTRY(zisslpcfi, true, PRIV_VERSION_1_12_0, ext_cfi),
> Add them one by one.
> >   };
> >
> >   static bool isa_ext_is_enabled(RISCVCPU *cpu,
> > @@ -792,6 +794,11 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
> >               return;
> >           }
> >
> > +        if (cpu->cfg.ext_cfi && !cpu->cfg.ext_zimops) {
> > +            error_setg(errp, "Zisslpcfi extension requires Zimops extension");
> > +            return;
> > +        }
> > +
> Seems reasonable for me.
> >           /* Set the ISA extensions, checks should have happened above */
> >           if (cpu->cfg.ext_zdinx || cpu->cfg.ext_zhinx ||
> >               cpu->cfg.ext_zhinxmin) {
> > @@ -1102,6 +1109,12 @@ static Property riscv_cpu_properties[] = {
> >   #ifndef CONFIG_USER_ONLY
> >       DEFINE_PROP_UINT64("resetvec", RISCVCPU, env.resetvec, DEFAULT_RSTVEC),
> >   #endif
> > +    /*
> > +     * Zisslpcfi CFI extension, Zisslpcfi implicitly means Zimops is
> > +     * implemented
> > +     */
> > +    DEFINE_PROP_BOOL("zisslpcfi", RISCVCPU, cfg.ext_cfi, true),
> > +    DEFINE_PROP_BOOL("zimops", RISCVCPU, cfg.ext_zimops, true),
>
> Default value should be false.

Yes, I have to fix this.

>
> Zhiwei
>
> >
> >       DEFINE_PROP_BOOL("short-isa-string", RISCVCPU, cfg.short_isa_string, false),
> >
> > diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> > index f5609b62a2..9a923760b2 100644
> > --- a/target/riscv/cpu.h
> > +++ b/target/riscv/cpu.h
> > @@ -471,6 +471,8 @@ struct RISCVCPUConfig {
> >       uint32_t mvendorid;
> >       uint64_t marchid;
> >       uint64_t mimpid;
> > +    bool ext_zimops;
> > +    bool ext_cfi;
> >
> >       /* Vendor-specific custom extensions */
> >       bool ext_XVentanaCondOps;


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v1 RFC Zisslpcfi 5/9] target/riscv: state save and restore of zisslppcfi state
  2023-02-15  6:10   ` LIU Zhiwei
@ 2023-02-15 23:13     ` Deepak Gupta
  0 siblings, 0 replies; 42+ messages in thread
From: Deepak Gupta @ 2023-02-15 23:13 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, liweiwei, dbarboza,
	Kip Walker, qemu-riscv, qemu-devel

On Tue, Feb 14, 2023 at 10:11 PM LIU Zhiwei
<zhiwei_liu@linux.alibaba.com> wrote:
>
>
> On 2023/2/9 14:24, Deepak Gupta wrote:
> > zisslpcfi's forward cfi if enabled on a hart, enables tracking of
> > indirect branches. CPU/hart internally keeps a state `elp` short
> > for expecting landing pad instruction. This state goes into
> > LP_EXPECTED on an indirect branch. But an interrupt/exception can occur
> > before target instruction is executed. In such a case this state must be
> > preserved so that it can be restored later. zisslpcfi saves elp state in
> > `sstatus` CSR.
>
> And mstatus CSR.

Noted.

>
> Otherwise,
>
> Reviewed-by: LIU Zhiwei <zhiwei_liu@linux.alibaba.com>
>
> > This patch saves elp state in sstatus CSR on trap delivery
> > while restores from sstatus CSR on trap return.
> >
> > Additionally state in sstatus CSR must have save and restore zisslpcfi
> > state on exiting from hypervisor and entering into hypervisor.
> >
> > Signed-off-by: Deepak Gupta <debug@rivosinc.com>
> > Signed-off-by: Kip Walker  <kip@rivosinc.com>
> > ---
> >   target/riscv/cpu_bits.h   |  5 +++++
> >   target/riscv/cpu_helper.c | 26 ++++++++++++++++++++++++++
> >   target/riscv/op_helper.c  | 12 ++++++++++++
> >   3 files changed, 43 insertions(+)
> >
> > diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
> > index 1663ba5775..37100ec8f6 100644
> > --- a/target/riscv/cpu_bits.h
> > +++ b/target/riscv/cpu_bits.h
> > @@ -594,6 +594,11 @@ typedef enum {
> >
> >   #define CFISTATUS_S_MASK    (SSTATUS_UFCFIEN | SSTATUS_UBCFIEN | \
> >                                SSTATUS_SPELP)
> > +/* enum for branch tracking state in cpu/hart */
> > +typedef enum {
> > +    NO_LP_EXPECTED = 0,
> > +    LP_EXPECTED = 1,
> > +} cfi_elp;
> >
> >   /* hstatus CSR bits */
> >   #define HSTATUS_VSBE         0x00000020
> > diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> > index a397023840..fc188683c9 100644
> > --- a/target/riscv/cpu_helper.c
> > +++ b/target/riscv/cpu_helper.c
> > @@ -534,6 +534,16 @@ void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env)
> >       if (riscv_has_ext(env, RVF)) {
> >           mstatus_mask |= MSTATUS_FS;
> >       }
> > +
> > +    /*
> > +     * If cfi extension available, menvcfg.CFI = 1 and henvcfg.CFI = 1,
> > +     * then apply CFI mask on mstatus
> > +     */
> > +    if (env_archcpu(env)->cfg.ext_cfi &&
> > +        get_field(env->menvcfg, MENVCFG_CFI) &&
> > +        get_field(env->henvcfg, HENVCFG_CFI)) {
> > +        mstatus_mask |= CFISTATUS_S_MASK;
> > +    }
> >       bool current_virt = riscv_cpu_virt_enabled(env);
> >
> >       g_assert(riscv_has_ext(env, RVH));
> > @@ -1723,6 +1733,10 @@ void riscv_cpu_do_interrupt(CPUState *cs)
> >       if (env->priv <= PRV_S &&
> >               cause < TARGET_LONG_BITS && ((deleg >> cause) & 1)) {
> >           /* handle the trap in S-mode */
> > +        /* save elp status */
> > +        if (cpu_get_fcfien(env)) {
> > +            env->mstatus = set_field(env->mstatus, MSTATUS_SPELP, env->elp);
> > +        }
> >           if (riscv_has_ext(env, RVH)) {
> >               uint64_t hdeleg = async ? env->hideleg : env->hedeleg;
> >
> > @@ -1772,6 +1786,10 @@ void riscv_cpu_do_interrupt(CPUState *cs)
> >           riscv_cpu_set_mode(env, PRV_S);
> >       } else {
> >           /* handle the trap in M-mode */
> > +        /* save elp status */
> > +        if (cpu_get_fcfien(env)) {
> > +            env->mstatus = set_field(env->mstatus, MSTATUS_MPELP, env->elp);
> > +        }
> >           if (riscv_has_ext(env, RVH)) {
> >               if (riscv_cpu_virt_enabled(env)) {
> >                   riscv_cpu_swap_hypervisor_regs(env);
> > @@ -1803,6 +1821,14 @@ void riscv_cpu_do_interrupt(CPUState *cs)
> >           riscv_cpu_set_mode(env, PRV_M);
> >       }
> >
> > +    /*
> > +     * Interrupt/exception/trap delivery is asynchronous event and as per
> > +     * Zisslpcfi spec CPU should clear up the ELP state. If cfi extension is
> > +     * available, clear ELP state.
> > +     */
> > +    if (cpu->cfg.ext_cfi) {
> > +        env->elp = NO_LP_EXPECTED;
> > +    }
> >       /* NOTE: it is not necessary to yield load reservations here. It is only
> >        * necessary for an SC from "another hart" to cause a load reservation
> >        * to be yielded. Refer to the memory consistency model section of the
> > diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
> > index 878bcb03b8..d15893aa82 100644
> > --- a/target/riscv/op_helper.c
> > +++ b/target/riscv/op_helper.c
> > @@ -176,6 +176,12 @@ target_ulong helper_sret(CPURISCVState *env)
> >           riscv_cpu_set_virt_enabled(env, prev_virt);
> >       }
> >
> > +    /* If forward cfi enabled for target, restore elp status */
> > +    if (cpu_get_fcfien(env)) {
> > +        env->elp = get_field(env->mstatus, MSTATUS_SPELP);
> > +        env->mstatus = set_field(env->mstatus, MSTATUS_SPELP, 0);
> > +    }
> > +
> >       riscv_cpu_set_mode(env, prev_priv);
> >
> >       return retpc;
> > @@ -220,6 +226,12 @@ target_ulong helper_mret(CPURISCVState *env)
> >           riscv_cpu_set_virt_enabled(env, prev_virt);
> >       }
> >
> > +    /* If forward cfi enabled for target, restore elp status */
> > +    if (cpu_get_fcfien(env)) {
> > +        env->elp = get_field(env->mstatus, MSTATUS_MPELP);
> > +        env->mstatus = set_field(env->mstatus, MSTATUS_MPELP, 0);
> > +    }
> > +
> >       return retpc;
> >   }
> >


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v1 RFC Zisslpcfi 3/9] target/riscv: implements CSRs and new bits in existing CSRs in zisslpcfi
  2023-02-15  5:47   ` LIU Zhiwei
  2023-02-15  6:24     ` LIU Zhiwei
@ 2023-02-15 23:33     ` Deepak Gupta
  2023-02-16  0:02       ` Richard Henderson
  1 sibling, 1 reply; 42+ messages in thread
From: Deepak Gupta @ 2023-02-15 23:33 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, liweiwei, dbarboza,
	Kip Walker, qemu-riscv, qemu-devel

On Tue, Feb 14, 2023 at 9:47 PM LIU Zhiwei <zhiwei_liu@linux.alibaba.com> wrote:
>
>
> On 2023/2/9 14:23, Deepak Gupta wrote:
> > CSR_SSP and CSR_LPLR are new CSR additions to cpu/hart. This patch allows
> > access to these CSRs. A predicate routine handles access to these CSR as
> > per specification.
> >
> > This patch also implments new bit definitions in menvcfg/henvcfg/mstatus/
> > sstatus CSRs to master enabled cfi and enable forward cfi in S and M mode.
> > mstatus CSR holds forward and backward cfi enabling for U mode.
> >
> > There is no enabling bit for backward cfi in S and M mode. It is always
> > enabled if extension is implemented by CPU.
> >
> > Signed-off-by: Deepak Gupta <debug@rivosinc.com>
> > Signed-off-by: Kip Walker  <kip@rivosinc.com>
> > ---
> >   target/riscv/csr.c | 137 ++++++++++++++++++++++++++++++++++++++++++++-
> >   target/riscv/pmp.c |   9 +++
> >   2 files changed, 145 insertions(+), 1 deletion(-)
> >
> > diff --git a/target/riscv/csr.c b/target/riscv/csr.c
> > index 0db2c233e5..24e208ebed 100644
> > --- a/target/riscv/csr.c
> > +++ b/target/riscv/csr.c
> > @@ -163,6 +163,50 @@ static RISCVException ctr32(CPURISCVState *env, int csrno)
> >       return ctr(env, csrno);
> >   }
> >
> > +static RISCVException cfi(CPURISCVState *env, int csrno)
> > +{
> > +    /* no cfi extension */
> > +    if (!env_archcpu(env)->cfg.ext_cfi) {
> > +        return RISCV_EXCP_ILLEGAL_INST;
> > +    }
> > +    /*
> > +     * CONFIG_USER_MODE always allow access for now. Better for user mode only
> > +     * functionality
> > +     */
> > +#if !defined(CONFIG_USER_ONLY)
> > +    /* current priv not M */
> > +    if (env->priv != PRV_M) {
> > +        /* menvcfg says no CFI */
> > +        if (!get_field(env->menvcfg, MENVCFG_CFI)) {
> > +            return RISCV_EXCP_ILLEGAL_INST;
> > +        }
> > +
> > +        /* V = 1 and henvcfg says no CFI. raise virtual instr fault */
> > +        if (riscv_cpu_virt_enabled(env) &&
> > +            !get_field(env->henvcfg, HENVCFG_CFI)) {
> > +            return RISCV_EXCP_VIRT_INSTRUCTION_FAULT;
> > +        }
> > +
> > +        /*
> > +         * LPLR and SSP are not accessible to U mode if disabled via status
> > +         * CSR
> > +         */
> > +        if (env->priv == PRV_U) {
> > +            if (csrno == CSR_LPLR &&
> > +                !get_field(env->mstatus, MSTATUS_UFCFIEN)) {
> > +                return RISCV_EXCP_ILLEGAL_INST;
> > +            }
> > +            if (csrno == CSR_SSP &&
> > +                !get_field(env->mstatus, MSTATUS_UBCFIEN)) {
> > +                return RISCV_EXCP_ILLEGAL_INST;
> > +            }
> > +        }
> > +    }
> > +#endif
> > +
> > +    return RISCV_EXCP_NONE;
> > +}
> > +
> >   #if !defined(CONFIG_USER_ONLY)
> >   static RISCVException mctr(CPURISCVState *env, int csrno)
> >   {
> > @@ -485,6 +529,32 @@ static RISCVException seed(CPURISCVState *env, int csrno)
> >   #endif
> >   }
> >
> > +/* Zisslpcfi CSR_LPLR read/write */
> > +static int read_lplr(CPURISCVState *env, int csrno, target_ulong *val)
> > +{
> > +    *val = env->lplr;
> > +    return RISCV_EXCP_NONE;
> > +}
> > +
> > +static int write_lplr(CPURISCVState *env, int csrno, target_ulong val)
> > +{
> > +    env->lplr = val & (LPLR_UL | LPLR_ML | LPLR_LL);
> > +    return RISCV_EXCP_NONE;
> > +}
> > +
> > +/* Zisslpcfi CSR_SSP read/write */
> > +static int read_ssp(CPURISCVState *env, int csrno, target_ulong *val)
> > +{
> > +    *val = env->ssp;
> > +    return RISCV_EXCP_NONE;
> > +}
> > +
> > +static int write_ssp(CPURISCVState *env, int csrno, target_ulong val)
> > +{
> > +    env->ssp = val;
> > +    return RISCV_EXCP_NONE;
> > +}
> > +
> >   /* User Floating-Point CSRs */
> >   static RISCVException read_fflags(CPURISCVState *env, int csrno,
> >                                     target_ulong *val)
> > @@ -1227,7 +1297,7 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno,
> >
> >       /* flush tlb on mstatus fields that affect VM */
> >       if ((val ^ mstatus) & (MSTATUS_MXR | MSTATUS_MPP | MSTATUS_MPV |
> > -            MSTATUS_MPRV | MSTATUS_SUM)) {
> > +            MSTATUS_MPRV | MSTATUS_SUM | MSTATUS_UFCFIEN | MSTATUS_UBCFIEN)) {
>
> These two fields should be guarded by the check of ext_cfi.

Yes this makes sense. Will fix it.

>
> And MSTATUS_UBCFIEN field change don't need flush tlb.
>

TCG code-gen would be different depending on whether ubcfi is enabled or not.
As an example a TB might have code generated when bcfi was enabled.
But if someone disables it,
translation needs to happen again and zimops implementation should be
generated this time.

> I didn't get why we should flush tlb for forward cfi. For background,
> there are some enhancement for the PTE and PMP, we may need do some
> memory adjustments. But forward cfi just adds some instructions. Why we
> should flush tlb? Does the tlb can't be used any more?

Pretty much same reasoning as back cfi. commit message in patch #7
goes in more detail on that.
TLB flush is mainly to invalidate TB.

>
> >           tlb_flush(env_cpu(env));
> >       }
> >       mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE |
> > @@ -1250,6 +1320,11 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno,
> >           }
> >       }
> >
> > +    /* If cfi extension is available, then apply cfi status mask */
> > +    if (env_archcpu(env)->cfg.ext_cfi) {
> > +        mask |= CFISTATUS_M_MASK;
> > +    }
> > +
> >       mstatus = (mstatus & ~mask) | (val & mask);
> >
> >       if (xl > MXL_RV32) {
> > @@ -1880,9 +1955,17 @@ static RISCVException write_menvcfg(CPURISCVState *env, int csrno,
> >                                     target_ulong val)
> >   {
> >       uint64_t mask = MENVCFG_FIOM | MENVCFG_CBIE | MENVCFG_CBCFE | MENVCFG_CBZE;
> > +    uint64_t cfi_mask = MENVCFG_CFI | MENVCFG_SFCFIEN;
> >
> >       if (riscv_cpu_mxl(env) == MXL_RV64) {
> >           mask |= MENVCFG_PBMTE | MENVCFG_STCE;
> > +        if (env_archcpu(env)->cfg.ext_cfi) {
> > +            mask |= cfi_mask;
> > +            /* If any cfi enabling bit changes in menvcfg, flush tlb */
> > +            if ((val ^ env->menvcfg) & cfi_mask) {
> > +                tlb_flush(env_cpu(env));
> Don't flush tlb for MENVCFG_SFCFIE field changes.

We need to because we have invalidate earlier generated TB code.

> > +            }
> > +        }
> >       }
> >       env->menvcfg = (env->menvcfg & ~mask) | (val & mask);
> >
> > @@ -1900,8 +1983,17 @@ static RISCVException write_menvcfgh(CPURISCVState *env, int csrno,
> >                                     target_ulong val)
> >   {
> >       uint64_t mask = MENVCFG_PBMTE | MENVCFG_STCE;
> > +    uint64_t cfi_mask = MENVCFG_CFI | MENVCFG_SFCFIEN;
> MENVCFG_SFCFIE
> >       uint64_t valh = (uint64_t)val << 32;
> >
> > +    if (env_archcpu(env)->cfg.ext_cfi) {
> > +            mask |= cfi_mask;
> > +            /* If any cfi enabling bit changes in menvcfg, flush tlb */
> > +            if ((val ^ env->menvcfg) & cfi_mask) {
> > +                tlb_flush(env_cpu(env));
> > +            }
> If SFCFIE field change, we should not flush the tlb.

It's same reasoning that generated TB code is invalid now.

> > +    }
> > +
> >       env->menvcfg = (env->menvcfg & ~mask) | (valh & mask);
> >
> >       return RISCV_EXCP_NONE;
> > @@ -1954,6 +2046,7 @@ static RISCVException write_henvcfg(CPURISCVState *env, int csrno,
> >                                     target_ulong val)
> >   {
> >       uint64_t mask = HENVCFG_FIOM | HENVCFG_CBIE | HENVCFG_CBCFE | HENVCFG_CBZE;
> > +    uint64_t cfi_mask = HENVCFG_CFI | HENVCFG_SFCFIEN;
>
> HENVCFG_SFCFIE
>
> >       RISCVException ret;
> >
> >       ret = smstateen_acc_ok(env, 0, SMSTATEEN0_HSENVCFG);
> > @@ -1963,6 +2056,18 @@ static RISCVException write_henvcfg(CPURISCVState *env, int csrno,
> >
> >       if (riscv_cpu_mxl(env) == MXL_RV64) {
> >           mask |= HENVCFG_PBMTE | HENVCFG_STCE;
> > +        /*
> > +         * If cfi available and menvcfg.CFI = 1, then apply cfi mask for
> > +         * henvcfg
> > +         */
> > +        if (env_archcpu(env)->cfg.ext_cfi &&
> > +            get_field(env->menvcfg, MENVCFG_CFI)) {
> > +            mask |= cfi_mask;
> > +            /* If any cfi enabling bit changes in henvcfg, flush tlb */
> > +            if ((val ^ env->henvcfg) & cfi_mask) {
> > +                tlb_flush(env_cpu(env));
> > +            }
> If SFCFIE field change, we should not flush the tlb.

It's same reasoning that generated TB code is invalid now.

> > +        }
> >       }
> >
> >       env->henvcfg = (env->henvcfg & ~mask) | (val & mask);
> > @@ -1988,9 +2093,19 @@ static RISCVException write_henvcfgh(CPURISCVState *env, int csrno,
> >                                     target_ulong val)
> >   {
> >       uint64_t mask = HENVCFG_PBMTE | HENVCFG_STCE;
> > +    uint64_t cfi_mask = HENVCFG_CFI | HENVCFG_SFCFIEN;
> >       uint64_t valh = (uint64_t)val << 32;
> >       RISCVException ret;
> >
> > +    if (env_archcpu(env)->cfg.ext_cfi &&
> > +        get_field(env->menvcfg, MENVCFG_CFI)) {
> > +        mask |= cfi_mask;
> > +        /* If any cfi enabling bit changes in henvcfg, flush tlb */
> > +        if ((val ^ env->henvcfg) & cfi_mask) {
> > +            tlb_flush(env_cpu(env));
> > +        }
> > +    }
> > +
> >       ret = smstateen_acc_ok(env, 0, SMSTATEEN0_HSENVCFG);
> >       if (ret != RISCV_EXCP_NONE) {
> >           return ret;
> > @@ -2270,6 +2385,11 @@ static RISCVException read_sstatus_i128(CPURISCVState *env, int csrno,
> >           mask |= SSTATUS64_UXL;
> >       }
> >
> > +    if ((env_archcpu(env)->cfg.ext_cfi) &&
> > +         get_field(env->menvcfg, MENVCFG_CFI)) {
> > +        mask |= CFISTATUS_S_MASK;
> > +    }
> > +
> >       *val = int128_make128(sstatus, add_status_sd(MXL_RV128, sstatus));
> >       return RISCV_EXCP_NONE;
> >   }
> > @@ -2281,6 +2401,11 @@ static RISCVException read_sstatus(CPURISCVState *env, int csrno,
> >       if (env->xl != MXL_RV32 || env->debugger) {
> >           mask |= SSTATUS64_UXL;
> >       }
> > +
> > +    if ((env_archcpu(env)->cfg.ext_cfi) &&
> > +         get_field(env->menvcfg, MENVCFG_CFI)) {
> > +        mask |= CFISTATUS_S_MASK;
> > +    }
> >       /* TODO: Use SXL not MXL. */
> >       *val = add_status_sd(riscv_cpu_mxl(env), env->mstatus & mask);
> >       return RISCV_EXCP_NONE;
> > @@ -2296,6 +2421,12 @@ static RISCVException write_sstatus(CPURISCVState *env, int csrno,
> >               mask |= SSTATUS64_UXL;
> >           }
> >       }
> > +
> > +    /* If cfi available and menvcfg.CFI = 1, apply CFI mask for sstatus */
> > +    if ((env_archcpu(env)->cfg.ext_cfi) &&
> > +         get_field(env->menvcfg, MENVCFG_CFI)) {
> > +        mask |= CFISTATUS_S_MASK;
> > +    }
> >       target_ulong newval = (env->mstatus & ~mask) | (val & mask);
> >       return write_mstatus(env, CSR_MSTATUS, newval);
> >   }
> > @@ -4001,6 +4132,10 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
> >       /* Crypto Extension */
> >       [CSR_SEED] = { "seed", seed, NULL, NULL, rmw_seed },
> >
> > +    /* User mode CFI CSR */
> > +    [CSR_LPLR] = { "lplr", cfi, read_lplr, write_lplr },
> > +    [CSR_SSP]  = { "ssp", cfi, read_ssp, write_ssp },
> > +
> >   #if !defined(CONFIG_USER_ONLY)
> >       /* Machine Timers and Counters */
> >       [CSR_MCYCLE]    = { "mcycle",    any,   read_hpmcounter,
> > diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c
> > index d1126a6066..89745d46cd 100644
> > --- a/target/riscv/pmp.c
> > +++ b/target/riscv/pmp.c
> > @@ -579,6 +579,15 @@ void mseccfg_csr_write(CPURISCVState *env, target_ulong val)
> >       /* Sticky bits */
> >       val |= (env->mseccfg & (MSECCFG_MMWP | MSECCFG_MML));
> >
> > +    /* M-mode forward cfi to be enabled if cfi extension is implemented */
> > +    if (env_archcpu(env)->cfg.ext_cfi) {
> > +        val |= (val & MSECCFG_MFCFIEN);
> This statement does nothing. Is it a typo?
> > +        /* If forward cfi in mseccfg is being toggled, flush tlb */
> > +        if ((env->mseccfg ^ val) & MSECCFG_MFCFIEN) {
> > +                tlb_flush(env_cpu(env));
> > +        }
>
> No need flush tlb.
>
> Zhiwei
>
> > +    }
> > +
> >       env->mseccfg = val;
> >   }
> >


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v1 RFC Zisslpcfi 4/9] target/riscv: helper functions for forward and backward cfi
  2023-02-15  6:26   ` LIU Zhiwei
@ 2023-02-15 23:35     ` Deepak Gupta
  0 siblings, 0 replies; 42+ messages in thread
From: Deepak Gupta @ 2023-02-15 23:35 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, liweiwei, dbarboza,
	Kip Walker, qemu-riscv, qemu-devel

On Tue, Feb 14, 2023 at 10:26 PM LIU Zhiwei
<zhiwei_liu@linux.alibaba.com> wrote:
>
>
> On 2023/2/9 14:23, Deepak Gupta wrote:
> > Implementation for forward cfi and backward cfi needs helper function
> > to determine if currently fcfi and bcfi are enabled. Enable depends on
> > privilege mode and settings in sstatus/menvcfg/henvcfg/mseccfg CSRs.
> >
> > Signed-off-by: Deepak Gupta <debug@rivosinc.com>
> > Signed-off-by: Kip Walker  <kip@rivosinc.com>
> > ---
> >   target/riscv/cpu.h        |  2 ++
> >   target/riscv/cpu_helper.c | 51 +++++++++++++++++++++++++++++++++++++++
> >   2 files changed, 53 insertions(+)
> >
> > diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> > index 18db61a06a..d14ea4f91d 100644
> > --- a/target/riscv/cpu.h
> > +++ b/target/riscv/cpu.h
> > @@ -568,6 +568,8 @@ bool riscv_cpu_virt_enabled(CPURISCVState *env);
> >   void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable);
> >   bool riscv_cpu_two_stage_lookup(int mmu_idx);
> >   int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch);
> > +bool cpu_get_fcfien(CPURISCVState *env);
> > +bool cpu_get_bcfien(CPURISCVState *env);
> >   hwaddr riscv_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
> >   G_NORETURN void  riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
> >                                                  MMUAccessType access_type, int mmu_idx,
> > diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> > index 9a28816521..a397023840 100644
> > --- a/target/riscv/cpu_helper.c
> > +++ b/target/riscv/cpu_helper.c
> > @@ -30,6 +30,7 @@
> >   #include "sysemu/cpu-timers.h"
> >   #include "cpu_bits.h"
> >   #include "debug.h"
> > +#include "pmp.h"
> >
> >   int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch)
> >   {
> > @@ -40,6 +41,56 @@ int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch)
> >   #endif
> >   }
> >
> > +bool cpu_get_fcfien(CPURISCVState *env)
> > +{
> > +#ifdef CONFIG_USER_ONLY
> > +    return false;
> > +#else
> > +    /* no cfi extension, return false */
> > +    if (!env_archcpu(env)->cfg.ext_cfi) {
> > +        return false;
> > +    }
> > +
> > +    switch (env->priv) {
> > +    case PRV_U:
> > +        return (env->mstatus & MSTATUS_UFCFIEN) ? true : false;
>
> It's not right. We should also check for menvcfg.cfie. The same to other
> checks in S mode or U mode.
>

Yes you're right. I lost that check in one of the re-factoring.
Thanks. Will fix it.

> Zhiwei
>
> > +    case PRV_S:
> > +        return (env->menvcfg & MENVCFG_SFCFIEN) ? true : false;
> > +    case PRV_M:
> > +        return (env->mseccfg & MSECCFG_MFCFIEN) ? true : false;
> > +    default:
> > +        g_assert_not_reached();
> > +    }
> > +#endif
> > +}
> > +
> > +bool cpu_get_bcfien(CPURISCVState *env)
> > +{
> > +#ifdef CONFIG_USER_ONLY
> > +    return false;
> > +#else
> > +    /* no cfi extension, return false */
> > +    if (!env_archcpu(env)->cfg.ext_cfi) {
> > +        return false;
> > +    }
> > +
> > +    switch (env->priv) {
> > +    case PRV_U:
> > +        return (env->mstatus & MSTATUS_UBCFIEN) ? true : false;
> > +
> > +        /*
> > +         * no gating for back cfi in M/S mode. back cfi is always on for
> > +         * M/S mode
> > +         */
> > +    case PRV_S:
> > +    case PRV_M:
> > +        return true;
> > +    default:
> > +        g_assert_not_reached();
> > +    }
> > +#endif
> > +}
> > +
> >   void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc,
> >                             target_ulong *cs_base, uint32_t *pflags)
> >   {


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v1 RFC Zisslpcfi 3/9] target/riscv: implements CSRs and new bits in existing CSRs in zisslpcfi
  2023-02-15  6:24     ` LIU Zhiwei
@ 2023-02-15 23:42       ` Deepak Gupta
  0 siblings, 0 replies; 42+ messages in thread
From: Deepak Gupta @ 2023-02-15 23:42 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, liweiwei, dbarboza,
	Kip Walker, qemu-riscv, qemu-devel

On Tue, Feb 14, 2023 at 10:24 PM LIU Zhiwei
<zhiwei_liu@linux.alibaba.com> wrote:
>
> I don't find the modification for read_mstatus.

Doesn't need any modification in read_mstatus.
It just returns whatever is in the mstatus.

>
> Zhiwei
>
> On 2023/2/15 13:47, LIU Zhiwei wrote:
> >
> > On 2023/2/9 14:23, Deepak Gupta wrote:
> >> CSR_SSP and CSR_LPLR are new CSR additions to cpu/hart. This patch
> >> allows
> >> access to these CSRs. A predicate routine handles access to these CSR as
> >> per specification.
> >>
> >> This patch also implments new bit definitions in
> >> menvcfg/henvcfg/mstatus/
> >> sstatus CSRs to master enabled cfi and enable forward cfi in S and M
> >> mode.
> >> mstatus CSR holds forward and backward cfi enabling for U mode.
> >>
> >> There is no enabling bit for backward cfi in S and M mode. It is always
> >> enabled if extension is implemented by CPU.
> >>
> >> Signed-off-by: Deepak Gupta <debug@rivosinc.com>
> >> Signed-off-by: Kip Walker  <kip@rivosinc.com>
> >> ---
> >>   target/riscv/csr.c | 137 ++++++++++++++++++++++++++++++++++++++++++++-
> >>   target/riscv/pmp.c |   9 +++
> >>   2 files changed, 145 insertions(+), 1 deletion(-)
> >>
> >> diff --git a/target/riscv/csr.c b/target/riscv/csr.c
> >> index 0db2c233e5..24e208ebed 100644
> >> --- a/target/riscv/csr.c
> >> +++ b/target/riscv/csr.c
> >> @@ -163,6 +163,50 @@ static RISCVException ctr32(CPURISCVState *env,
> >> int csrno)
> >>       return ctr(env, csrno);
> >>   }
> >>   +static RISCVException cfi(CPURISCVState *env, int csrno)
> >> +{
> >> +    /* no cfi extension */
> >> +    if (!env_archcpu(env)->cfg.ext_cfi) {
> >> +        return RISCV_EXCP_ILLEGAL_INST;
> >> +    }
> >> +    /*
> >> +     * CONFIG_USER_MODE always allow access for now. Better for user
> >> mode only
> >> +     * functionality
> >> +     */
> >> +#if !defined(CONFIG_USER_ONLY)
> >> +    /* current priv not M */
> >> +    if (env->priv != PRV_M) {
> >> +        /* menvcfg says no CFI */
> >> +        if (!get_field(env->menvcfg, MENVCFG_CFI)) {
> >> +            return RISCV_EXCP_ILLEGAL_INST;
> >> +        }
> >> +
> >> +        /* V = 1 and henvcfg says no CFI. raise virtual instr fault */
> >> +        if (riscv_cpu_virt_enabled(env) &&
> >> +            !get_field(env->henvcfg, HENVCFG_CFI)) {
> >> +            return RISCV_EXCP_VIRT_INSTRUCTION_FAULT;
> >> +        }
> >> +
> >> +        /*
> >> +         * LPLR and SSP are not accessible to U mode if disabled via
> >> status
> >> +         * CSR
> >> +         */
> >> +        if (env->priv == PRV_U) {
> >> +            if (csrno == CSR_LPLR &&
> >> +                !get_field(env->mstatus, MSTATUS_UFCFIEN)) {
> >> +                return RISCV_EXCP_ILLEGAL_INST;
> >> +            }
> >> +            if (csrno == CSR_SSP &&
> >> +                !get_field(env->mstatus, MSTATUS_UBCFIEN)) {
> >> +                return RISCV_EXCP_ILLEGAL_INST;
> >> +            }
> >> +        }
> >> +    }
> >> +#endif
> >> +
> >> +    return RISCV_EXCP_NONE;
> >> +}
> >> +
> >>   #if !defined(CONFIG_USER_ONLY)
> >>   static RISCVException mctr(CPURISCVState *env, int csrno)
> >>   {
> >> @@ -485,6 +529,32 @@ static RISCVException seed(CPURISCVState *env,
> >> int csrno)
> >>   #endif
> >>   }
> >>   +/* Zisslpcfi CSR_LPLR read/write */
> >> +static int read_lplr(CPURISCVState *env, int csrno, target_ulong *val)
> >> +{
> >> +    *val = env->lplr;
> >> +    return RISCV_EXCP_NONE;
> >> +}
> >> +
> >> +static int write_lplr(CPURISCVState *env, int csrno, target_ulong val)
> >> +{
> >> +    env->lplr = val & (LPLR_UL | LPLR_ML | LPLR_LL);
> >> +    return RISCV_EXCP_NONE;
> >> +}
> >> +
> >> +/* Zisslpcfi CSR_SSP read/write */
> >> +static int read_ssp(CPURISCVState *env, int csrno, target_ulong *val)
> >> +{
> >> +    *val = env->ssp;
> >> +    return RISCV_EXCP_NONE;
> >> +}
> >> +
> >> +static int write_ssp(CPURISCVState *env, int csrno, target_ulong val)
> >> +{
> >> +    env->ssp = val;
> >> +    return RISCV_EXCP_NONE;
> >> +}
> >> +
> >>   /* User Floating-Point CSRs */
> >>   static RISCVException read_fflags(CPURISCVState *env, int csrno,
> >>                                     target_ulong *val)
> >> @@ -1227,7 +1297,7 @@ static RISCVException
> >> write_mstatus(CPURISCVState *env, int csrno,
> >>         /* flush tlb on mstatus fields that affect VM */
> >>       if ((val ^ mstatus) & (MSTATUS_MXR | MSTATUS_MPP | MSTATUS_MPV |
> >> -            MSTATUS_MPRV | MSTATUS_SUM)) {
> >> +            MSTATUS_MPRV | MSTATUS_SUM | MSTATUS_UFCFIEN |
> >> MSTATUS_UBCFIEN)) {
> >
> > These two fields should be guarded by the check of ext_cfi.
> >
> > And MSTATUS_UBCFIEN field change don't need flush tlb.
> >
> > I didn't get why we should flush tlb for forward cfi. For background,
> > there are some enhancement for the PTE and PMP, we may need do some
> > memory adjustments. But forward cfi just adds some instructions. Why
> > we should flush tlb? Does the tlb can't be used any more?
> >
> >>           tlb_flush(env_cpu(env));
> >>       }
> >>       mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE |
> >> @@ -1250,6 +1320,11 @@ static RISCVException
> >> write_mstatus(CPURISCVState *env, int csrno,
> >>           }
> >>       }
> >>   +    /* If cfi extension is available, then apply cfi status mask */
> >> +    if (env_archcpu(env)->cfg.ext_cfi) {
> >> +        mask |= CFISTATUS_M_MASK;
> >> +    }
> >> +
> >>       mstatus = (mstatus & ~mask) | (val & mask);
> >>         if (xl > MXL_RV32) {
> >> @@ -1880,9 +1955,17 @@ static RISCVException
> >> write_menvcfg(CPURISCVState *env, int csrno,
> >>                                     target_ulong val)
> >>   {
> >>       uint64_t mask = MENVCFG_FIOM | MENVCFG_CBIE | MENVCFG_CBCFE |
> >> MENVCFG_CBZE;
> >> +    uint64_t cfi_mask = MENVCFG_CFI | MENVCFG_SFCFIEN;
> >>         if (riscv_cpu_mxl(env) == MXL_RV64) {
> >>           mask |= MENVCFG_PBMTE | MENVCFG_STCE;
> >> +        if (env_archcpu(env)->cfg.ext_cfi) {
> >> +            mask |= cfi_mask;
> >> +            /* If any cfi enabling bit changes in menvcfg, flush tlb */
> >> +            if ((val ^ env->menvcfg) & cfi_mask) {
> >> +                tlb_flush(env_cpu(env));
> > Don't flush tlb for MENVCFG_SFCFIE field changes.
> >> +            }
> >> +        }
> >>       }
> >>       env->menvcfg = (env->menvcfg & ~mask) | (val & mask);
> >>   @@ -1900,8 +1983,17 @@ static RISCVException
> >> write_menvcfgh(CPURISCVState *env, int csrno,
> >>                                     target_ulong val)
> >>   {
> >>       uint64_t mask = MENVCFG_PBMTE | MENVCFG_STCE;
> >> +    uint64_t cfi_mask = MENVCFG_CFI | MENVCFG_SFCFIEN;
> > MENVCFG_SFCFIE
> >>       uint64_t valh = (uint64_t)val << 32;
> >>   +    if (env_archcpu(env)->cfg.ext_cfi) {
> >> +            mask |= cfi_mask;
> >> +            /* If any cfi enabling bit changes in menvcfg, flush tlb */
> >> +            if ((val ^ env->menvcfg) & cfi_mask) {
> >> +                tlb_flush(env_cpu(env));
> >> +            }
> > If SFCFIE field change, we should not flush the tlb.
> >> +    }
> >> +
> >>       env->menvcfg = (env->menvcfg & ~mask) | (valh & mask);
> >>         return RISCV_EXCP_NONE;
> >> @@ -1954,6 +2046,7 @@ static RISCVException
> >> write_henvcfg(CPURISCVState *env, int csrno,
> >>                                     target_ulong val)
> >>   {
> >>       uint64_t mask = HENVCFG_FIOM | HENVCFG_CBIE | HENVCFG_CBCFE |
> >> HENVCFG_CBZE;
> >> +    uint64_t cfi_mask = HENVCFG_CFI | HENVCFG_SFCFIEN;
> >
> > HENVCFG_SFCFIE
> >
> >>       RISCVException ret;
> >>         ret = smstateen_acc_ok(env, 0, SMSTATEEN0_HSENVCFG);
> >> @@ -1963,6 +2056,18 @@ static RISCVException
> >> write_henvcfg(CPURISCVState *env, int csrno,
> >>         if (riscv_cpu_mxl(env) == MXL_RV64) {
> >>           mask |= HENVCFG_PBMTE | HENVCFG_STCE;
> >> +        /*
> >> +         * If cfi available and menvcfg.CFI = 1, then apply cfi mask
> >> for
> >> +         * henvcfg
> >> +         */
> >> +        if (env_archcpu(env)->cfg.ext_cfi &&
> >> +            get_field(env->menvcfg, MENVCFG_CFI)) {
> >> +            mask |= cfi_mask;
> >> +            /* If any cfi enabling bit changes in henvcfg, flush tlb */
> >> +            if ((val ^ env->henvcfg) & cfi_mask) {
> >> +                tlb_flush(env_cpu(env));
> >> +            }
> > If SFCFIE field change, we should not flush the tlb.
> >> +        }
> >>       }
> >>         env->henvcfg = (env->henvcfg & ~mask) | (val & mask);
> >> @@ -1988,9 +2093,19 @@ static RISCVException
> >> write_henvcfgh(CPURISCVState *env, int csrno,
> >>                                     target_ulong val)
> >>   {
> >>       uint64_t mask = HENVCFG_PBMTE | HENVCFG_STCE;
> >> +    uint64_t cfi_mask = HENVCFG_CFI | HENVCFG_SFCFIEN;
> >>       uint64_t valh = (uint64_t)val << 32;
> >>       RISCVException ret;
> >>   +    if (env_archcpu(env)->cfg.ext_cfi &&
> >> +        get_field(env->menvcfg, MENVCFG_CFI)) {
> >> +        mask |= cfi_mask;
> >> +        /* If any cfi enabling bit changes in henvcfg, flush tlb */
> >> +        if ((val ^ env->henvcfg) & cfi_mask) {
> >> +            tlb_flush(env_cpu(env));
> >> +        }
> >> +    }
> >> +
> >>       ret = smstateen_acc_ok(env, 0, SMSTATEEN0_HSENVCFG);
> >>       if (ret != RISCV_EXCP_NONE) {
> >>           return ret;
> >> @@ -2270,6 +2385,11 @@ static RISCVException
> >> read_sstatus_i128(CPURISCVState *env, int csrno,
> >>           mask |= SSTATUS64_UXL;
> >>       }
> >>   +    if ((env_archcpu(env)->cfg.ext_cfi) &&
> >> +         get_field(env->menvcfg, MENVCFG_CFI)) {
> >> +        mask |= CFISTATUS_S_MASK;
> >> +    }
> >> +
> >>       *val = int128_make128(sstatus, add_status_sd(MXL_RV128, sstatus));
> >>       return RISCV_EXCP_NONE;
> >>   }
> >> @@ -2281,6 +2401,11 @@ static RISCVException
> >> read_sstatus(CPURISCVState *env, int csrno,
> >>       if (env->xl != MXL_RV32 || env->debugger) {
> >>           mask |= SSTATUS64_UXL;
> >>       }
> >> +
> >> +    if ((env_archcpu(env)->cfg.ext_cfi) &&
> >> +         get_field(env->menvcfg, MENVCFG_CFI)) {
> >> +        mask |= CFISTATUS_S_MASK;
> >> +    }
> >>       /* TODO: Use SXL not MXL. */
> >>       *val = add_status_sd(riscv_cpu_mxl(env), env->mstatus & mask);
> >>       return RISCV_EXCP_NONE;
> >> @@ -2296,6 +2421,12 @@ static RISCVException
> >> write_sstatus(CPURISCVState *env, int csrno,
> >>               mask |= SSTATUS64_UXL;
> >>           }
> >>       }
> >> +
> >> +    /* If cfi available and menvcfg.CFI = 1, apply CFI mask for
> >> sstatus */
> >> +    if ((env_archcpu(env)->cfg.ext_cfi) &&
> >> +         get_field(env->menvcfg, MENVCFG_CFI)) {
> >> +        mask |= CFISTATUS_S_MASK;
> >> +    }
> >>       target_ulong newval = (env->mstatus & ~mask) | (val & mask);
> >>       return write_mstatus(env, CSR_MSTATUS, newval);
> >>   }
> >> @@ -4001,6 +4132,10 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
> >>       /* Crypto Extension */
> >>       [CSR_SEED] = { "seed", seed, NULL, NULL, rmw_seed },
> >>   +    /* User mode CFI CSR */
> >> +    [CSR_LPLR] = { "lplr", cfi, read_lplr, write_lplr },
> >> +    [CSR_SSP]  = { "ssp", cfi, read_ssp, write_ssp },
> >> +
> >>   #if !defined(CONFIG_USER_ONLY)
> >>       /* Machine Timers and Counters */
> >>       [CSR_MCYCLE]    = { "mcycle",    any,   read_hpmcounter,
> >> diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c
> >> index d1126a6066..89745d46cd 100644
> >> --- a/target/riscv/pmp.c
> >> +++ b/target/riscv/pmp.c
> >> @@ -579,6 +579,15 @@ void mseccfg_csr_write(CPURISCVState *env,
> >> target_ulong val)
> >>       /* Sticky bits */
> >>       val |= (env->mseccfg & (MSECCFG_MMWP | MSECCFG_MML));
> >>   +    /* M-mode forward cfi to be enabled if cfi extension is
> >> implemented */
> >> +    if (env_archcpu(env)->cfg.ext_cfi) {
> >> +        val |= (val & MSECCFG_MFCFIEN);
> > This statement does nothing. Is it a typo?
> >> +        /* If forward cfi in mseccfg is being toggled, flush tlb */
> >> +        if ((env->mseccfg ^ val) & MSECCFG_MFCFIEN) {
> >> +                tlb_flush(env_cpu(env));
> >> +        }
> >
> > No need flush tlb.
> >
> > Zhiwei
> >
> >> +    }
> >> +
> >>       env->mseccfg = val;
> >>   }


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v1 RFC Zisslpcfi 6/9] target/riscv: MMU changes for back cfi's shadow stack
  2023-02-15  8:43   ` LIU Zhiwei
@ 2023-02-15 23:57     ` Deepak Gupta
  2023-02-16  2:36       ` LIU Zhiwei
  0 siblings, 1 reply; 42+ messages in thread
From: Deepak Gupta @ 2023-02-15 23:57 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, liweiwei, dbarboza,
	Kip Walker, qemu-riscv, qemu-devel

`On Wed, Feb 15, 2023 at 12:43 AM LIU Zhiwei
<zhiwei_liu@linux.alibaba.com> wrote:
>
>
> On 2023/2/9 14:24, Deepak Gupta wrote:
> > zisslpcfi protects returns(back cfi) using shadow stack. If compiled with
> > enabled compiler, function prologs will have `sspush ra` instruction to
> > push return address on shadow stack and function epilogs will have
> > `sspop t0; sschckra` instruction sequences. `sspop t0` will pop the
> > value from top of the shadow stack in t0. `sschckra` will compare `t0`
> > and `x1` and if they don't match then hart will raise an illegal
> > instruction exception.
> >
> > Shadow stack is read-only memory except stores can be performed via
> > `sspush` and `ssamoswap` instructions. This requires new PTE encoding for
> > shadow stack. zisslpcfi uses R=0, W=1, X=0 (an existing reserved encoding
> > ) to encode a shadow stack. If backward cfi is not enabled for current
> > mode, shadow stack PTE encodings remain reserved. Regular stores to
> > shadow stack raise AMO/store access fault. Shadow stack loads/stores on
> > regular memory raise load access/store access fault.
> >
> > This patch creates a new MMU TLB index for shadow stack and flushes TLB
> > for shadow stack on privileges changes. This patch doesn't implement
> > `Smepmp` related enforcement on shadow stack pmp entry. Reason being qemu
> > doesn't have `Smepmp` implementation yet.
> I don't know that the Smepmp means here. QEMU has supported the epmp.

https://github.com/riscv/riscv-tee/blob/main/Smepmp/Smepmp.pdf

> >   `Smepmp` enforcement should come
> > whenever it is implemented.
> >
> > Signed-off-by: Deepak Gupta <debug@rivosinc.com>
> > Signed-off-by: Kip Walker  <kip@rivosinc.com>
> > ---
> >   target/riscv/cpu-param.h  |   1 +
> >   target/riscv/cpu.c        |   2 +
> >   target/riscv/cpu.h        |   3 ++
> >   target/riscv/cpu_helper.c | 107 +++++++++++++++++++++++++++++++-------
> >   4 files changed, 94 insertions(+), 19 deletions(-)
> >
> > diff --git a/target/riscv/cpu-param.h b/target/riscv/cpu-param.h
> > index ebaf26d26d..a1e379beb7 100644
> > --- a/target/riscv/cpu-param.h
> > +++ b/target/riscv/cpu-param.h
> > @@ -25,6 +25,7 @@
> >    *  - M mode 0b011
> >    *  - U mode HLV/HLVX/HSV 0b100
> >    *  - S mode HLV/HLVX/HSV 0b101
> > + *  - BCFI shadow stack   0b110
> >    *  - M mode HLV/HLVX/HSV 0b111
> >    */
> >   #define NB_MMU_MODES 8
> > diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> > index 6b4e90eb91..14cfb93288 100644
> > --- a/target/riscv/cpu.c
> > +++ b/target/riscv/cpu.c
> > @@ -584,6 +584,8 @@ static void riscv_cpu_reset_hold(Object *obj)
> >       }
> >       /* mmte is supposed to have pm.current hardwired to 1 */
> >       env->mmte |= (PM_EXT_INITIAL | MMTE_M_PM_CURRENT);
> > +    /* Initialize ss_priv to current priv. */
> > +    env->ss_priv = env->priv;
> >   #endif
> >       env->xl = riscv_cpu_mxl(env);
> >       riscv_cpu_update_mask(env);
> > diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> > index d14ea4f91d..8803ea6426 100644
> > --- a/target/riscv/cpu.h
> > +++ b/target/riscv/cpu.h
> > @@ -379,6 +379,7 @@ struct CPUArchState {
> >       uint64_t sstateen[SMSTATEEN_MAX_COUNT];
> >       target_ulong senvcfg;
> >       uint64_t henvcfg;
> > +    target_ulong ss_priv;
> >   #endif
> >       target_ulong cur_pmmask;
> >       target_ulong cur_pmbase;
> > @@ -617,6 +618,8 @@ void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong);
> >   #define TB_FLAGS_PRIV_HYP_ACCESS_MASK   (1 << 2)
> >   #define TB_FLAGS_MSTATUS_FS MSTATUS_FS
> >   #define TB_FLAGS_MSTATUS_VS MSTATUS_VS
> > +/* TLB MMU index for shadow stack accesses */
> > +#define MMU_IDX_SS_ACCESS    6
> >
> >   #include "exec/cpu-all.h"
> >
> > diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> > index fc188683c9..63377abc2f 100644
> > --- a/target/riscv/cpu_helper.c
> > +++ b/target/riscv/cpu_helper.c
> > @@ -657,7 +657,8 @@ void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable)
> >
> >   bool riscv_cpu_two_stage_lookup(int mmu_idx)
> >   {
> > -    return mmu_idx & TB_FLAGS_PRIV_HYP_ACCESS_MASK;
> > +    return (mmu_idx & TB_FLAGS_PRIV_HYP_ACCESS_MASK) &&
> > +           (mmu_idx != MMU_IDX_SS_ACCESS);
> >   }
> >
> >   int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts)
> > @@ -745,6 +746,38 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv)
> >        * preemptive context switch. As a result, do both.
> >        */
> >       env->load_res = -1;
> > +
> > +    if (cpu_get_bcfien(env) && (env->priv != env->ss_priv)) {
> > +        /*
> > +         * If backward CFI is enabled in the new privilege state, the
> > +         * shadow stack TLB needs to be flushed - unless the most recent
> > +         * use of the SS TLB was for the same privilege mode.
> > +         */
> > +        tlb_flush_by_mmuidx(env_cpu(env), 1 << MMU_IDX_SS_ACCESS);
> > +        /*
> > +         * Ignoring env->virt here since currently every time it flips,
> > +         * all TLBs are flushed anyway.
> > +         */
> > +        env->ss_priv = env->priv;
> > +    }
> > +}
> > +
> > +typedef enum {
> > +    SSTACK_NO,          /* Access is not for a shadow stack instruction */
> > +    SSTACK_YES,         /* Access is for a shadow stack instruction */
> > +    SSTACK_DC           /* Don't care about SS attribute in PMP */
> > +} SStackPmpMode;
> > +
> > +static bool legal_sstack_access(int access_type, bool sstack_inst,
> > +                                bool sstack_attribute)
> > +{
> > +    /*
> > +     * Read/write/execution permissions are checked as usual. Shadow
> > +     * stack enforcement is just that (1) instruction type must match
> > +     * the attribute unless (2) a non-SS load to an SS region.
> > +     */
> > +    return (sstack_inst == sstack_attribute) ||
> > +        ((access_type == MMU_DATA_LOAD) && sstack_attribute);
> >   }
> >
> >   /*
> > @@ -764,7 +797,7 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv)
> >   static int get_physical_address_pmp(CPURISCVState *env, int *prot,
> >                                       target_ulong *tlb_size, hwaddr addr,
> >                                       int size, MMUAccessType access_type,
> > -                                    int mode)
> > +                                    int mode, SStackPmpMode sstack)
> Why this parameter if you don't use it?
> >   {
> >       pmp_priv_t pmp_priv;
> >       int pmp_index = -1;
> > @@ -812,13 +845,16 @@ static int get_physical_address_pmp(CPURISCVState *env, int *prot,
> >    *               Second stage is used for hypervisor guest translation
> >    * @two_stage: Are we going to perform two stage translation
> >    * @is_debug: Is this access from a debugger or the monitor?
> > + * @sstack: Is this access for a shadow stack? Passed by reference so
> > +            it can be forced to SSTACK_DC when the SS check is completed
> > +            based on a PTE - so the PMP SS attribute will be ignored.
> >    */
> >   static int get_physical_address(CPURISCVState *env, hwaddr *physical,
> >                                   int *prot, target_ulong addr,
> >                                   target_ulong *fault_pte_addr,
> >                                   int access_type, int mmu_idx,
> >                                   bool first_stage, bool two_stage,
> > -                                bool is_debug)
> > +                                bool is_debug, SStackPmpMode *sstack)
> >   {
> >       /* NOTE: the env->pc value visible here will not be
> >        * correct, but the value visible to the exception handler
> > @@ -830,6 +866,7 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical,
> >       hwaddr ppn;
> >       RISCVCPU *cpu = env_archcpu(env);
> >       int napot_bits = 0;
> > +    bool is_sstack = (sstack != NULL) && (*sstack == SSTACK_YES);
> >       target_ulong napot_mask;
> >
> >       /*
> > @@ -851,6 +888,8 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical,
> >           if (get_field(env->mstatus, MSTATUS_MPRV)) {
> >               mode = get_field(env->mstatus, MSTATUS_MPP);
> >           }
> > +    } else if (mmu_idx == MMU_IDX_SS_ACCESS) {
> > +        mode = env->priv;
> >       }
> >
> >       if (first_stage == false) {
> > @@ -966,7 +1005,7 @@ restart:
> >               int vbase_ret = get_physical_address(env, &vbase, &vbase_prot,
> >                                                    base, NULL, MMU_DATA_LOAD,
> >                                                    mmu_idx, false, true,
> > -                                                 is_debug);
> > +                                                 is_debug, NULL);
> >
> >               if (vbase_ret != TRANSLATE_SUCCESS) {
> >                   if (fault_pte_addr) {
> > @@ -983,7 +1022,7 @@ restart:
> >           int pmp_prot;
> >           int pmp_ret = get_physical_address_pmp(env, &pmp_prot, NULL, pte_addr,
> >                                                  sizeof(target_ulong),
> > -                                               MMU_DATA_LOAD, PRV_S);
> > +                                               MMU_DATA_LOAD, PRV_S, SSTACK_NO);
> >           if (pmp_ret != TRANSLATE_SUCCESS) {
> >               return TRANSLATE_PMP_FAIL;
> >           }
> > @@ -1010,6 +1049,18 @@ restart:
> >               }
> >           }
> >
> > +        /*
> > +         * When backward CFI is enabled, the R=0, W=1, X=0 reserved encoding
> > +         * is used to mark Shadow Stack (SS) pages. If back CFI enabled, allow
> > +         * normal loads on SS pages, regular stores raise store access fault
> > +         * and avoid hitting the reserved-encoding case. Only shadow stack
> > +         * stores are allowed on SS pages. Shadow stack loads and stores on
> > +         * regular memory (non-SS) raise load and store/AMO access fault.
> > +         * Second stage translations don't participate in Shadow Stack.
> > +         */
> > +        bool sstack_page = (cpu_get_bcfien(env) && first_stage &&
> > +                            ((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W));
> > +
> >           if (!(pte & PTE_V)) {
> >               /* Invalid PTE */
> >               return TRANSLATE_FAIL;
> > @@ -1021,7 +1072,7 @@ restart:
> >                   return TRANSLATE_FAIL;
> >               }
> >               base = ppn << PGSHIFT;
> > -        } else if ((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W) {
> > +        } else if (((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W) && !sstack_page) {
> >               /* Reserved leaf PTE flags: PTE_W */
> >               return TRANSLATE_FAIL;
> >           } else if ((pte & (PTE_R | PTE_W | PTE_X)) == (PTE_W | PTE_X)) {
> > @@ -1038,16 +1089,21 @@ restart:
> >           } else if (ppn & ((1ULL << ptshift) - 1)) {
> >               /* Misaligned PPN */
> >               return TRANSLATE_FAIL;
> > -        } else if (access_type == MMU_DATA_LOAD && !((pte & PTE_R) ||
> > -                   ((pte & PTE_X) && mxr))) {
> > +        } else if (access_type == MMU_DATA_LOAD && !(((pte & PTE_R) ||
> > +                   sstack_page) || ((pte & PTE_X) && mxr))) {
> >               /* Read access check failed */
> >               return TRANSLATE_FAIL;
> > -        } else if (access_type == MMU_DATA_STORE && !(pte & PTE_W)) {
> > +        } else if ((access_type == MMU_DATA_STORE && !is_sstack) &&
> > +                   !(pte & PTE_W)) {
> Why limit to !is_sstack? Even is_sstack, we should make sure
>
> (access_type == MMU_DATA_STORE && !(pte & PTE_W)
>
> fails.

As per spec if a shadow stack store happens to a memory which is not a
shadow stack memory then cpu must raise
access store fault. This failure here converts to a page fault.
TRANSLATE_PMP_FAIL is the one which converts to
access faults.  So this check here ensures that legacy behavior is
maintained i.e.
"all store accesses which are not shadow stack stores and if W is not
set then they convert to store page faults"

Few lines down there is a call to `legal_sstack_access` which actually
does the logic check of
"If a regular store happened on shadow stack memory, returns false"
"If a shadow stack access happened on regular memory, returns false"
And this check returns PMP_TRANSLATE_FAIL which converts to access faults.

On a very high level, shadow stack accesses (sspush/sspop/ssamoswap)
to regular memory result in access faults.
Regular store to shadow stack memory result in store/AMO access fault.
Regular load to shadow stack memory is allowed.

Let me know if this was clear.

> >               /* Write access check failed */
> >               return TRANSLATE_FAIL;
> >           } else if (access_type == MMU_INST_FETCH && !(pte & PTE_X)) {
> >               /* Fetch access check failed */
> >               return TRANSLATE_FAIL;
> > +        } else if (!legal_sstack_access(access_type, is_sstack,
> > +                                        sstack_page)) {
> > +            /* Illegal combo of instruction type and page attribute */
> > +            return TRANSLATE_PMP_FAIL;
> Not sure about this. Does the cfi escape the pmp check?
> >           } else {
> >               /* if necessary, set accessed and dirty bits. */
> >               target_ulong updated_pte = pte | PTE_A |
> > @@ -1107,18 +1163,27 @@ restart:
> >                            ) << PGSHIFT) | (addr & ~TARGET_PAGE_MASK);
> >
> >               /* set permissions on the TLB entry */
> > -            if ((pte & PTE_R) || ((pte & PTE_X) && mxr)) {
> > +            if ((pte & PTE_R) || ((pte & PTE_X) && mxr) || sstack_page) {
>
> I see that we should add the PAGE_READ for sstack_page, such as for a
> no-SS load.

I didn't get this comment. Can you clarify a bit more?

>
> Zhiwei
>
> >                   *prot |= PAGE_READ;
> >               }
> >               if ((pte & PTE_X)) {
> >                   *prot |= PAGE_EXEC;
> >               }
> > -            /* add write permission on stores or if the page is already dirty,
> > -               so that we TLB miss on later writes to update the dirty bit */
> > +            /*
> > +             * add write permission on stores or if the page is already dirty,
> > +             * so that we TLB miss on later writes to update the dirty bit
> > +             */
> >               if ((pte & PTE_W) &&
> >                       (access_type == MMU_DATA_STORE || (pte & PTE_D))) {
> >                   *prot |= PAGE_WRITE;
> >               }
> > +            if (sstack) {
> > +                /*
> > +                 * Tell the caller to skip the SS bit in the PMP since we
> > +                 * resolved the attributes via the page table.
> > +                 */
> > +                *sstack = SSTACK_DC;
> > +            }
> >               return TRANSLATE_SUCCESS;
> >           }
> >       }
> > @@ -1190,13 +1255,13 @@ hwaddr riscv_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
> >       int mmu_idx = cpu_mmu_index(&cpu->env, false);
> >
> >       if (get_physical_address(env, &phys_addr, &prot, addr, NULL, 0, mmu_idx,
> > -                             true, riscv_cpu_virt_enabled(env), true)) {
> > +                             true, riscv_cpu_virt_enabled(env), true, NULL)) {
> >           return -1;
> >       }
> >
> >       if (riscv_cpu_virt_enabled(env)) {
> >           if (get_physical_address(env, &phys_addr, &prot, phys_addr, NULL,
> > -                                 0, mmu_idx, false, true, true)) {
> > +                                 0, mmu_idx, false, true, true, NULL)) {
> >               return -1;
> >           }
> >       }
> > @@ -1291,6 +1356,8 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
> >       bool two_stage_indirect_error = false;
> >       int ret = TRANSLATE_FAIL;
> >       int mode = mmu_idx;
> > +    bool sstack = (mmu_idx == MMU_IDX_SS_ACCESS);
> > +    SStackPmpMode ssmode = sstack ? SSTACK_YES : SSTACK_NO;
> >       /* default TLB page size */
> >       target_ulong tlb_size = TARGET_PAGE_SIZE;
> >
> > @@ -1318,7 +1385,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
> >           /* Two stage lookup */
> >           ret = get_physical_address(env, &pa, &prot, address,
> >                                      &env->guest_phys_fault_addr, access_type,
> > -                                   mmu_idx, true, true, false);
> > +                                   mmu_idx, true, true, false, &ssmode);
> >
> >           /*
> >            * A G-stage exception may be triggered during two state lookup.
> > @@ -1342,7 +1409,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
> >
> >               ret = get_physical_address(env, &pa, &prot2, im_address, NULL,
> >                                          access_type, mmu_idx, false, true,
> > -                                       false);
> > +                                       false, NULL);
> >
> >               qemu_log_mask(CPU_LOG_MMU,
> >                       "%s 2nd-stage address=%" VADDR_PRIx " ret %d physical "
> > @@ -1353,7 +1420,8 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
> >
> >               if (ret == TRANSLATE_SUCCESS) {
> >                   ret = get_physical_address_pmp(env, &prot_pmp, &tlb_size, pa,
> > -                                               size, access_type, mode);
> > +                                               size, access_type, mode,
> > +                                               SSTACK_NO);
> >
> >                   qemu_log_mask(CPU_LOG_MMU,
> >                                 "%s PMP address=" HWADDR_FMT_plx " ret %d prot"
> > @@ -1377,7 +1445,8 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
> >       } else {
> >           /* Single stage lookup */
> >           ret = get_physical_address(env, &pa, &prot, address, NULL,
> > -                                   access_type, mmu_idx, true, false, false);
> > +                                   access_type, mmu_idx, true, false,
> > +                                   false, &ssmode);
> >
> >           qemu_log_mask(CPU_LOG_MMU,
> >                         "%s address=%" VADDR_PRIx " ret %d physical "
> > @@ -1386,7 +1455,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
> >
> >           if (ret == TRANSLATE_SUCCESS) {
> >               ret = get_physical_address_pmp(env, &prot_pmp, &tlb_size, pa,
> > -                                           size, access_type, mode);
> > +                                           size, access_type, mode, ssmode);
> >
> >               qemu_log_mask(CPU_LOG_MMU,
> >                             "%s PMP address=" HWADDR_FMT_plx " ret %d prot"


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v1 RFC Zisslpcfi 7/9] target/riscv: Tracking indirect branches (fcfi) using TCG
  2023-02-15  8:55   ` LIU Zhiwei
@ 2023-02-16  0:02     ` Deepak Gupta
  2023-02-16  2:43       ` LIU Zhiwei
  0 siblings, 1 reply; 42+ messages in thread
From: Deepak Gupta @ 2023-02-16  0:02 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, liweiwei, dbarboza,
	Kip Walker, qemu-riscv, qemu-devel

On Wed, Feb 15, 2023 at 12:55 AM LIU Zhiwei
<zhiwei_liu@linux.alibaba.com> wrote:
>
>
> On 2023/2/9 14:24, Deepak Gupta wrote:
> > zisslpcfi protects forward control flow (if enabled) by enforcing all
> > indirect call and jmp must land on a landing pad instruction `lpcll`
> > short for landing pad and check lower label value. If target of an
> > indirect call or jmp is not `lpcll` then cpu/hart must raise an illegal
> > instruction exception.
> >
> > This patch implements the mechanism using TCG. Target architecture branch
> > instruction must define the end of a TB. Using this property, during
> > translation of branch instruction, TB flag = FCFI_LP_EXPECTED can be set.
> > Translation of target TB can check if FCFI_LP_EXPECTED flag is set and a
> > flag (fcfi_lp_expected) can be set in DisasContext. If `lpcll` gets
> > translated, fcfi_lp_expected flag in DisasContext can be cleared. Else
> > it'll fault.
> >
> > This patch also also adds flag for forward and backward cfi in
> > DisasContext.
> >
> > Signed-off-by: Deepak Gupta <debug@rivosinc.com>
> > Signed-off-by: Kip Walker  <kip@rivosinc.com>
> > ---
> >   target/riscv/cpu.h        |  3 +++
> >   target/riscv/cpu_helper.c | 12 +++++++++
> >   target/riscv/translate.c  | 52 +++++++++++++++++++++++++++++++++++++++
> >   3 files changed, 67 insertions(+)
> >
> > diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> > index 8803ea6426..98b272bcad 100644
> > --- a/target/riscv/cpu.h
> > +++ b/target/riscv/cpu.h
> > @@ -644,6 +644,9 @@ FIELD(TB_FLAGS, VMA, 25, 1)
> >   /* Native debug itrigger */
> >   FIELD(TB_FLAGS, ITRIGGER, 26, 1)
> >
> > +/* Zisslpcfi needs a TB flag to track indirect branches */
> > +FIELD(TB_FLAGS, FCFI_LP_EXPECTED, 27, 1)
> > +
> >   #ifdef TARGET_RISCV32
> >   #define riscv_cpu_mxl(env)  ((void)(env), MXL_RV32)
> >   #else
> > diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> > index 63377abc2f..d15918f534 100644
> > --- a/target/riscv/cpu_helper.c
> > +++ b/target/riscv/cpu_helper.c
> > @@ -129,6 +129,18 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc,
> >           flags = FIELD_DP32(flags, TB_FLAGS, VILL, 1);
> >       }
> >
> > +    if (cpu->cfg.ext_cfi) {
> > +        /*
> > +         * For Forward CFI, only the expectation of a lpcll at
> > +         * the start of the block is tracked (which can only happen
> > +         * when FCFI is enabled for the current processor mode). A jump
> > +         * or call at the end of the previous TB will have updated
> > +         * env->elp to indicate the expectation.
> > +         */
> > +        flags = FIELD_DP32(flags, TB_FLAGS, FCFI_LP_EXPECTED,
> > +                           env->elp != NO_LP_EXPECTED);
> > +    }
> > +
> >   #ifdef CONFIG_USER_ONLY
> >       flags |= TB_FLAGS_MSTATUS_FS;
> >       flags |= TB_FLAGS_MSTATUS_VS;
> > diff --git a/target/riscv/translate.c b/target/riscv/translate.c
> > index df38db7553..7d43d20fc3 100644
> > --- a/target/riscv/translate.c
> > +++ b/target/riscv/translate.c
> > @@ -41,6 +41,7 @@ static TCGv load_val;
> >   /* globals for PM CSRs */
> >   static TCGv pm_mask;
> >   static TCGv pm_base;
> > +static TCGOp *cfi_lp_check;
> >
> >   #include "exec/gen-icount.h"
> >
> > @@ -116,6 +117,10 @@ typedef struct DisasContext {
> >       bool itrigger;
> >       /* TCG of the current insn_start */
> >       TCGOp *insn_start;
> > +    /* CFI extension */
> > +    bool bcfi_enabled;
> > +    bool fcfi_enabled;
> > +    bool fcfi_lp_expected;
> >   } DisasContext;
> >
> >   static inline bool has_ext(DisasContext *ctx, uint32_t ext)
> > @@ -1166,11 +1171,44 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
> >       ctx->pm_mask_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_MASK_ENABLED);
> >       ctx->pm_base_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_BASE_ENABLED);
> >       ctx->itrigger = FIELD_EX32(tb_flags, TB_FLAGS, ITRIGGER);
> > +    ctx->bcfi_enabled = cpu_get_bcfien(env);
> > +    ctx->fcfi_enabled = cpu_get_fcfien(env);
> This is wrong.  If you ctx->bcfi_enabled in the translation and don't
> put it in a tb flags field, the translated tb will
> be misused.

TLB for shadow stack index is flushed on privilege transfers.
All TLBs is flushed whenever enable/disable bits for shadow stack are toggled.
Can you elaborate a bit more how this can be misused?

>
>
> Zhiwei
>
> > +    ctx->fcfi_lp_expected = FIELD_EX32(tb_flags, TB_FLAGS, FCFI_LP_EXPECTED);
> >       ctx->zero = tcg_constant_tl(0);
> >   }
> >
> >   static void riscv_tr_tb_start(DisasContextBase *db, CPUState *cpu)
> >   {
> > +    DisasContext *ctx = container_of(db, DisasContext, base);
> > +
> > +    if (ctx->fcfi_lp_expected) {
> > +        /*
> > +         * Since we can't look ahead to confirm that the first
> > +         * instruction is a legal landing pad instruction, emit
> > +         * compare-and-branch sequence that will be fixed-up in
> > +         * riscv_tr_tb_stop() to either statically hit or skip an
> > +         * illegal instruction exception depending on whether the
> > +         * flag was lowered by translation of a CJLP or JLP as
> > +         * the first instruction in the block.
> > +         */
> > +        TCGv_i32 immediate;
> > +        TCGLabel *l;
> > +        l = gen_new_label();
> > +        immediate = tcg_temp_local_new_i32();
> > +        tcg_gen_movi_i32(immediate, 0);
> > +        cfi_lp_check = tcg_last_op();
> > +        tcg_gen_brcondi_i32(TCG_COND_EQ, immediate, 0, l);
> > +        tcg_temp_free_i32(immediate);
> > +        gen_exception_illegal(ctx);
> > +        gen_set_label(l);
> > +        /*
> > +         * Despite the use of gen_exception_illegal(), the rest of
> > +         * the TB needs to be generated. The TCG optimizer will
> > +         * clean things up depending on which path ends up being
> > +         * active.
> > +         */
> > +        ctx->base.is_jmp = DISAS_NEXT;
> > +    }
> >   }
> >
> >   static void riscv_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu)
> > @@ -1225,6 +1263,7 @@ static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
> >   static void riscv_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
> >   {
> >       DisasContext *ctx = container_of(dcbase, DisasContext, base);
> > +    CPURISCVState *env = cpu->env_ptr;
> >
> >       switch (ctx->base.is_jmp) {
> >       case DISAS_TOO_MANY:
> > @@ -1235,6 +1274,19 @@ static void riscv_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
> >       default:
> >           g_assert_not_reached();
> >       }
> > +
> > +    if (ctx->fcfi_lp_expected) {
> > +        /*
> > +         * If the "lp expected" flag is still up, the block needs to take an
> > +         * illegal instruction exception.
> > +         */
> > +        tcg_set_insn_param(cfi_lp_check, 1, tcgv_i32_arg(tcg_constant_i32(1)));
> > +    } else {
> > +        /*
> > +        * LP instruction requirement was met, clear up LP expected
> > +        */
> > +        env->elp = NO_LP_EXPECTED;
> > +    }
> >   }
> >
> >   static void riscv_tr_disas_log(const DisasContextBase *dcbase,


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v1 RFC Zisslpcfi 3/9] target/riscv: implements CSRs and new bits in existing CSRs in zisslpcfi
  2023-02-15 23:33     ` Deepak Gupta
@ 2023-02-16  0:02       ` Richard Henderson
  2023-02-16  1:38         ` Deepak Gupta
  0 siblings, 1 reply; 42+ messages in thread
From: Richard Henderson @ 2023-02-16  0:02 UTC (permalink / raw)
  To: Deepak Gupta, LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, liweiwei, dbarboza,
	Kip Walker, qemu-riscv, qemu-devel

On 2/15/23 13:33, Deepak Gupta wrote:
> On Tue, Feb 14, 2023 at 9:47 PM LIU Zhiwei <zhiwei_liu@linux.alibaba.com> wrote:
>> And MSTATUS_UBCFIEN field change don't need flush tlb.
>>
> 
> TCG code-gen would be different depending on whether ubcfi is enabled or not.
> As an example a TB might have code generated when bcfi was enabled.
> But if someone disables it,
> translation needs to happen again and zimops implementation should be
> generated this time.

tlb_flush does not affect translation.  TB are tied to physical addresses and are only 
flushed by writes or tb_flush().

The correct fix is to allocate a bit from FIELD(TB_FLAGS, X, Y, 1), and adjust 
cpu_get_tb_cpu_state to indicate when CFI is active in the current cpu mode.


r~


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v1 RFC Zisslpcfi 3/9] target/riscv: implements CSRs and new bits in existing CSRs in zisslpcfi
  2023-02-16  0:02       ` Richard Henderson
@ 2023-02-16  1:38         ` Deepak Gupta
  2023-02-16  2:43           ` Richard Henderson
  0 siblings, 1 reply; 42+ messages in thread
From: Deepak Gupta @ 2023-02-16  1:38 UTC (permalink / raw)
  To: Richard Henderson
  Cc: LIU Zhiwei, Palmer Dabbelt, Alistair Francis, Bin Meng, liweiwei,
	dbarboza, Kip Walker, qemu-riscv, qemu-devel

On Wed, Feb 15, 2023 at 4:02 PM Richard Henderson
<richard.henderson@linaro.org> wrote:
>
> On 2/15/23 13:33, Deepak Gupta wrote:
> > On Tue, Feb 14, 2023 at 9:47 PM LIU Zhiwei <zhiwei_liu@linux.alibaba.com> wrote:
> >> And MSTATUS_UBCFIEN field change don't need flush tlb.
> >>
> >
> > TCG code-gen would be different depending on whether ubcfi is enabled or not.
> > As an example a TB might have code generated when bcfi was enabled.
> > But if someone disables it,
> > translation needs to happen again and zimops implementation should be
> > generated this time.
>
> tlb_flush does not affect translation.  TB are tied to physical addresses and are only
> flushed by writes or tb_flush().
>
> The correct fix is to allocate a bit from FIELD(TB_FLAGS, X, Y, 1), and adjust
> cpu_get_tb_cpu_state to indicate when CFI is active in the current cpu mode.
>
>

Hmm... So this looks like a major oversight on my side.
I had been under the impression that tlb flush does TB flushes too.
I was trying to save on TB_FLAGS.
I saw `tb_jmp_cache` was being cleared, didn't dig deep and assumed
that tlb flush clears up TB as well.
Now that you've pointed it out, it looks like that's a different optimization.

So looks like this definitely needs a fix.

Question:
I'll basically need two bits (one for forward cfi and one for backward cfi).
But I need to throw away the TB if cfi enabling bits mismatch at the
time TB was generated and the current state of enabling bits.
Reason being, this needs to get translated again and zimops need to be
generated.

What's the best way to throw away a single TB?


> r~


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v1 RFC Zisslpcfi 1/9] target/riscv: adding zimops and zisslpcfi extension to RISCV cpu config
  2023-02-15 20:47     ` Deepak Gupta
@ 2023-02-16  1:46       ` LIU Zhiwei
  2023-02-16  4:20         ` Richard Henderson
  0 siblings, 1 reply; 42+ messages in thread
From: LIU Zhiwei @ 2023-02-16  1:46 UTC (permalink / raw)
  To: Deepak Gupta
  Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, liweiwei, dbarboza,
	Kip Walker, qemu-riscv, qemu-devel


On 2023/2/16 4:47, Deepak Gupta wrote:
> On Tue, Feb 14, 2023 at 6:52 PM LIU Zhiwei <zhiwei_liu@linux.alibaba.com> wrote:
>>
>> On 2023/2/9 14:23, Deepak Gupta wrote:
>>> Introducing riscv `zisslpcfi` extension to riscv target. `zisslpcfi`
>>> extension provides hardware assistance to riscv hart to enable control
>>> flow integrity (CFI) for software.
>>>
>>> `zisslpcfi` extension expects hart to implement `zimops`. `zimops` stands
>>> for "unprivileged integer maybe operations". `zimops` carve out certain
>>> reserved opcodes encodings from integer spec to "may be operations"
>>> encodings. `zimops` opcode encodings simply move 0 to rd.
>>> `zisslpcfi` claims some of the `zimops` encodings and use them for shadow
>>> stack management or indirect branch tracking. Any future extension can
>>> also claim `zimops` encodings.
>> Does  the zimops has a independent specification? If so, you should give
>> a link to this
>> specification.
> Actual formal documentation is still a work in progress.
> I am hoping to provide a reference to it in my next iteration.
>
>>> This patch also adds a dependency check for `zimops` to be enabled if
>>> `zisslpcfi` is enabled on the hart.
>> You should don't add two extensions in one patch. I think you should add
>> them one by one.
>> And add the zimop first.  In my opinion, you should implement the whole
>> zimop extension before
>> adding any patch for zisslpcfi, including the implementation of mop.rr
>> and mop.r.
> Noted will make sure of that and will send two different patch series then.

We can rearrange the patch set as follows:

1. Implement the zimop extension.

2. Implement the forward cfi only for system mode.

3. Implement the backward cfi only for system mode.

4. Carefully make the forward cfi can work on user mode.

5. Carefully make the backward cfi work for user mode.

I don't think we can easily make cfi  work on user mode. So we can also 
ignore the 4 or 5, or both.

Thus, we don't have to implement a patch like the patch 8, which is too 
big to review.


Zhiwei

>
>>> Signed-off-by: Deepak Gupta <debug@rivosinc.com>
>>> Signed-off-by: Kip Walker  <kip@rivosinc.com>
>>> ---
>>>    target/riscv/cpu.c | 13 +++++++++++++
>>>    target/riscv/cpu.h |  2 ++
>>>    2 files changed, 15 insertions(+)
>>>
>>> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
>>> index cc75ca7667..6b4e90eb91 100644
>>> --- a/target/riscv/cpu.c
>>> +++ b/target/riscv/cpu.c
>>> @@ -110,6 +110,8 @@ static const struct isa_ext_data isa_edata_arr[] = {
>>>        ISA_EXT_DATA_ENTRY(svnapot, true, PRIV_VERSION_1_12_0, ext_svnapot),
>>>        ISA_EXT_DATA_ENTRY(svpbmt, true, PRIV_VERSION_1_12_0, ext_svpbmt),
>>>        ISA_EXT_DATA_ENTRY(xventanacondops, true, PRIV_VERSION_1_12_0, ext_XVentanaCondOps),
>>> +    ISA_EXT_DATA_ENTRY(zimops, true, PRIV_VERSION_1_12_0, ext_zimops),
>>> +    ISA_EXT_DATA_ENTRY(zisslpcfi, true, PRIV_VERSION_1_12_0, ext_cfi),
>> Add them one by one.
>>>    };
>>>
>>>    static bool isa_ext_is_enabled(RISCVCPU *cpu,
>>> @@ -792,6 +794,11 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
>>>                return;
>>>            }
>>>
>>> +        if (cpu->cfg.ext_cfi && !cpu->cfg.ext_zimops) {
>>> +            error_setg(errp, "Zisslpcfi extension requires Zimops extension");
>>> +            return;
>>> +        }
>>> +
>> Seems reasonable for me.
>>>            /* Set the ISA extensions, checks should have happened above */
>>>            if (cpu->cfg.ext_zdinx || cpu->cfg.ext_zhinx ||
>>>                cpu->cfg.ext_zhinxmin) {
>>> @@ -1102,6 +1109,12 @@ static Property riscv_cpu_properties[] = {
>>>    #ifndef CONFIG_USER_ONLY
>>>        DEFINE_PROP_UINT64("resetvec", RISCVCPU, env.resetvec, DEFAULT_RSTVEC),
>>>    #endif
>>> +    /*
>>> +     * Zisslpcfi CFI extension, Zisslpcfi implicitly means Zimops is
>>> +     * implemented
>>> +     */
>>> +    DEFINE_PROP_BOOL("zisslpcfi", RISCVCPU, cfg.ext_cfi, true),
>>> +    DEFINE_PROP_BOOL("zimops", RISCVCPU, cfg.ext_zimops, true),
>> Default value should be false.
> Yes, I have to fix this.
>
>> Zhiwei
>>
>>>        DEFINE_PROP_BOOL("short-isa-string", RISCVCPU, cfg.short_isa_string, false),
>>>
>>> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
>>> index f5609b62a2..9a923760b2 100644
>>> --- a/target/riscv/cpu.h
>>> +++ b/target/riscv/cpu.h
>>> @@ -471,6 +471,8 @@ struct RISCVCPUConfig {
>>>        uint32_t mvendorid;
>>>        uint64_t marchid;
>>>        uint64_t mimpid;
>>> +    bool ext_zimops;
>>> +    bool ext_cfi;
>>>
>>>        /* Vendor-specific custom extensions */
>>>        bool ext_XVentanaCondOps;


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v1 RFC Zisslpcfi 6/9] target/riscv: MMU changes for back cfi's shadow stack
  2023-02-15 23:57     ` Deepak Gupta
@ 2023-02-16  2:36       ` LIU Zhiwei
  2023-02-16  5:43         ` Deepak Gupta
  0 siblings, 1 reply; 42+ messages in thread
From: LIU Zhiwei @ 2023-02-16  2:36 UTC (permalink / raw)
  To: Deepak Gupta
  Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, liweiwei, dbarboza,
	Kip Walker, qemu-riscv, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 19472 bytes --]


On 2023/2/16 7:57, Deepak Gupta wrote:
> `On Wed, Feb 15, 2023 at 12:43 AM LIU Zhiwei
> <zhiwei_liu@linux.alibaba.com>  wrote:
>>
>> On 2023/2/9 14:24, Deepak Gupta wrote:
>>> zisslpcfi protects returns(back cfi) using shadow stack. If compiled with
>>> enabled compiler, function prologs will have `sspush ra` instruction to
>>> push return address on shadow stack and function epilogs will have
>>> `sspop t0; sschckra` instruction sequences. `sspop t0` will pop the
>>> value from top of the shadow stack in t0. `sschckra` will compare `t0`
>>> and `x1` and if they don't match then hart will raise an illegal
>>> instruction exception.
>>>
>>> Shadow stack is read-only memory except stores can be performed via
>>> `sspush` and `ssamoswap` instructions. This requires new PTE encoding for
>>> shadow stack. zisslpcfi uses R=0, W=1, X=0 (an existing reserved encoding
>>> ) to encode a shadow stack. If backward cfi is not enabled for current
>>> mode, shadow stack PTE encodings remain reserved. Regular stores to
>>> shadow stack raise AMO/store access fault. Shadow stack loads/stores on
>>> regular memory raise load access/store access fault.
>>>
>>> This patch creates a new MMU TLB index for shadow stack and flushes TLB
>>> for shadow stack on privileges changes. This patch doesn't implement
>>> `Smepmp` related enforcement on shadow stack pmp entry. Reason being qemu
>>> doesn't have `Smepmp` implementation yet.
>> I don't know that the Smepmp means here. QEMU has supported the epmp.
> https://github.com/riscv/riscv-tee/blob/main/Smepmp/Smepmp.pdf

This specification has been supported. You can enable this extension by 
-cpu rv64,x-epmp=on.

I didn't see the special contents for shadow stack, neither on cfi 
specfication nor the epmp specification.

You should make it clear that how the shadow stack influenced by the pmp 
rules.

>
>>>    `Smepmp` enforcement should come
>>> whenever it is implemented.
>>>
>>> Signed-off-by: Deepak Gupta<debug@rivosinc.com>
>>> Signed-off-by: Kip Walker<kip@rivosinc.com>
>>> ---
>>>    target/riscv/cpu-param.h  |   1 +
>>>    target/riscv/cpu.c        |   2 +
>>>    target/riscv/cpu.h        |   3 ++
>>>    target/riscv/cpu_helper.c | 107 +++++++++++++++++++++++++++++++-------
>>>    4 files changed, 94 insertions(+), 19 deletions(-)
>>>
>>> diff --git a/target/riscv/cpu-param.h b/target/riscv/cpu-param.h
>>> index ebaf26d26d..a1e379beb7 100644
>>> --- a/target/riscv/cpu-param.h
>>> +++ b/target/riscv/cpu-param.h
>>> @@ -25,6 +25,7 @@
>>>     *  - M mode 0b011
>>>     *  - U mode HLV/HLVX/HSV 0b100
>>>     *  - S mode HLV/HLVX/HSV 0b101
>>> + *  - BCFI shadow stack   0b110
>>>     *  - M mode HLV/HLVX/HSV 0b111
>>>     */
>>>    #define NB_MMU_MODES 8
>>> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
>>> index 6b4e90eb91..14cfb93288 100644
>>> --- a/target/riscv/cpu.c
>>> +++ b/target/riscv/cpu.c
>>> @@ -584,6 +584,8 @@ static void riscv_cpu_reset_hold(Object *obj)
>>>        }
>>>        /* mmte is supposed to have pm.current hardwired to 1 */
>>>        env->mmte |= (PM_EXT_INITIAL | MMTE_M_PM_CURRENT);
>>> +    /* Initialize ss_priv to current priv. */
>>> +    env->ss_priv = env->priv;
>>>    #endif
>>>        env->xl = riscv_cpu_mxl(env);
>>>        riscv_cpu_update_mask(env);
>>> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
>>> index d14ea4f91d..8803ea6426 100644
>>> --- a/target/riscv/cpu.h
>>> +++ b/target/riscv/cpu.h
>>> @@ -379,6 +379,7 @@ struct CPUArchState {
>>>        uint64_t sstateen[SMSTATEEN_MAX_COUNT];
>>>        target_ulong senvcfg;
>>>        uint64_t henvcfg;
>>> +    target_ulong ss_priv;
>>>    #endif
>>>        target_ulong cur_pmmask;
>>>        target_ulong cur_pmbase;
>>> @@ -617,6 +618,8 @@ void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong);
>>>    #define TB_FLAGS_PRIV_HYP_ACCESS_MASK   (1 << 2)
>>>    #define TB_FLAGS_MSTATUS_FS MSTATUS_FS
>>>    #define TB_FLAGS_MSTATUS_VS MSTATUS_VS
>>> +/* TLB MMU index for shadow stack accesses */
>>> +#define MMU_IDX_SS_ACCESS    6
>>>
>>>    #include "exec/cpu-all.h"
>>>
>>> diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
>>> index fc188683c9..63377abc2f 100644
>>> --- a/target/riscv/cpu_helper.c
>>> +++ b/target/riscv/cpu_helper.c
>>> @@ -657,7 +657,8 @@ void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable)
>>>
>>>    bool riscv_cpu_two_stage_lookup(int mmu_idx)
>>>    {
>>> -    return mmu_idx & TB_FLAGS_PRIV_HYP_ACCESS_MASK;
>>> +    return (mmu_idx & TB_FLAGS_PRIV_HYP_ACCESS_MASK) &&
>>> +           (mmu_idx != MMU_IDX_SS_ACCESS);
>>>    }
>>>
>>>    int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts)
>>> @@ -745,6 +746,38 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv)
>>>         * preemptive context switch. As a result, do both.
>>>         */
>>>        env->load_res = -1;
>>> +
>>> +    if (cpu_get_bcfien(env) && (env->priv != env->ss_priv)) {
>>> +        /*
>>> +         * If backward CFI is enabled in the new privilege state, the
>>> +         * shadow stack TLB needs to be flushed - unless the most recent
>>> +         * use of the SS TLB was for the same privilege mode.
>>> +         */
>>> +        tlb_flush_by_mmuidx(env_cpu(env), 1 << MMU_IDX_SS_ACCESS);
>>> +        /*
>>> +         * Ignoring env->virt here since currently every time it flips,
>>> +         * all TLBs are flushed anyway.
>>> +         */
>>> +        env->ss_priv = env->priv;
>>> +    }
>>> +}
>>> +
>>> +typedef enum {
>>> +    SSTACK_NO,          /* Access is not for a shadow stack instruction */
>>> +    SSTACK_YES,         /* Access is for a shadow stack instruction */
>>> +    SSTACK_DC           /* Don't care about SS attribute in PMP */
>>> +} SStackPmpMode;
>>> +
>>> +static bool legal_sstack_access(int access_type, bool sstack_inst,
>>> +                                bool sstack_attribute)
>>> +{
>>> +    /*
>>> +     * Read/write/execution permissions are checked as usual. Shadow
>>> +     * stack enforcement is just that (1) instruction type must match
>>> +     * the attribute unless (2) a non-SS load to an SS region.
>>> +     */
>>> +    return (sstack_inst == sstack_attribute) ||
>>> +        ((access_type == MMU_DATA_LOAD) && sstack_attribute);
>>>    }
>>>
>>>    /*
>>> @@ -764,7 +797,7 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv)
>>>    static int get_physical_address_pmp(CPURISCVState *env, int *prot,
>>>                                        target_ulong *tlb_size, hwaddr addr,
>>>                                        int size, MMUAccessType access_type,
>>> -                                    int mode)
>>> +                                    int mode, SStackPmpMode sstack)
>> Why this parameter if you don't use it?
>>>    {
>>>        pmp_priv_t pmp_priv;
>>>        int pmp_index = -1;
>>> @@ -812,13 +845,16 @@ static int get_physical_address_pmp(CPURISCVState *env, int *prot,
>>>     *               Second stage is used for hypervisor guest translation
>>>     * @two_stage: Are we going to perform two stage translation
>>>     * @is_debug: Is this access from a debugger or the monitor?
>>> + * @sstack: Is this access for a shadow stack? Passed by reference so
>>> +            it can be forced to SSTACK_DC when the SS check is completed
>>> +            based on a PTE - so the PMP SS attribute will be ignored.
>>>     */
>>>    static int get_physical_address(CPURISCVState *env, hwaddr *physical,
>>>                                    int *prot, target_ulong addr,
>>>                                    target_ulong *fault_pte_addr,
>>>                                    int access_type, int mmu_idx,
>>>                                    bool first_stage, bool two_stage,
>>> -                                bool is_debug)
>>> +                                bool is_debug, SStackPmpMode *sstack)
>>>    {
>>>        /* NOTE: the env->pc value visible here will not be
>>>         * correct, but the value visible to the exception handler
>>> @@ -830,6 +866,7 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical,
>>>        hwaddr ppn;
>>>        RISCVCPU *cpu = env_archcpu(env);
>>>        int napot_bits = 0;
>>> +    bool is_sstack = (sstack != NULL) && (*sstack == SSTACK_YES);
>>>        target_ulong napot_mask;
>>>
>>>        /*
>>> @@ -851,6 +888,8 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical,
>>>            if (get_field(env->mstatus, MSTATUS_MPRV)) {
>>>                mode = get_field(env->mstatus, MSTATUS_MPP);
>>>            }
>>> +    } else if (mmu_idx == MMU_IDX_SS_ACCESS) {
>>> +        mode = env->priv;
>>>        }
>>>
>>>        if (first_stage == false) {
>>> @@ -966,7 +1005,7 @@ restart:
>>>                int vbase_ret = get_physical_address(env, &vbase, &vbase_prot,
>>>                                                     base, NULL, MMU_DATA_LOAD,
>>>                                                     mmu_idx, false, true,
>>> -                                                 is_debug);
>>> +                                                 is_debug, NULL);
>>>
>>>                if (vbase_ret != TRANSLATE_SUCCESS) {
>>>                    if (fault_pte_addr) {
>>> @@ -983,7 +1022,7 @@ restart:
>>>            int pmp_prot;
>>>            int pmp_ret = get_physical_address_pmp(env, &pmp_prot, NULL, pte_addr,
>>>                                                   sizeof(target_ulong),
>>> -                                               MMU_DATA_LOAD, PRV_S);
>>> +                                               MMU_DATA_LOAD, PRV_S, SSTACK_NO);
>>>            if (pmp_ret != TRANSLATE_SUCCESS) {
>>>                return TRANSLATE_PMP_FAIL;
>>>            }
>>> @@ -1010,6 +1049,18 @@ restart:
>>>                }
>>>            }
>>>
>>> +        /*
>>> +         * When backward CFI is enabled, the R=0, W=1, X=0 reserved encoding
>>> +         * is used to mark Shadow Stack (SS) pages. If back CFI enabled, allow
>>> +         * normal loads on SS pages, regular stores raise store access fault
>>> +         * and avoid hitting the reserved-encoding case. Only shadow stack
>>> +         * stores are allowed on SS pages. Shadow stack loads and stores on
>>> +         * regular memory (non-SS) raise load and store/AMO access fault.
>>> +         * Second stage translations don't participate in Shadow Stack.
>>> +         */
>>> +        bool sstack_page = (cpu_get_bcfien(env) && first_stage &&
>>> +                            ((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W));
>>> +
>>>            if (!(pte & PTE_V)) {
>>>                /* Invalid PTE */
>>>                return TRANSLATE_FAIL;
>>> @@ -1021,7 +1072,7 @@ restart:
>>>                    return TRANSLATE_FAIL;
>>>                }
>>>                base = ppn << PGSHIFT;
>>> -        } else if ((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W) {
>>> +        } else if (((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W) && !sstack_page) {
>>>                /* Reserved leaf PTE flags: PTE_W */
>>>                return TRANSLATE_FAIL;
>>>            } else if ((pte & (PTE_R | PTE_W | PTE_X)) == (PTE_W | PTE_X)) {
>>> @@ -1038,16 +1089,21 @@ restart:
>>>            } else if (ppn & ((1ULL << ptshift) - 1)) {
>>>                /* Misaligned PPN */
>>>                return TRANSLATE_FAIL;
>>> -        } else if (access_type == MMU_DATA_LOAD && !((pte & PTE_R) ||
>>> -                   ((pte & PTE_X) && mxr))) {
>>> +        } else if (access_type == MMU_DATA_LOAD && !(((pte & PTE_R) ||
>>> +                   sstack_page) || ((pte & PTE_X) && mxr))) {
>>>                /* Read access check failed */
>>>                return TRANSLATE_FAIL;
>>> -        } else if (access_type == MMU_DATA_STORE && !(pte & PTE_W)) {
>>> +        } else if ((access_type == MMU_DATA_STORE && !is_sstack) &&
>>> +                   !(pte & PTE_W)) {
>> Why limit to !is_sstack? Even is_sstack, we should make sure
>>
>> (access_type == MMU_DATA_STORE && !(pte & PTE_W)
>>
>> fails.
> As per spec if a shadow stack store happens to a memory which is not a
> shadow stack memory then cpu must raise
> access store fault. This failure here converts to a page fault.
> TRANSLATE_PMP_FAIL is the one which converts to
> access faults.  So this check here ensures that legacy behavior is
> maintained i.e.

Fair enough.  I think we can just use the more redundant but clearer 
condition

if ((access_type == MMU_DATA_STORE && !is_sstack && !sstack_page) && !(pte & PTE_W))

Thus two new memory access types will be postpone to the 
legal_sstack_access.
Or you can use  current code, and with a comment.


>
> Few lines down there is a call to `legal_sstack_access` which actually
> does the logic check of
> "If a regular store happened on shadow stack memory, returns false"
> "If a shadow stack access happened on regular memory, returns false"
> And this check returns PMP_TRANSLATE_FAIL which converts to access faults.
>
> On a very high level, shadow stack accesses (sspush/sspop/ssamoswap)
> to regular memory result in access faults.
> Regular store to shadow stack memory result in store/AMO access fault.
> Regular load to shadow stack memory is allowed.
>
> Let me know if this was clear.
>
>>>                /* Write access check failed */
>>>                return TRANSLATE_FAIL;
>>>            } else if (access_type == MMU_INST_FETCH && !(pte & PTE_X)) {
>>>                /* Fetch access check failed */
>>>                return TRANSLATE_FAIL;
>>> +        } else if (!legal_sstack_access(access_type, is_sstack,
>>> +                                        sstack_page)) {
>>> +            /* Illegal combo of instruction type and page attribute */
>>> +            return TRANSLATE_PMP_FAIL;
>> Not sure about this. Does the cfi escape the pmp check?
>>>            } else {
>>>                /* if necessary, set accessed and dirty bits. */
>>>                target_ulong updated_pte = pte | PTE_A |
>>> @@ -1107,18 +1163,27 @@ restart:
>>>                             ) << PGSHIFT) | (addr & ~TARGET_PAGE_MASK);
>>>
>>>                /* set permissions on the TLB entry */
>>> -            if ((pte & PTE_R) || ((pte & PTE_X) && mxr)) {
>>> +            if ((pte & PTE_R) || ((pte & PTE_X) && mxr) || sstack_page) {
>> I see that we should add the PAGE_READ for sstack_page, such as for a
>> no-SS load.
> I didn't get this comment. Can you clarify a bit more?

Just a guess why you add sstack_page here. Nothing too much. Ignore it.

Zhiwei

>> Zhiwei
>>
>>>                    *prot |= PAGE_READ;
>>>                }
>>>                if ((pte & PTE_X)) {
>>>                    *prot |= PAGE_EXEC;
>>>                }
>>> -            /* add write permission on stores or if the page is already dirty,
>>> -               so that we TLB miss on later writes to update the dirty bit */
>>> +            /*
>>> +             * add write permission on stores or if the page is already dirty,
>>> +             * so that we TLB miss on later writes to update the dirty bit
>>> +             */
>>>                if ((pte & PTE_W) &&
>>>                        (access_type == MMU_DATA_STORE || (pte & PTE_D))) {
>>>                    *prot |= PAGE_WRITE;
>>>                }
>>> +            if (sstack) {
>>> +                /*
>>> +                 * Tell the caller to skip the SS bit in the PMP since we
>>> +                 * resolved the attributes via the page table.
>>> +                 */
>>> +                *sstack = SSTACK_DC;
>>> +            }
>>>                return TRANSLATE_SUCCESS;
>>>            }
>>>        }
>>> @@ -1190,13 +1255,13 @@ hwaddr riscv_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
>>>        int mmu_idx = cpu_mmu_index(&cpu->env, false);
>>>
>>>        if (get_physical_address(env, &phys_addr, &prot, addr, NULL, 0, mmu_idx,
>>> -                             true, riscv_cpu_virt_enabled(env), true)) {
>>> +                             true, riscv_cpu_virt_enabled(env), true, NULL)) {
>>>            return -1;
>>>        }
>>>
>>>        if (riscv_cpu_virt_enabled(env)) {
>>>            if (get_physical_address(env, &phys_addr, &prot, phys_addr, NULL,
>>> -                                 0, mmu_idx, false, true, true)) {
>>> +                                 0, mmu_idx, false, true, true, NULL)) {
>>>                return -1;
>>>            }
>>>        }
>>> @@ -1291,6 +1356,8 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
>>>        bool two_stage_indirect_error = false;
>>>        int ret = TRANSLATE_FAIL;
>>>        int mode = mmu_idx;
>>> +    bool sstack = (mmu_idx == MMU_IDX_SS_ACCESS);
>>> +    SStackPmpMode ssmode = sstack ? SSTACK_YES : SSTACK_NO;
>>>        /* default TLB page size */
>>>        target_ulong tlb_size = TARGET_PAGE_SIZE;
>>>
>>> @@ -1318,7 +1385,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
>>>            /* Two stage lookup */
>>>            ret = get_physical_address(env, &pa, &prot, address,
>>>                                       &env->guest_phys_fault_addr, access_type,
>>> -                                   mmu_idx, true, true, false);
>>> +                                   mmu_idx, true, true, false, &ssmode);
>>>
>>>            /*
>>>             * A G-stage exception may be triggered during two state lookup.
>>> @@ -1342,7 +1409,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
>>>
>>>                ret = get_physical_address(env, &pa, &prot2, im_address, NULL,
>>>                                           access_type, mmu_idx, false, true,
>>> -                                       false);
>>> +                                       false, NULL);
>>>
>>>                qemu_log_mask(CPU_LOG_MMU,
>>>                        "%s 2nd-stage address=%" VADDR_PRIx " ret %d physical "
>>> @@ -1353,7 +1420,8 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
>>>
>>>                if (ret == TRANSLATE_SUCCESS) {
>>>                    ret = get_physical_address_pmp(env, &prot_pmp, &tlb_size, pa,
>>> -                                               size, access_type, mode);
>>> +                                               size, access_type, mode,
>>> +                                               SSTACK_NO);
>>>
>>>                    qemu_log_mask(CPU_LOG_MMU,
>>>                                  "%s PMP address=" HWADDR_FMT_plx " ret %d prot"
>>> @@ -1377,7 +1445,8 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
>>>        } else {
>>>            /* Single stage lookup */
>>>            ret = get_physical_address(env, &pa, &prot, address, NULL,
>>> -                                   access_type, mmu_idx, true, false, false);
>>> +                                   access_type, mmu_idx, true, false,
>>> +                                   false, &ssmode);
>>>
>>>            qemu_log_mask(CPU_LOG_MMU,
>>>                          "%s address=%" VADDR_PRIx " ret %d physical "
>>> @@ -1386,7 +1455,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
>>>
>>>            if (ret == TRANSLATE_SUCCESS) {
>>>                ret = get_physical_address_pmp(env, &prot_pmp, &tlb_size, pa,
>>> -                                           size, access_type, mode);
>>> +                                           size, access_type, mode, ssmode);
>>>
>>>                qemu_log_mask(CPU_LOG_MMU,
>>>                              "%s PMP address=" HWADDR_FMT_plx " ret %d prot"

[-- Attachment #2: Type: text/html, Size: 21050 bytes --]

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v1 RFC Zisslpcfi 7/9] target/riscv: Tracking indirect branches (fcfi) using TCG
  2023-02-16  0:02     ` Deepak Gupta
@ 2023-02-16  2:43       ` LIU Zhiwei
  2023-02-16  5:45         ` Deepak Gupta
  0 siblings, 1 reply; 42+ messages in thread
From: LIU Zhiwei @ 2023-02-16  2:43 UTC (permalink / raw)
  To: Deepak Gupta
  Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, liweiwei, dbarboza,
	Kip Walker, qemu-riscv, qemu-devel, Richard Henderson


On 2023/2/16 8:02, Deepak Gupta wrote:
> On Wed, Feb 15, 2023 at 12:55 AM LIU Zhiwei
> <zhiwei_liu@linux.alibaba.com> wrote:
>>
>> On 2023/2/9 14:24, Deepak Gupta wrote:
>>> zisslpcfi protects forward control flow (if enabled) by enforcing all
>>> indirect call and jmp must land on a landing pad instruction `lpcll`
>>> short for landing pad and check lower label value. If target of an
>>> indirect call or jmp is not `lpcll` then cpu/hart must raise an illegal
>>> instruction exception.
>>>
>>> This patch implements the mechanism using TCG. Target architecture branch
>>> instruction must define the end of a TB. Using this property, during
>>> translation of branch instruction, TB flag = FCFI_LP_EXPECTED can be set.
>>> Translation of target TB can check if FCFI_LP_EXPECTED flag is set and a
>>> flag (fcfi_lp_expected) can be set in DisasContext. If `lpcll` gets
>>> translated, fcfi_lp_expected flag in DisasContext can be cleared. Else
>>> it'll fault.
>>>
>>> This patch also also adds flag for forward and backward cfi in
>>> DisasContext.
>>>
>>> Signed-off-by: Deepak Gupta <debug@rivosinc.com>
>>> Signed-off-by: Kip Walker  <kip@rivosinc.com>
>>> ---
>>>    target/riscv/cpu.h        |  3 +++
>>>    target/riscv/cpu_helper.c | 12 +++++++++
>>>    target/riscv/translate.c  | 52 +++++++++++++++++++++++++++++++++++++++
>>>    3 files changed, 67 insertions(+)
>>>
>>> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
>>> index 8803ea6426..98b272bcad 100644
>>> --- a/target/riscv/cpu.h
>>> +++ b/target/riscv/cpu.h
>>> @@ -644,6 +644,9 @@ FIELD(TB_FLAGS, VMA, 25, 1)
>>>    /* Native debug itrigger */
>>>    FIELD(TB_FLAGS, ITRIGGER, 26, 1)
>>>
>>> +/* Zisslpcfi needs a TB flag to track indirect branches */
>>> +FIELD(TB_FLAGS, FCFI_LP_EXPECTED, 27, 1)
>>> +
>>>    #ifdef TARGET_RISCV32
>>>    #define riscv_cpu_mxl(env)  ((void)(env), MXL_RV32)
>>>    #else
>>> diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
>>> index 63377abc2f..d15918f534 100644
>>> --- a/target/riscv/cpu_helper.c
>>> +++ b/target/riscv/cpu_helper.c
>>> @@ -129,6 +129,18 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc,
>>>            flags = FIELD_DP32(flags, TB_FLAGS, VILL, 1);
>>>        }
>>>
>>> +    if (cpu->cfg.ext_cfi) {
>>> +        /*
>>> +         * For Forward CFI, only the expectation of a lpcll at
>>> +         * the start of the block is tracked (which can only happen
>>> +         * when FCFI is enabled for the current processor mode). A jump
>>> +         * or call at the end of the previous TB will have updated
>>> +         * env->elp to indicate the expectation.
>>> +         */
>>> +        flags = FIELD_DP32(flags, TB_FLAGS, FCFI_LP_EXPECTED,
>>> +                           env->elp != NO_LP_EXPECTED);
>>> +    }
>>> +
>>>    #ifdef CONFIG_USER_ONLY
>>>        flags |= TB_FLAGS_MSTATUS_FS;
>>>        flags |= TB_FLAGS_MSTATUS_VS;
>>> diff --git a/target/riscv/translate.c b/target/riscv/translate.c
>>> index df38db7553..7d43d20fc3 100644
>>> --- a/target/riscv/translate.c
>>> +++ b/target/riscv/translate.c
>>> @@ -41,6 +41,7 @@ static TCGv load_val;
>>>    /* globals for PM CSRs */
>>>    static TCGv pm_mask;
>>>    static TCGv pm_base;
>>> +static TCGOp *cfi_lp_check;
>>>
>>>    #include "exec/gen-icount.h"
>>>
>>> @@ -116,6 +117,10 @@ typedef struct DisasContext {
>>>        bool itrigger;
>>>        /* TCG of the current insn_start */
>>>        TCGOp *insn_start;
>>> +    /* CFI extension */
>>> +    bool bcfi_enabled;
>>> +    bool fcfi_enabled;
>>> +    bool fcfi_lp_expected;
>>>    } DisasContext;
>>>
>>>    static inline bool has_ext(DisasContext *ctx, uint32_t ext)
>>> @@ -1166,11 +1171,44 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
>>>        ctx->pm_mask_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_MASK_ENABLED);
>>>        ctx->pm_base_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_BASE_ENABLED);
>>>        ctx->itrigger = FIELD_EX32(tb_flags, TB_FLAGS, ITRIGGER);
>>> +    ctx->bcfi_enabled = cpu_get_bcfien(env);
>>> +    ctx->fcfi_enabled = cpu_get_fcfien(env);
>> This is wrong.  If you ctx->bcfi_enabled in the translation and don't
>> put it in a tb flags field, the translated tb will
>> be misused.
> TLB for shadow stack index is flushed on privilege transfers.
> All TLBs is flushed whenever enable/disable bits for shadow stack are toggled.
> Can you elaborate a bit more how this can be misused?
As Richard has pointed out, you need put this fields into tb flags if 
you want to use them in translation process as constants.
Nothing to do with TLB.

Tb flags will always be calculated (Except the direct block chain I 
think, is it right? @Richard) before using the translated tb. So if your 
translated tb depend on some machine states, you
should put it in tb flags. Otherwise, the translated tb will be misused, 
as its execution environment varies from its translation machine states.

Zhiwei

>
>>
>> Zhiwei
>>
>>> +    ctx->fcfi_lp_expected = FIELD_EX32(tb_flags, TB_FLAGS, FCFI_LP_EXPECTED);
>>>        ctx->zero = tcg_constant_tl(0);
>>>    }
>>>
>>>    static void riscv_tr_tb_start(DisasContextBase *db, CPUState *cpu)
>>>    {
>>> +    DisasContext *ctx = container_of(db, DisasContext, base);
>>> +
>>> +    if (ctx->fcfi_lp_expected) {
>>> +        /*
>>> +         * Since we can't look ahead to confirm that the first
>>> +         * instruction is a legal landing pad instruction, emit
>>> +         * compare-and-branch sequence that will be fixed-up in
>>> +         * riscv_tr_tb_stop() to either statically hit or skip an
>>> +         * illegal instruction exception depending on whether the
>>> +         * flag was lowered by translation of a CJLP or JLP as
>>> +         * the first instruction in the block.
>>> +         */
>>> +        TCGv_i32 immediate;
>>> +        TCGLabel *l;
>>> +        l = gen_new_label();
>>> +        immediate = tcg_temp_local_new_i32();
>>> +        tcg_gen_movi_i32(immediate, 0);
>>> +        cfi_lp_check = tcg_last_op();
>>> +        tcg_gen_brcondi_i32(TCG_COND_EQ, immediate, 0, l);
>>> +        tcg_temp_free_i32(immediate);
>>> +        gen_exception_illegal(ctx);
>>> +        gen_set_label(l);
>>> +        /*
>>> +         * Despite the use of gen_exception_illegal(), the rest of
>>> +         * the TB needs to be generated. The TCG optimizer will
>>> +         * clean things up depending on which path ends up being
>>> +         * active.
>>> +         */
>>> +        ctx->base.is_jmp = DISAS_NEXT;
>>> +    }
>>>    }
>>>
>>>    static void riscv_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu)
>>> @@ -1225,6 +1263,7 @@ static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
>>>    static void riscv_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
>>>    {
>>>        DisasContext *ctx = container_of(dcbase, DisasContext, base);
>>> +    CPURISCVState *env = cpu->env_ptr;
>>>
>>>        switch (ctx->base.is_jmp) {
>>>        case DISAS_TOO_MANY:
>>> @@ -1235,6 +1274,19 @@ static void riscv_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
>>>        default:
>>>            g_assert_not_reached();
>>>        }
>>> +
>>> +    if (ctx->fcfi_lp_expected) {
>>> +        /*
>>> +         * If the "lp expected" flag is still up, the block needs to take an
>>> +         * illegal instruction exception.
>>> +         */
>>> +        tcg_set_insn_param(cfi_lp_check, 1, tcgv_i32_arg(tcg_constant_i32(1)));
>>> +    } else {
>>> +        /*
>>> +        * LP instruction requirement was met, clear up LP expected
>>> +        */
>>> +        env->elp = NO_LP_EXPECTED;
>>> +    }
>>>    }
>>>
>>>    static void riscv_tr_disas_log(const DisasContextBase *dcbase,


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v1 RFC Zisslpcfi 3/9] target/riscv: implements CSRs and new bits in existing CSRs in zisslpcfi
  2023-02-16  1:38         ` Deepak Gupta
@ 2023-02-16  2:43           ` Richard Henderson
  2023-02-16  5:20             ` Deepak Gupta
  0 siblings, 1 reply; 42+ messages in thread
From: Richard Henderson @ 2023-02-16  2:43 UTC (permalink / raw)
  To: Deepak Gupta
  Cc: LIU Zhiwei, Palmer Dabbelt, Alistair Francis, Bin Meng, liweiwei,
	dbarboza, Kip Walker, qemu-riscv, qemu-devel

On 2/15/23 15:38, Deepak Gupta wrote:
> Question:
> I'll basically need two bits (one for forward cfi and one for backward cfi).

Are they separately enabled?  It may also be possible to use a single bit and then perform 
a runtime check.  I guess I should read the spec...

> But I need to throw away the TB if cfi enabling bits mismatch at the
> time TB was generated and the current state of enabling bits.
> Reason being, this needs to get translated again and zimops need to be
> generated.
> 
> What's the best way to throw away a single TB?

You don't throw TBs away at all.

The current cpu state is produced by cpu_get_tb_cpu_state.  This is included into the hash 
table lookup and will only match a TB which has been generated with the same state.  Which 
means that you can have multiple live TBs, those with CFI enabled and those without, and 
the correct one will be selected at runtime.


r~


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v1 RFC Zisslpcfi 1/9] target/riscv: adding zimops and zisslpcfi extension to RISCV cpu config
  2023-02-16  1:46       ` LIU Zhiwei
@ 2023-02-16  4:20         ` Richard Henderson
  0 siblings, 0 replies; 42+ messages in thread
From: Richard Henderson @ 2023-02-16  4:20 UTC (permalink / raw)
  To: LIU Zhiwei, Deepak Gupta
  Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, liweiwei, dbarboza,
	Kip Walker, qemu-riscv, qemu-devel

On 2/15/23 15:46, LIU Zhiwei wrote:
> We can rearrange the patch set as follows:
> 
> 1. Implement the zimop extension.
> 
> 2. Implement the forward cfi only for system mode.
> 
> 3. Implement the backward cfi only for system mode.
> 
> 4. Carefully make the forward cfi can work on user mode.
> 
> 5. Carefully make the backward cfi work for user mode.
> 
> I don't think we can easily make cfi  work on user mode. So we can also ignore the 4 or 5, 
> or both.

This is a good ordering.  Similar to how we implemented CFI for AArch64.

I strongly suspect that you will need to defer 5 until the Linux uabi is defined.  It will 
require some mmap bit (MAP_* or PROT_*) which libc will use to define shadow stacks for 
new threads.

But having the system support in means that you can work on the corresponding kernel bits.


r~


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v1 RFC Zisslpcfi 3/9] target/riscv: implements CSRs and new bits in existing CSRs in zisslpcfi
  2023-02-16  2:43           ` Richard Henderson
@ 2023-02-16  5:20             ` Deepak Gupta
  0 siblings, 0 replies; 42+ messages in thread
From: Deepak Gupta @ 2023-02-16  5:20 UTC (permalink / raw)
  To: Richard Henderson
  Cc: LIU Zhiwei, Palmer Dabbelt, Alistair Francis, Bin Meng, liweiwei,
	dbarboza, Kip Walker, qemu-riscv, qemu-devel

On Wed, Feb 15, 2023 at 6:44 PM Richard Henderson
<richard.henderson@linaro.org> wrote:
>
> On 2/15/23 15:38, Deepak Gupta wrote:
> > Question:
> > I'll basically need two bits (one for forward cfi and one for backward cfi).
>
> Are they separately enabled?  It may also be possible to use a single bit and then perform
> a runtime check.  I guess I should read the spec...

There is a master enable but there're controls to individually enable & disable.
Thus the CPU could be in a state where one is enabled and other is not.

>
> > But I need to throw away the TB if cfi enabling bits mismatch at the
> > time TB was generated and the current state of enabling bits.
> > Reason being, this needs to get translated again and zimops need to be
> > generated.
> >
> > What's the best way to throw away a single TB?
>
> You don't throw TBs away at all.
>
> The current cpu state is produced by cpu_get_tb_cpu_state.  This is included into the hash
> table lookup and will only match a TB which has been generated with the same state.  Which
> means that you can have multiple live TBs, those with CFI enabled and those without, and
> the correct one will be selected at runtime.
>

Thanks a lot. I see `tb_lookup`  & `tb_htable_lookup`.
I'll revise and add some bits in TB_FLAGS for both of them.
And probably ask some questions on the list if anything is not clear to me.

>
> r~


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v1 RFC Zisslpcfi 6/9] target/riscv: MMU changes for back cfi's shadow stack
  2023-02-16  2:36       ` LIU Zhiwei
@ 2023-02-16  5:43         ` Deepak Gupta
  0 siblings, 0 replies; 42+ messages in thread
From: Deepak Gupta @ 2023-02-16  5:43 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, liweiwei, dbarboza,
	Kip Walker, qemu-riscv, qemu-devel

On Wed, Feb 15, 2023 at 6:36 PM LIU Zhiwei <zhiwei_liu@linux.alibaba.com> wrote:
>
>
> On 2023/2/16 7:57, Deepak Gupta wrote:
>
> `On Wed, Feb 15, 2023 at 12:43 AM LIU Zhiwei
> <zhiwei_liu@linux.alibaba.com> wrote:
>
> On 2023/2/9 14:24, Deepak Gupta wrote:
>
> zisslpcfi protects returns(back cfi) using shadow stack. If compiled with
> enabled compiler, function prologs will have `sspush ra` instruction to
> push return address on shadow stack and function epilogs will have
> `sspop t0; sschckra` instruction sequences. `sspop t0` will pop the
> value from top of the shadow stack in t0. `sschckra` will compare `t0`
> and `x1` and if they don't match then hart will raise an illegal
> instruction exception.
>
> Shadow stack is read-only memory except stores can be performed via
> `sspush` and `ssamoswap` instructions. This requires new PTE encoding for
> shadow stack. zisslpcfi uses R=0, W=1, X=0 (an existing reserved encoding
> ) to encode a shadow stack. If backward cfi is not enabled for current
> mode, shadow stack PTE encodings remain reserved. Regular stores to
> shadow stack raise AMO/store access fault. Shadow stack loads/stores on
> regular memory raise load access/store access fault.
>
> This patch creates a new MMU TLB index for shadow stack and flushes TLB
> for shadow stack on privileges changes. This patch doesn't implement
> `Smepmp` related enforcement on shadow stack pmp entry. Reason being qemu
> doesn't have `Smepmp` implementation yet.
>
> I don't know that the Smepmp means here. QEMU has supported the epmp.
>
> https://github.com/riscv/riscv-tee/blob/main/Smepmp/Smepmp.pdf
>
> This specification has been supported. You can enable this extension by -cpu rv64,x-epmp=on.
>
> I didn't see the special contents for shadow stack, neither on cfi specfication nor the epmp specification.
>
> You should make it clear that how the shadow stack influenced by the pmp rules.

Aah cool. I didn't notice that. I was looking for the string `smepmp`.

WIthout paging, shadow stack store/load simply honor pmpcfg read/write
permissions.
`sspush/ssamoswap` will succeed if the selected pmp entry's pmpcfg
allows a write. `sspop/ssamoswap` will
succeed if pmpcfg allows a read.

For M-mode shadow stack, there is a 6 bit field `sspmp` in mseccfg which selects
pmp entry for shadow stack accesses in M mode

M mode protection of shadow stack:
When mseccfg.MML=1, then
- sspush/sspop/ssamoswap must match PMP entry pointed to by `sspmp`
- stores other than `sspush` and `ssamoswap` that match pmp entry
`sspmp` must can access violation

I've to implement mseccfg.MML=1 rule for shadow stack in M mode.

Note: W=1,R=0,X=0 has already been claimed by Smepmp. So we can't use
that for the shadow stack in pmpcfg.

>
>   `Smepmp` enforcement should come
> whenever it is implemented.
>
> Signed-off-by: Deepak Gupta <debug@rivosinc.com>
> Signed-off-by: Kip Walker  <kip@rivosinc.com>
> ---
>   target/riscv/cpu-param.h  |   1 +
>   target/riscv/cpu.c        |   2 +
>   target/riscv/cpu.h        |   3 ++
>   target/riscv/cpu_helper.c | 107 +++++++++++++++++++++++++++++++-------
>   4 files changed, 94 insertions(+), 19 deletions(-)
>
> diff --git a/target/riscv/cpu-param.h b/target/riscv/cpu-param.h
> index ebaf26d26d..a1e379beb7 100644
> --- a/target/riscv/cpu-param.h
> +++ b/target/riscv/cpu-param.h
> @@ -25,6 +25,7 @@
>    *  - M mode 0b011
>    *  - U mode HLV/HLVX/HSV 0b100
>    *  - S mode HLV/HLVX/HSV 0b101
> + *  - BCFI shadow stack   0b110
>    *  - M mode HLV/HLVX/HSV 0b111
>    */
>   #define NB_MMU_MODES 8
> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> index 6b4e90eb91..14cfb93288 100644
> --- a/target/riscv/cpu.c
> +++ b/target/riscv/cpu.c
> @@ -584,6 +584,8 @@ static void riscv_cpu_reset_hold(Object *obj)
>       }
>       /* mmte is supposed to have pm.current hardwired to 1 */
>       env->mmte |= (PM_EXT_INITIAL | MMTE_M_PM_CURRENT);
> +    /* Initialize ss_priv to current priv. */
> +    env->ss_priv = env->priv;
>   #endif
>       env->xl = riscv_cpu_mxl(env);
>       riscv_cpu_update_mask(env);
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index d14ea4f91d..8803ea6426 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -379,6 +379,7 @@ struct CPUArchState {
>       uint64_t sstateen[SMSTATEEN_MAX_COUNT];
>       target_ulong senvcfg;
>       uint64_t henvcfg;
> +    target_ulong ss_priv;
>   #endif
>       target_ulong cur_pmmask;
>       target_ulong cur_pmbase;
> @@ -617,6 +618,8 @@ void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong);
>   #define TB_FLAGS_PRIV_HYP_ACCESS_MASK   (1 << 2)
>   #define TB_FLAGS_MSTATUS_FS MSTATUS_FS
>   #define TB_FLAGS_MSTATUS_VS MSTATUS_VS
> +/* TLB MMU index for shadow stack accesses */
> +#define MMU_IDX_SS_ACCESS    6
>
>   #include "exec/cpu-all.h"
>
> diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> index fc188683c9..63377abc2f 100644
> --- a/target/riscv/cpu_helper.c
> +++ b/target/riscv/cpu_helper.c
> @@ -657,7 +657,8 @@ void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable)
>
>   bool riscv_cpu_two_stage_lookup(int mmu_idx)
>   {
> -    return mmu_idx & TB_FLAGS_PRIV_HYP_ACCESS_MASK;
> +    return (mmu_idx & TB_FLAGS_PRIV_HYP_ACCESS_MASK) &&
> +           (mmu_idx != MMU_IDX_SS_ACCESS);
>   }
>
>   int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts)
> @@ -745,6 +746,38 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv)
>        * preemptive context switch. As a result, do both.
>        */
>       env->load_res = -1;
> +
> +    if (cpu_get_bcfien(env) && (env->priv != env->ss_priv)) {
> +        /*
> +         * If backward CFI is enabled in the new privilege state, the
> +         * shadow stack TLB needs to be flushed - unless the most recent
> +         * use of the SS TLB was for the same privilege mode.
> +         */
> +        tlb_flush_by_mmuidx(env_cpu(env), 1 << MMU_IDX_SS_ACCESS);
> +        /*
> +         * Ignoring env->virt here since currently every time it flips,
> +         * all TLBs are flushed anyway.
> +         */
> +        env->ss_priv = env->priv;
> +    }
> +}
> +
> +typedef enum {
> +    SSTACK_NO,          /* Access is not for a shadow stack instruction */
> +    SSTACK_YES,         /* Access is for a shadow stack instruction */
> +    SSTACK_DC           /* Don't care about SS attribute in PMP */
> +} SStackPmpMode;
> +
> +static bool legal_sstack_access(int access_type, bool sstack_inst,
> +                                bool sstack_attribute)
> +{
> +    /*
> +     * Read/write/execution permissions are checked as usual. Shadow
> +     * stack enforcement is just that (1) instruction type must match
> +     * the attribute unless (2) a non-SS load to an SS region.
> +     */
> +    return (sstack_inst == sstack_attribute) ||
> +        ((access_type == MMU_DATA_LOAD) && sstack_attribute);
>   }
>
>   /*
> @@ -764,7 +797,7 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv)
>   static int get_physical_address_pmp(CPURISCVState *env, int *prot,
>                                       target_ulong *tlb_size, hwaddr addr,
>                                       int size, MMUAccessType access_type,
> -                                    int mode)
> +                                    int mode, SStackPmpMode sstack)
>
> Why this parameter if you don't use it?
>
>   {
>       pmp_priv_t pmp_priv;
>       int pmp_index = -1;
> @@ -812,13 +845,16 @@ static int get_physical_address_pmp(CPURISCVState *env, int *prot,
>    *               Second stage is used for hypervisor guest translation
>    * @two_stage: Are we going to perform two stage translation
>    * @is_debug: Is this access from a debugger or the monitor?
> + * @sstack: Is this access for a shadow stack? Passed by reference so
> +            it can be forced to SSTACK_DC when the SS check is completed
> +            based on a PTE - so the PMP SS attribute will be ignored.
>    */
>   static int get_physical_address(CPURISCVState *env, hwaddr *physical,
>                                   int *prot, target_ulong addr,
>                                   target_ulong *fault_pte_addr,
>                                   int access_type, int mmu_idx,
>                                   bool first_stage, bool two_stage,
> -                                bool is_debug)
> +                                bool is_debug, SStackPmpMode *sstack)
>   {
>       /* NOTE: the env->pc value visible here will not be
>        * correct, but the value visible to the exception handler
> @@ -830,6 +866,7 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical,
>       hwaddr ppn;
>       RISCVCPU *cpu = env_archcpu(env);
>       int napot_bits = 0;
> +    bool is_sstack = (sstack != NULL) && (*sstack == SSTACK_YES);
>       target_ulong napot_mask;
>
>       /*
> @@ -851,6 +888,8 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical,
>           if (get_field(env->mstatus, MSTATUS_MPRV)) {
>               mode = get_field(env->mstatus, MSTATUS_MPP);
>           }
> +    } else if (mmu_idx == MMU_IDX_SS_ACCESS) {
> +        mode = env->priv;
>       }
>
>       if (first_stage == false) {
> @@ -966,7 +1005,7 @@ restart:
>               int vbase_ret = get_physical_address(env, &vbase, &vbase_prot,
>                                                    base, NULL, MMU_DATA_LOAD,
>                                                    mmu_idx, false, true,
> -                                                 is_debug);
> +                                                 is_debug, NULL);
>
>               if (vbase_ret != TRANSLATE_SUCCESS) {
>                   if (fault_pte_addr) {
> @@ -983,7 +1022,7 @@ restart:
>           int pmp_prot;
>           int pmp_ret = get_physical_address_pmp(env, &pmp_prot, NULL, pte_addr,
>                                                  sizeof(target_ulong),
> -                                               MMU_DATA_LOAD, PRV_S);
> +                                               MMU_DATA_LOAD, PRV_S, SSTACK_NO);
>           if (pmp_ret != TRANSLATE_SUCCESS) {
>               return TRANSLATE_PMP_FAIL;
>           }
> @@ -1010,6 +1049,18 @@ restart:
>               }
>           }
>
> +        /*
> +         * When backward CFI is enabled, the R=0, W=1, X=0 reserved encoding
> +         * is used to mark Shadow Stack (SS) pages. If back CFI enabled, allow
> +         * normal loads on SS pages, regular stores raise store access fault
> +         * and avoid hitting the reserved-encoding case. Only shadow stack
> +         * stores are allowed on SS pages. Shadow stack loads and stores on
> +         * regular memory (non-SS) raise load and store/AMO access fault.
> +         * Second stage translations don't participate in Shadow Stack.
> +         */
> +        bool sstack_page = (cpu_get_bcfien(env) && first_stage &&
> +                            ((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W));
> +
>           if (!(pte & PTE_V)) {
>               /* Invalid PTE */
>               return TRANSLATE_FAIL;
> @@ -1021,7 +1072,7 @@ restart:
>                   return TRANSLATE_FAIL;
>               }
>               base = ppn << PGSHIFT;
> -        } else if ((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W) {
> +        } else if (((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W) && !sstack_page) {
>               /* Reserved leaf PTE flags: PTE_W */
>               return TRANSLATE_FAIL;
>           } else if ((pte & (PTE_R | PTE_W | PTE_X)) == (PTE_W | PTE_X)) {
> @@ -1038,16 +1089,21 @@ restart:
>           } else if (ppn & ((1ULL << ptshift) - 1)) {
>               /* Misaligned PPN */
>               return TRANSLATE_FAIL;
> -        } else if (access_type == MMU_DATA_LOAD && !((pte & PTE_R) ||
> -                   ((pte & PTE_X) && mxr))) {
> +        } else if (access_type == MMU_DATA_LOAD && !(((pte & PTE_R) ||
> +                   sstack_page) || ((pte & PTE_X) && mxr))) {
>               /* Read access check failed */
>               return TRANSLATE_FAIL;
> -        } else if (access_type == MMU_DATA_STORE && !(pte & PTE_W)) {
> +        } else if ((access_type == MMU_DATA_STORE && !is_sstack) &&
> +                   !(pte & PTE_W)) {
>
> Why limit to !is_sstack? Even is_sstack, we should make sure
>
> (access_type == MMU_DATA_STORE && !(pte & PTE_W)
>
> fails.
>
> As per spec if a shadow stack store happens to a memory which is not a
> shadow stack memory then cpu must raise
> access store fault. This failure here converts to a page fault.
> TRANSLATE_PMP_FAIL is the one which converts to
> access faults.  So this check here ensures that legacy behavior is
> maintained i.e.
>
> Fair enough.  I think we can just use the more redundant but clearer condition
>
> if ((access_type == MMU_DATA_STORE && !is_sstack && !sstack_page) && !(pte & PTE_W))
>
> Thus two new memory access types will be postpone to the legal_sstack_access.
> Or you can use  current code, and with a comment.
>
>
>
> Few lines down there is a call to `legal_sstack_access` which actually
> does the logic check of
> "If a regular store happened on shadow stack memory, returns false"
> "If a shadow stack access happened on regular memory, returns false"
> And this check returns PMP_TRANSLATE_FAIL which converts to access faults.
>
> On a very high level, shadow stack accesses (sspush/sspop/ssamoswap)
> to regular memory result in access faults.
> Regular store to shadow stack memory result in store/AMO access fault.
> Regular load to shadow stack memory is allowed.
>
> Let me know if this was clear.
>
>               /* Write access check failed */
>               return TRANSLATE_FAIL;
>           } else if (access_type == MMU_INST_FETCH && !(pte & PTE_X)) {
>               /* Fetch access check failed */
>               return TRANSLATE_FAIL;
> +        } else if (!legal_sstack_access(access_type, is_sstack,
> +                                        sstack_page)) {
> +            /* Illegal combo of instruction type and page attribute */
> +            return TRANSLATE_PMP_FAIL;
>
> Not sure about this. Does the cfi escape the pmp check?
>
>           } else {
>               /* if necessary, set accessed and dirty bits. */
>               target_ulong updated_pte = pte | PTE_A |
> @@ -1107,18 +1163,27 @@ restart:
>                            ) << PGSHIFT) | (addr & ~TARGET_PAGE_MASK);
>
>               /* set permissions on the TLB entry */
> -            if ((pte & PTE_R) || ((pte & PTE_X) && mxr)) {
> +            if ((pte & PTE_R) || ((pte & PTE_X) && mxr) || sstack_page) {
>
> I see that we should add the PAGE_READ for sstack_page, such as for a
> no-SS load.
>
> I didn't get this comment. Can you clarify a bit more?
>
> Just a guess why you add sstack_page here. Nothing too much. Ignore it.
>
> Zhiwei
>
> Zhiwei
>
>                   *prot |= PAGE_READ;
>               }
>               if ((pte & PTE_X)) {
>                   *prot |= PAGE_EXEC;
>               }
> -            /* add write permission on stores or if the page is already dirty,
> -               so that we TLB miss on later writes to update the dirty bit */
> +            /*
> +             * add write permission on stores or if the page is already dirty,
> +             * so that we TLB miss on later writes to update the dirty bit
> +             */
>               if ((pte & PTE_W) &&
>                       (access_type == MMU_DATA_STORE || (pte & PTE_D))) {
>                   *prot |= PAGE_WRITE;
>               }
> +            if (sstack) {
> +                /*
> +                 * Tell the caller to skip the SS bit in the PMP since we
> +                 * resolved the attributes via the page table.
> +                 */
> +                *sstack = SSTACK_DC;
> +            }
>               return TRANSLATE_SUCCESS;
>           }
>       }
> @@ -1190,13 +1255,13 @@ hwaddr riscv_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
>       int mmu_idx = cpu_mmu_index(&cpu->env, false);
>
>       if (get_physical_address(env, &phys_addr, &prot, addr, NULL, 0, mmu_idx,
> -                             true, riscv_cpu_virt_enabled(env), true)) {
> +                             true, riscv_cpu_virt_enabled(env), true, NULL)) {
>           return -1;
>       }
>
>       if (riscv_cpu_virt_enabled(env)) {
>           if (get_physical_address(env, &phys_addr, &prot, phys_addr, NULL,
> -                                 0, mmu_idx, false, true, true)) {
> +                                 0, mmu_idx, false, true, true, NULL)) {
>               return -1;
>           }
>       }
> @@ -1291,6 +1356,8 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
>       bool two_stage_indirect_error = false;
>       int ret = TRANSLATE_FAIL;
>       int mode = mmu_idx;
> +    bool sstack = (mmu_idx == MMU_IDX_SS_ACCESS);
> +    SStackPmpMode ssmode = sstack ? SSTACK_YES : SSTACK_NO;
>       /* default TLB page size */
>       target_ulong tlb_size = TARGET_PAGE_SIZE;
>
> @@ -1318,7 +1385,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
>           /* Two stage lookup */
>           ret = get_physical_address(env, &pa, &prot, address,
>                                      &env->guest_phys_fault_addr, access_type,
> -                                   mmu_idx, true, true, false);
> +                                   mmu_idx, true, true, false, &ssmode);
>
>           /*
>            * A G-stage exception may be triggered during two state lookup.
> @@ -1342,7 +1409,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
>
>               ret = get_physical_address(env, &pa, &prot2, im_address, NULL,
>                                          access_type, mmu_idx, false, true,
> -                                       false);
> +                                       false, NULL);
>
>               qemu_log_mask(CPU_LOG_MMU,
>                       "%s 2nd-stage address=%" VADDR_PRIx " ret %d physical "
> @@ -1353,7 +1420,8 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
>
>               if (ret == TRANSLATE_SUCCESS) {
>                   ret = get_physical_address_pmp(env, &prot_pmp, &tlb_size, pa,
> -                                               size, access_type, mode);
> +                                               size, access_type, mode,
> +                                               SSTACK_NO);
>
>                   qemu_log_mask(CPU_LOG_MMU,
>                                 "%s PMP address=" HWADDR_FMT_plx " ret %d prot"
> @@ -1377,7 +1445,8 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
>       } else {
>           /* Single stage lookup */
>           ret = get_physical_address(env, &pa, &prot, address, NULL,
> -                                   access_type, mmu_idx, true, false, false);
> +                                   access_type, mmu_idx, true, false,
> +                                   false, &ssmode);
>
>           qemu_log_mask(CPU_LOG_MMU,
>                         "%s address=%" VADDR_PRIx " ret %d physical "
> @@ -1386,7 +1455,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
>
>           if (ret == TRANSLATE_SUCCESS) {
>               ret = get_physical_address_pmp(env, &prot_pmp, &tlb_size, pa,
> -                                           size, access_type, mode);
> +                                           size, access_type, mode, ssmode);
>
>               qemu_log_mask(CPU_LOG_MMU,
>                             "%s PMP address=" HWADDR_FMT_plx " ret %d prot"


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v1 RFC Zisslpcfi 7/9] target/riscv: Tracking indirect branches (fcfi) using TCG
  2023-02-16  2:43       ` LIU Zhiwei
@ 2023-02-16  5:45         ` Deepak Gupta
  0 siblings, 0 replies; 42+ messages in thread
From: Deepak Gupta @ 2023-02-16  5:45 UTC (permalink / raw)
  To: LIU Zhiwei
  Cc: Palmer Dabbelt, Alistair Francis, Bin Meng, liweiwei, dbarboza,
	Kip Walker, qemu-riscv, qemu-devel, Richard Henderson

On Wed, Feb 15, 2023 at 6:44 PM LIU Zhiwei <zhiwei_liu@linux.alibaba.com> wrote:
>
>
> On 2023/2/16 8:02, Deepak Gupta wrote:
> > On Wed, Feb 15, 2023 at 12:55 AM LIU Zhiwei
> > <zhiwei_liu@linux.alibaba.com> wrote:
> >>
> >> On 2023/2/9 14:24, Deepak Gupta wrote:
> >>> zisslpcfi protects forward control flow (if enabled) by enforcing all
> >>> indirect call and jmp must land on a landing pad instruction `lpcll`
> >>> short for landing pad and check lower label value. If target of an
> >>> indirect call or jmp is not `lpcll` then cpu/hart must raise an illegal
> >>> instruction exception.
> >>>
> >>> This patch implements the mechanism using TCG. Target architecture branch
> >>> instruction must define the end of a TB. Using this property, during
> >>> translation of branch instruction, TB flag = FCFI_LP_EXPECTED can be set.
> >>> Translation of target TB can check if FCFI_LP_EXPECTED flag is set and a
> >>> flag (fcfi_lp_expected) can be set in DisasContext. If `lpcll` gets
> >>> translated, fcfi_lp_expected flag in DisasContext can be cleared. Else
> >>> it'll fault.
> >>>
> >>> This patch also also adds flag for forward and backward cfi in
> >>> DisasContext.
> >>>
> >>> Signed-off-by: Deepak Gupta <debug@rivosinc.com>
> >>> Signed-off-by: Kip Walker  <kip@rivosinc.com>
> >>> ---
> >>>    target/riscv/cpu.h        |  3 +++
> >>>    target/riscv/cpu_helper.c | 12 +++++++++
> >>>    target/riscv/translate.c  | 52 +++++++++++++++++++++++++++++++++++++++
> >>>    3 files changed, 67 insertions(+)
> >>>
> >>> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> >>> index 8803ea6426..98b272bcad 100644
> >>> --- a/target/riscv/cpu.h
> >>> +++ b/target/riscv/cpu.h
> >>> @@ -644,6 +644,9 @@ FIELD(TB_FLAGS, VMA, 25, 1)
> >>>    /* Native debug itrigger */
> >>>    FIELD(TB_FLAGS, ITRIGGER, 26, 1)
> >>>
> >>> +/* Zisslpcfi needs a TB flag to track indirect branches */
> >>> +FIELD(TB_FLAGS, FCFI_LP_EXPECTED, 27, 1)
> >>> +
> >>>    #ifdef TARGET_RISCV32
> >>>    #define riscv_cpu_mxl(env)  ((void)(env), MXL_RV32)
> >>>    #else
> >>> diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> >>> index 63377abc2f..d15918f534 100644
> >>> --- a/target/riscv/cpu_helper.c
> >>> +++ b/target/riscv/cpu_helper.c
> >>> @@ -129,6 +129,18 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc,
> >>>            flags = FIELD_DP32(flags, TB_FLAGS, VILL, 1);
> >>>        }
> >>>
> >>> +    if (cpu->cfg.ext_cfi) {
> >>> +        /*
> >>> +         * For Forward CFI, only the expectation of a lpcll at
> >>> +         * the start of the block is tracked (which can only happen
> >>> +         * when FCFI is enabled for the current processor mode). A jump
> >>> +         * or call at the end of the previous TB will have updated
> >>> +         * env->elp to indicate the expectation.
> >>> +         */
> >>> +        flags = FIELD_DP32(flags, TB_FLAGS, FCFI_LP_EXPECTED,
> >>> +                           env->elp != NO_LP_EXPECTED);
> >>> +    }
> >>> +
> >>>    #ifdef CONFIG_USER_ONLY
> >>>        flags |= TB_FLAGS_MSTATUS_FS;
> >>>        flags |= TB_FLAGS_MSTATUS_VS;
> >>> diff --git a/target/riscv/translate.c b/target/riscv/translate.c
> >>> index df38db7553..7d43d20fc3 100644
> >>> --- a/target/riscv/translate.c
> >>> +++ b/target/riscv/translate.c
> >>> @@ -41,6 +41,7 @@ static TCGv load_val;
> >>>    /* globals for PM CSRs */
> >>>    static TCGv pm_mask;
> >>>    static TCGv pm_base;
> >>> +static TCGOp *cfi_lp_check;
> >>>
> >>>    #include "exec/gen-icount.h"
> >>>
> >>> @@ -116,6 +117,10 @@ typedef struct DisasContext {
> >>>        bool itrigger;
> >>>        /* TCG of the current insn_start */
> >>>        TCGOp *insn_start;
> >>> +    /* CFI extension */
> >>> +    bool bcfi_enabled;
> >>> +    bool fcfi_enabled;
> >>> +    bool fcfi_lp_expected;
> >>>    } DisasContext;
> >>>
> >>>    static inline bool has_ext(DisasContext *ctx, uint32_t ext)
> >>> @@ -1166,11 +1171,44 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
> >>>        ctx->pm_mask_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_MASK_ENABLED);
> >>>        ctx->pm_base_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_BASE_ENABLED);
> >>>        ctx->itrigger = FIELD_EX32(tb_flags, TB_FLAGS, ITRIGGER);
> >>> +    ctx->bcfi_enabled = cpu_get_bcfien(env);
> >>> +    ctx->fcfi_enabled = cpu_get_fcfien(env);
> >> This is wrong.  If you ctx->bcfi_enabled in the translation and don't
> >> put it in a tb flags field, the translated tb will
> >> be misused.
> > TLB for shadow stack index is flushed on privilege transfers.
> > All TLBs is flushed whenever enable/disable bits for shadow stack are toggled.
> > Can you elaborate a bit more how this can be misused?
> As Richard has pointed out, you need put this fields into tb flags if
> you want to use them in translation process as constants.
> Nothing to do with TLB.
>
> Tb flags will always be calculated (Except the direct block chain I
> think, is it right? @Richard) before using the translated tb. So if your
> translated tb depend on some machine states, you
> should put it in tb flags. Otherwise, the translated tb will be misused,
> as its execution environment varies from its translation machine states.
>

Yes it's TIL day.
Thanks a lot for the review and comments. Really appreciate it.

> Zhiwei
>
> >
> >>
> >> Zhiwei
> >>
> >>> +    ctx->fcfi_lp_expected = FIELD_EX32(tb_flags, TB_FLAGS, FCFI_LP_EXPECTED);
> >>>        ctx->zero = tcg_constant_tl(0);
> >>>    }
> >>>
> >>>    static void riscv_tr_tb_start(DisasContextBase *db, CPUState *cpu)
> >>>    {
> >>> +    DisasContext *ctx = container_of(db, DisasContext, base);
> >>> +
> >>> +    if (ctx->fcfi_lp_expected) {
> >>> +        /*
> >>> +         * Since we can't look ahead to confirm that the first
> >>> +         * instruction is a legal landing pad instruction, emit
> >>> +         * compare-and-branch sequence that will be fixed-up in
> >>> +         * riscv_tr_tb_stop() to either statically hit or skip an
> >>> +         * illegal instruction exception depending on whether the
> >>> +         * flag was lowered by translation of a CJLP or JLP as
> >>> +         * the first instruction in the block.
> >>> +         */
> >>> +        TCGv_i32 immediate;
> >>> +        TCGLabel *l;
> >>> +        l = gen_new_label();
> >>> +        immediate = tcg_temp_local_new_i32();
> >>> +        tcg_gen_movi_i32(immediate, 0);
> >>> +        cfi_lp_check = tcg_last_op();
> >>> +        tcg_gen_brcondi_i32(TCG_COND_EQ, immediate, 0, l);
> >>> +        tcg_temp_free_i32(immediate);
> >>> +        gen_exception_illegal(ctx);
> >>> +        gen_set_label(l);
> >>> +        /*
> >>> +         * Despite the use of gen_exception_illegal(), the rest of
> >>> +         * the TB needs to be generated. The TCG optimizer will
> >>> +         * clean things up depending on which path ends up being
> >>> +         * active.
> >>> +         */
> >>> +        ctx->base.is_jmp = DISAS_NEXT;
> >>> +    }
> >>>    }
> >>>
> >>>    static void riscv_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu)
> >>> @@ -1225,6 +1263,7 @@ static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
> >>>    static void riscv_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
> >>>    {
> >>>        DisasContext *ctx = container_of(dcbase, DisasContext, base);
> >>> +    CPURISCVState *env = cpu->env_ptr;
> >>>
> >>>        switch (ctx->base.is_jmp) {
> >>>        case DISAS_TOO_MANY:
> >>> @@ -1235,6 +1274,19 @@ static void riscv_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
> >>>        default:
> >>>            g_assert_not_reached();
> >>>        }
> >>> +
> >>> +    if (ctx->fcfi_lp_expected) {
> >>> +        /*
> >>> +         * If the "lp expected" flag is still up, the block needs to take an
> >>> +         * illegal instruction exception.
> >>> +         */
> >>> +        tcg_set_insn_param(cfi_lp_check, 1, tcgv_i32_arg(tcg_constant_i32(1)));
> >>> +    } else {
> >>> +        /*
> >>> +        * LP instruction requirement was met, clear up LP expected
> >>> +        */
> >>> +        env->elp = NO_LP_EXPECTED;
> >>> +    }
> >>>    }
> >>>
> >>>    static void riscv_tr_disas_log(const DisasContextBase *dcbase,


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v1 RFC Zisslpcfi 7/9] target/riscv: Tracking indirect branches (fcfi) using TCG
  2023-02-09  6:24 ` [PATCH v1 RFC Zisslpcfi 7/9] target/riscv: Tracking indirect branches (fcfi) using TCG Deepak Gupta
  2023-02-15  8:55   ` LIU Zhiwei
@ 2023-02-16  6:05   ` Richard Henderson
  1 sibling, 0 replies; 42+ messages in thread
From: Richard Henderson @ 2023-02-16  6:05 UTC (permalink / raw)
  To: Deepak Gupta, Palmer Dabbelt, Alistair Francis, Bin Meng
  Cc: Kip Walker, qemu-riscv, qemu-devel

On 2/8/23 20:24, Deepak Gupta wrote:
> +    if (cpu->cfg.ext_cfi) {
> +        /*
> +         * For Forward CFI, only the expectation of a lpcll at
> +         * the start of the block is tracked (which can only happen
> +         * when FCFI is enabled for the current processor mode). A jump
> +         * or call at the end of the previous TB will have updated
> +         * env->elp to indicate the expectation.
> +         */
> +        flags = FIELD_DP32(flags, TB_FLAGS, FCFI_LP_EXPECTED,
> +                           env->elp != NO_LP_EXPECTED);

You should also check cpu_fcfien here.  We can completely ignore elp if the feature is 
disabled.  Which means that the tb flag will be set if and only if we require a landing pad.

>   static void riscv_tr_tb_start(DisasContextBase *db, CPUState *cpu)
>   {
> +    DisasContext *ctx = container_of(db, DisasContext, base);
> +
> +    if (ctx->fcfi_lp_expected) {
> +        /*
> +         * Since we can't look ahead to confirm that the first
> +         * instruction is a legal landing pad instruction, emit
> +         * compare-and-branch sequence that will be fixed-up in
> +         * riscv_tr_tb_stop() to either statically hit or skip an
> +         * illegal instruction exception depending on whether the
> +         * flag was lowered by translation of a CJLP or JLP as
> +         * the first instruction in the block.

You can "look ahead" by deferring this to riscv_tr_translate_insn.
Compare target/arm/translate-a64.c, btype_destination_ok and uses thereof.
Note that risc-v does not have the same "guarded page" bit that aa64 does.


r~


^ permalink raw reply	[flat|nested] 42+ messages in thread

end of thread, other threads:[~2023-02-16  6:05 UTC | newest]

Thread overview: 42+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <20230209062404.3582018-1-debug@rivosinc.com>
2023-02-09  6:23 ` [PATCH v1 RFC Zisslpcfi 1/9] target/riscv: adding zimops and zisslpcfi extension to RISCV cpu config Deepak Gupta
2023-02-11  3:19   ` weiwei
2023-02-13  3:15     ` Deepak Gupta
2023-02-15  2:52   ` LIU Zhiwei
2023-02-15 20:47     ` Deepak Gupta
2023-02-16  1:46       ` LIU Zhiwei
2023-02-16  4:20         ` Richard Henderson
2023-02-09  6:23 ` [PATCH v1 RFC Zisslpcfi 2/9] target/riscv: zisslpcfi CSR, bit positions and other definitions Deepak Gupta
2023-02-11  3:32   ` weiwei
2023-02-13  3:21     ` Deepak Gupta
2023-02-15  3:31   ` LIU Zhiwei
2023-02-15 20:42     ` Deepak Gupta
2023-02-09  6:23 ` [PATCH v1 RFC Zisslpcfi 3/9] target/riscv: implements CSRs and new bits in existing CSRs in zisslpcfi Deepak Gupta
2023-02-15  5:47   ` LIU Zhiwei
2023-02-15  6:24     ` LIU Zhiwei
2023-02-15 23:42       ` Deepak Gupta
2023-02-15 23:33     ` Deepak Gupta
2023-02-16  0:02       ` Richard Henderson
2023-02-16  1:38         ` Deepak Gupta
2023-02-16  2:43           ` Richard Henderson
2023-02-16  5:20             ` Deepak Gupta
2023-02-09  6:23 ` [PATCH v1 RFC Zisslpcfi 4/9] target/riscv: helper functions for forward and backward cfi Deepak Gupta
2023-02-15  6:26   ` LIU Zhiwei
2023-02-15 23:35     ` Deepak Gupta
2023-02-09  6:24 ` [PATCH v1 RFC Zisslpcfi 5/9] target/riscv: state save and restore of zisslppcfi state Deepak Gupta
2023-02-15  6:10   ` LIU Zhiwei
2023-02-15 23:13     ` Deepak Gupta
2023-02-09  6:24 ` [PATCH v1 RFC Zisslpcfi 6/9] target/riscv: MMU changes for back cfi's shadow stack Deepak Gupta
2023-02-15  8:43   ` LIU Zhiwei
2023-02-15 23:57     ` Deepak Gupta
2023-02-16  2:36       ` LIU Zhiwei
2023-02-16  5:43         ` Deepak Gupta
2023-02-09  6:24 ` [PATCH v1 RFC Zisslpcfi 7/9] target/riscv: Tracking indirect branches (fcfi) using TCG Deepak Gupta
2023-02-15  8:55   ` LIU Zhiwei
2023-02-16  0:02     ` Deepak Gupta
2023-02-16  2:43       ` LIU Zhiwei
2023-02-16  5:45         ` Deepak Gupta
2023-02-16  6:05   ` Richard Henderson
2023-02-09  6:24 ` [PATCH v1 RFC Zisslpcfi 8/9] target/riscv: Instructions encodings, implementation and handlers Deepak Gupta
2023-02-15 10:43   ` LIU Zhiwei
2023-02-09  6:24 ` [PATCH v1 RFC Zisslpcfi 9/9] target/riscv: diassembly support for zisslpcfi instructions Deepak Gupta
2023-02-09  6:29 [PATCH v1 RFC Zisslpcfi 0/9] zimops and zisslpcfi extension to riscv Deepak Gupta
2023-02-09  6:29 ` [PATCH v1 RFC Zisslpcfi 4/9] target/riscv: helper functions for forward and backward cfi Deepak Gupta

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).