From 9ba5bd3171e97560bc28fe555ff7b8404272a3bb Mon Sep 17 00:00:00 2001 From: SuHsien Ho Date: Wed, 4 Oct 2023 15:00:49 +0800 Subject: Add Zicfiss extension from CFI extension, v0.4.0 1. Add EXT_ZICFISS for enable Zicfiss with zicfiss extension name. 2. Add new software exception with tval 3 for shadow stack. 3. Implement sspush_x1/sspush_x5/sspopchk_x1/sspopchk_x5/ssrdp/ssamoswap_w/ssamoswap_d. 4. Implement c_sspush_x1/c_sspopchk_x5 in c_lui.h which has same encoding. 5. Add new special access type ss_access in xlate_flags_t for checking special read/write permission in SS(Shadow Stack) page. 6. Add new ss_load/ss_store/ssamoswap to enable ss_access flag. 7. Check special pte(xwr=010) of SS page. --- riscv/csrs.cc | 10 ++++++++++ riscv/csrs.h | 7 +++++++ riscv/decode.h | 1 + riscv/encoding.h | 1 + riscv/insns/c_lui.h | 8 +++++++- riscv/insns/c_sspopchk_x5.h | 5 +++++ riscv/insns/c_sspush_x1.h | 5 +++++ riscv/insns/ssamoswap_d.h | 7 +++++++ riscv/insns/ssamoswap_w.h | 7 +++++++ riscv/insns/sspopchk_x1.h | 7 +++++++ riscv/insns/sspopchk_x5.h | 1 + riscv/insns/sspush_x1.h | 7 +++++++ riscv/insns/sspush_x5.h | 1 + riscv/insns/ssrdp.h | 7 +++++++ riscv/isa_parser.h | 1 + riscv/mmu.cc | 29 ++++++++++++++++++++++++++--- riscv/mmu.h | 32 +++++++++++++++++++++++++++++++- riscv/overlap_list.h | 7 +++++++ riscv/processor.cc | 14 +++++++++++--- riscv/processor.h | 2 ++ riscv/riscv.mk.in | 12 ++++++++++++ riscv/zicfiss.h | 31 +++++++++++++++++++++++++++++++ 22 files changed, 194 insertions(+), 8 deletions(-) create mode 100644 riscv/insns/c_sspopchk_x5.h create mode 100644 riscv/insns/c_sspush_x1.h create mode 100644 riscv/insns/ssamoswap_d.h create mode 100644 riscv/insns/ssamoswap_w.h create mode 100644 riscv/insns/sspopchk_x1.h create mode 100644 riscv/insns/sspopchk_x5.h create mode 100644 riscv/insns/sspush_x1.h create mode 100644 riscv/insns/sspush_x5.h create mode 100644 riscv/insns/ssrdp.h create mode 100644 riscv/zicfiss.h (limited to 'riscv') diff --git a/riscv/csrs.cc b/riscv/csrs.cc index 4a612e5..2bd8b0d 100644 --- a/riscv/csrs.cc +++ b/riscv/csrs.cc @@ -1757,3 +1757,13 @@ bool hvip_csr_t::unlogged_write(const reg_t val) noexcept { state->mip->write_with_mask(MIP_VSSIP, val); // hvip.VSSIP is an alias of mip.VSSIP return basic_csr_t::unlogged_write(val & (MIP_VSEIP | MIP_VSTIP)); } + +ssp_csr_t::ssp_csr_t(processor_t* const proc, const reg_t addr, const reg_t mask, const reg_t init): + masked_csr_t(proc, addr, mask, init) { +} + +void ssp_csr_t::verify_permissions(insn_t insn, bool write) const { + masked_csr_t::verify_permissions(insn, write); + DECLARE_XENVCFG_VARS(SSE); + require_envcfg(SSE); +} diff --git a/riscv/csrs.h b/riscv/csrs.h index 2595243..0920544 100644 --- a/riscv/csrs.h +++ b/riscv/csrs.h @@ -871,4 +871,11 @@ class hvip_csr_t : public basic_csr_t { }; typedef std::shared_ptr hvip_csr_t_p; + +// ssp CSR provided by CFI Zicfiss extension +class ssp_csr_t final : public masked_csr_t { + public: + ssp_csr_t(processor_t* const proc, const reg_t addr, const reg_t mask, const reg_t init); + virtual void verify_permissions(insn_t insn, bool write) const override; +}; #endif diff --git a/riscv/decode.h b/riscv/decode.h index cd1c0a1..f36c04e 100644 --- a/riscv/decode.h +++ b/riscv/decode.h @@ -26,6 +26,7 @@ const int NCSR = 4096; #define X_RA 1 #define X_SP 2 +#define X_T0 5 #define X_S0 8 #define X_A0 10 #define X_A1 11 diff --git a/riscv/encoding.h b/riscv/encoding.h index ff3f743..d600fa2 100644 --- a/riscv/encoding.h +++ b/riscv/encoding.h @@ -348,6 +348,7 @@ /* software check exception xtval codes */ #define LANDING_PAD_FAULT 2 +#define SHADOW_STACK_FAULT 3 #ifdef __riscv diff --git a/riscv/insns/c_lui.h b/riscv/insns/c_lui.h index 7a82c13..98a8898 100644 --- a/riscv/insns/c_lui.h +++ b/riscv/insns/c_lui.h @@ -5,7 +5,13 @@ if (insn.rvc_rd() == 2) { // c.addi16sp } else if (insn.rvc_imm() != 0) { // c.lui WRITE_RD(insn.rvc_imm() << 12); } else if ((insn.rvc_rd() & 0x11) == 1) { // c.mop.N - #include "c_mop_N.h" + if (insn.rvc_rd() == 5 && p->extension_enabled(EXT_ZICFISS)) { + #include "c_sspopchk_x5.h" + } else if (insn.rvc_rd() == 1 && p->extension_enabled(EXT_ZICFISS)) { + #include "c_sspush_x1.h" + } else { + #include "c_mop_N.h" + } } else { require(false); } diff --git a/riscv/insns/c_sspopchk_x5.h b/riscv/insns/c_sspopchk_x5.h new file mode 100644 index 0000000..d379f2c --- /dev/null +++ b/riscv/insns/c_sspopchk_x5.h @@ -0,0 +1,5 @@ +#include "zicfiss.h" + +if (xSSE()) { + POP_VALUE_FROM_SS_AND_CHECK(READ_REG(X_T0)); +} diff --git a/riscv/insns/c_sspush_x1.h b/riscv/insns/c_sspush_x1.h new file mode 100644 index 0000000..3645a9e --- /dev/null +++ b/riscv/insns/c_sspush_x1.h @@ -0,0 +1,5 @@ +#include "zicfiss.h" + +if (xSSE()) { + PUSH_VALUE_TO_SS(RA); +} diff --git a/riscv/insns/ssamoswap_d.h b/riscv/insns/ssamoswap_d.h new file mode 100644 index 0000000..10ea5ef --- /dev/null +++ b/riscv/insns/ssamoswap_d.h @@ -0,0 +1,7 @@ +require_extension(EXT_ZICFISS); +require_extension('A'); +require_rv64; + +DECLARE_XENVCFG_VARS(SSE); +require_envcfg(SSE); +WRITE_RD(MMU.ssamoswap(RS1, RS2)); diff --git a/riscv/insns/ssamoswap_w.h b/riscv/insns/ssamoswap_w.h new file mode 100644 index 0000000..3cdefc7 --- /dev/null +++ b/riscv/insns/ssamoswap_w.h @@ -0,0 +1,7 @@ +require_extension(EXT_ZICFISS); +require_extension('A'); + +DECLARE_XENVCFG_VARS(SSE); +require_envcfg(SSE); +WRITE_RD(sext32(MMU.ssamoswap(RS1, RS2))); + diff --git a/riscv/insns/sspopchk_x1.h b/riscv/insns/sspopchk_x1.h new file mode 100644 index 0000000..6ea4c22 --- /dev/null +++ b/riscv/insns/sspopchk_x1.h @@ -0,0 +1,7 @@ +#include "zicfiss.h" + +if (xSSE()) { + POP_VALUE_FROM_SS_AND_CHECK(RS1); +} else { + #include "mop_r_N.h" +} diff --git a/riscv/insns/sspopchk_x5.h b/riscv/insns/sspopchk_x5.h new file mode 100644 index 0000000..78218b3 --- /dev/null +++ b/riscv/insns/sspopchk_x5.h @@ -0,0 +1 @@ +#include "sspopchk_x1.h" diff --git a/riscv/insns/sspush_x1.h b/riscv/insns/sspush_x1.h new file mode 100644 index 0000000..67ad9bb --- /dev/null +++ b/riscv/insns/sspush_x1.h @@ -0,0 +1,7 @@ +#include "zicfiss.h" + +if (xSSE()) { + PUSH_VALUE_TO_SS(RS2); +} else { + #include "mop_rr_N.h" +} diff --git a/riscv/insns/sspush_x5.h b/riscv/insns/sspush_x5.h new file mode 100644 index 0000000..235333d --- /dev/null +++ b/riscv/insns/sspush_x5.h @@ -0,0 +1 @@ +#include "sspush_x1.h" diff --git a/riscv/insns/ssrdp.h b/riscv/insns/ssrdp.h new file mode 100644 index 0000000..20b0856 --- /dev/null +++ b/riscv/insns/ssrdp.h @@ -0,0 +1,7 @@ +#include "zicfiss.h" + +if (xSSE()) { + WRITE_RD(STATE.ssp->read()); +} else { + #include "mop_r_N.h" +} diff --git a/riscv/isa_parser.h b/riscv/isa_parser.h index f02b55d..72e8211 100644 --- a/riscv/isa_parser.h +++ b/riscv/isa_parser.h @@ -88,6 +88,7 @@ typedef enum { EXT_ZALASR, EXT_SSQOSID, EXT_ZICFILP, + EXT_ZICFISS, NUM_ISA_EXTENSIONS } isa_extension_t; diff --git a/riscv/mmu.cc b/riscv/mmu.cc index 1a0c830..8660b71 100644 --- a/riscv/mmu.cc +++ b/riscv/mmu.cc @@ -63,7 +63,7 @@ reg_t mmu_t::translate(mem_access_info_t access_info, reg_t len) reg_t mode = (reg_t) access_info.effective_priv; reg_t paddr = walk(access_info) | (addr & (PGSIZE-1)); - if (!pmp_ok(paddr, len, type, mode)) + if (!pmp_ok(paddr, len, access_info.flags.ss_access ? STORE : type, mode)) throw_access_exception(virt, addr, type); return paddr; } @@ -491,6 +491,15 @@ reg_t mmu_t::walk(mem_access_info_t access_info) reg_t page_mask = (reg_t(1) << PGSHIFT) - 1; reg_t satp = proc->get_state()->satp->readvirt(virt); vm_info vm = decode_vm_info(proc->get_const_xlen(), false, mode, satp); + + bool ss_access = access_info.flags.ss_access; + + if (ss_access) { + if (vm.levels == 0) + trap_store_access_fault(virt, addr, 0, 0); + type = STORE; + } + if (vm.levels == 0) return s2xlate(addr, addr & ((reg_t(2) << (proc->xlen-1))-1), type, type, virt, hlvx, false) & ~page_mask; // zero-extend from xlen @@ -516,6 +525,7 @@ reg_t mmu_t::walk(mem_access_info_t access_info) reg_t ppn = (pte & ~reg_t(PTE_ATTR)) >> PTE_PPN_SHIFT; bool pbmte = virt ? (proc->get_state()->henvcfg->read() & HENVCFG_PBMTE) : (proc->get_state()->menvcfg->read() & MENVCFG_PBMTE); bool hade = virt ? (proc->get_state()->henvcfg->read() & HENVCFG_ADUE) : (proc->get_state()->menvcfg->read() & MENVCFG_ADUE); + bool sse = virt ? (proc->get_state()->henvcfg->read() & HENVCFG_SSE) : (proc->get_state()->menvcfg->read() & MENVCFG_SSE); if (pte & PTE_RSVD) { break; @@ -531,11 +541,24 @@ reg_t mmu_t::walk(mem_access_info_t access_info) base = ppn << PGSHIFT; } else if ((pte & PTE_U) ? s_mode && (type == FETCH || !sum) : !s_mode) { break; - } else if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) { + } else if (!(pte & PTE_V) || + (!(pte & PTE_R) && (pte & PTE_W) && ((!sse && !(pte & PTE_X)) || (pte & PTE_X)))) { + // invalid + // not shadow stack access xwr=110 or xwr=010 page cause page fault + // shadow stack access with PTE_X moved to following check break; + } else if ((!(pte & PTE_R) && (pte & PTE_W) && !(pte & PTE_X)) && (type == STORE && !ss_access)) { + // not shadow stack store and xwr = 010 cause access-fault + throw trap_store_access_fault(virt, addr, 0, 0); + } else if ((!(pte & PTE_R) && (pte & PTE_W) && !(pte & PTE_X)) && type == FETCH) { + // fetch from shadow stack pages cause instruction access-fault + throw trap_instruction_access_fault(virt, addr, 0, 0); + } else if ((((pte & PTE_R) && (pte & PTE_W)) || (pte & PTE_X)) && ss_access) { + // shadow stack access cause store access fault if xwr!=010 and xwr!=001 + throw trap_store_access_fault(virt, addr, 0, 0); } else if (type == FETCH || hlvx ? !(pte & PTE_X) : type == LOAD ? !(pte & PTE_R) && !(mxr && (pte & PTE_X)) : - !((pte & PTE_R) && (pte & PTE_W))) { + !(pte & PTE_W)) { break; } else if ((ppn & ((reg_t(1) << ptshift) - 1)) != 0) { break; diff --git a/riscv/mmu.h b/riscv/mmu.h index 4404e4c..b218bf6 100644 --- a/riscv/mmu.h +++ b/riscv/mmu.h @@ -42,9 +42,10 @@ struct xlate_flags_t { const bool forced_virt : 1 {false}; const bool hlvx : 1 {false}; const bool lr : 1 {false}; + const bool ss_access : 1 {false}; bool is_special_access() const { - return forced_virt || hlvx || lr; + return forced_virt || hlvx || lr || ss_access; } }; @@ -127,6 +128,14 @@ public: return load(addr, {.forced_virt=true, .hlvx=true}); } + // shadow stack load + template + T ss_load(reg_t addr) { + if ((addr & (sizeof(T) - 1)) != 0) + throw trap_store_access_fault((proc) ? proc->state.v : false, addr, 0, 0); + return load(addr, {.forced_virt=false, .hlvx=false, .lr=false, .ss_access=true}); + } + template void ALWAYS_INLINE store(reg_t addr, T val, xlate_flags_t xlate_flags = {}) { reg_t vpn = addr >> PGSHIFT; @@ -149,6 +158,14 @@ public: store(addr, val, {.forced_virt=true}); } + // shadow stack store + template + void ss_store(reg_t addr, T val) { + if ((addr & (sizeof(T) - 1)) != 0) + throw trap_store_access_fault((proc) ? proc->state.v : false, addr, 0, 0); + store(addr, val, {.forced_virt=false, .hlvx=false, .lr=false, .ss_access=true}); + } + // AMO/Zicbom faults should be reported as store faults #define convert_load_traps_to_store_traps(BODY) \ try { \ @@ -175,6 +192,19 @@ public: }) } + // for shadow stack amoswap + template + T ssamoswap(reg_t addr, reg_t value) { + bool forced_virt = false; + bool hlvx = false; + bool lr = false; + bool ss_access = true; + store_slow_path(addr, sizeof(T), nullptr, {forced_virt, hlvx, lr, ss_access}, false, true); + auto data = load(addr, {forced_virt, hlvx, lr, ss_access}); + store(addr, value, {forced_virt, hlvx, lr, ss_access}); + return data; + } + template T amo_compare_and_swap(reg_t addr, T comp, T swap) { convert_load_traps_to_store_traps({ diff --git a/riscv/overlap_list.h b/riscv/overlap_list.h index 3084c36..6c943bd 100644 --- a/riscv/overlap_list.h +++ b/riscv/overlap_list.h @@ -22,3 +22,10 @@ DECLARE_OVERLAP_INSN(rstsa32, EXT_ZPN) DECLARE_OVERLAP_INSN(srli32_u, EXT_ZPN) DECLARE_OVERLAP_INSN(umax32, EXT_ZPN) DECLARE_OVERLAP_INSN(lpad, EXT_ZICFILP) +DECLARE_OVERLAP_INSN(mop_r_28, EXT_ZIMOP) +DECLARE_OVERLAP_INSN(mop_r_N, EXT_ZIMOP) +DECLARE_OVERLAP_INSN(mop_rr_7, EXT_ZIMOP) +DECLARE_OVERLAP_INSN(mop_rr_N, EXT_ZIMOP) +DECLARE_OVERLAP_INSN(c_sspush_x1, EXT_ZICFISS) +DECLARE_OVERLAP_INSN(c_sspopchk_x5, EXT_ZICFISS) +DECLARE_OVERLAP_INSN(c_mop_N, EXT_ZCMOP) diff --git a/riscv/processor.cc b/riscv/processor.cc index 3a1eadb..7a63f3c 100644 --- a/riscv/processor.cc +++ b/riscv/processor.cc @@ -453,7 +453,8 @@ void state_t::reset(processor_t* const proc, reg_t max_isa) (proc->extension_enabled(EXT_SVADU) ? MENVCFG_ADUE: 0) | (proc->extension_enabled(EXT_SVPBMT) ? MENVCFG_PBMTE : 0) | (proc->extension_enabled(EXT_SSTC) ? MENVCFG_STCE : 0) | - (proc->extension_enabled(EXT_ZICFILP) ? MENVCFG_LPE : 0); + (proc->extension_enabled(EXT_ZICFILP) ? MENVCFG_LPE : 0) | + (proc->extension_enabled(EXT_ZICFISS) ? MENVCFG_SSE : 0); const reg_t menvcfg_init = (proc->extension_enabled(EXT_SVPBMT) ? MENVCFG_PBMTE : 0); menvcfg = std::make_shared(proc, CSR_MENVCFG, menvcfg_mask, menvcfg_init); if (xlen == 32) { @@ -464,14 +465,16 @@ void state_t::reset(processor_t* const proc, reg_t max_isa) } const reg_t senvcfg_mask = (proc->extension_enabled(EXT_ZICBOM) ? SENVCFG_CBCFE | SENVCFG_CBIE : 0) | (proc->extension_enabled(EXT_ZICBOZ) ? SENVCFG_CBZE : 0) | - (proc->extension_enabled(EXT_ZICFILP) ? SENVCFG_LPE : 0); + (proc->extension_enabled(EXT_ZICFILP) ? SENVCFG_LPE : 0) | + (proc->extension_enabled(EXT_ZICFISS) ? SENVCFG_SSE : 0); csrmap[CSR_SENVCFG] = senvcfg = std::make_shared(proc, CSR_SENVCFG, senvcfg_mask, 0); const reg_t henvcfg_mask = (proc->extension_enabled(EXT_ZICBOM) ? HENVCFG_CBCFE | HENVCFG_CBIE : 0) | (proc->extension_enabled(EXT_ZICBOZ) ? HENVCFG_CBZE : 0) | (proc->extension_enabled(EXT_SVADU) ? HENVCFG_ADUE: 0) | (proc->extension_enabled(EXT_SVPBMT) ? HENVCFG_PBMTE : 0) | (proc->extension_enabled(EXT_SSTC) ? HENVCFG_STCE : 0) | - (proc->extension_enabled(EXT_ZICFILP) ? HENVCFG_LPE : 0); + (proc->extension_enabled(EXT_ZICFILP) ? HENVCFG_LPE : 0) | + (proc->extension_enabled(EXT_ZICFISS) ? HENVCFG_SSE : 0); const reg_t henvcfg_init = (proc->extension_enabled(EXT_SVPBMT) ? HENVCFG_PBMTE : 0); henvcfg = std::make_shared(proc, CSR_HENVCFG, henvcfg_mask, henvcfg_init, menvcfg); if (xlen == 32) { @@ -536,6 +539,11 @@ void state_t::reset(processor_t* const proc, reg_t max_isa) if (proc->extension_enabled(EXT_ZCMT)) csrmap[CSR_JVT] = jvt = std::make_shared(proc, CSR_JVT, 0); + if (proc->extension_enabled(EXT_ZICFISS)) { + reg_t ssp_mask = -reg_t(xlen / 8); + csrmap[CSR_SSP] = ssp = std::make_shared(proc, CSR_SSP, ssp_mask, 0); + } + // Smcsrind / Sscsrind sscsrind_reg_csr_t::sscsrind_reg_csr_t_p mireg[6]; diff --git a/riscv/processor.h b/riscv/processor.h index ec42418..f3e5294 100644 --- a/riscv/processor.h +++ b/riscv/processor.h @@ -173,6 +173,8 @@ struct state_t csr_t_p srmcfg; + csr_t_p ssp; + bool serialized; // whether timer CSRs are in a well-defined state // When true, execute a single instruction and then enter debug mode. This diff --git a/riscv/riscv.mk.in b/riscv/riscv.mk.in index 0c4a14b..f388426 100644 --- a/riscv/riscv.mk.in +++ b/riscv/riscv.mk.in @@ -1395,6 +1395,17 @@ riscv_insn_ext_zimop = \ riscv_insn_ext_zicfilp = \ lpad +riscv_insn_ext_zicfiss = \ + sspush_x1 \ + sspush_x5 \ + sspopchk_x1 \ + sspopchk_x5 \ + ssrdp \ + ssamoswap_w \ + ssamoswap_d \ + c_sspush_x1 \ + c_sspopchk_x5 \ + riscv_insn_ext_zvk = \ $(riscv_insn_ext_zvbb) \ $(riscv_insn_ext_zvbc) \ @@ -1435,6 +1446,7 @@ riscv_insn_list = \ $(riscv_insn_svinval) \ $(riscv_insn_ext_zimop) \ $(riscv_insn_ext_zicfilp) \ + $(riscv_insn_ext_zicfiss) \ riscv_gen_srcs = $(addsuffix .cc,$(riscv_insn_list)) diff --git a/riscv/zicfiss.h b/riscv/zicfiss.h new file mode 100644 index 0000000..83c166d --- /dev/null +++ b/riscv/zicfiss.h @@ -0,0 +1,31 @@ +// See LICENSE for license details. + +#ifndef _RISCV_ZICFISS_H +#define _RISCV_ZICFISS_H + +#define xSSE() \ + ((STATE.prv != PRV_M) && get_field(STATE.menvcfg->read(), MENVCFG_SSE) && \ + p->extension_enabled('S') && \ + ((STATE.v && get_field(STATE.henvcfg->read(), HENVCFG_SSE)) || !STATE.v) && \ + (((STATE.prv == PRV_U) && get_field(STATE.senvcfg->read(), SENVCFG_SSE)) || (STATE.prv != PRV_U))) + +#define PUSH_VALUE_TO_SS(value) ({ \ + reg_t push_value = (value); \ + reg_t push_ssp_addr = STATE.ssp->read() - xlen / 8; \ + if (xlen == 32) \ + MMU.ss_store(push_ssp_addr, push_value); \ + else \ + MMU.ss_store(push_ssp_addr, push_value); \ + STATE.ssp->write(push_ssp_addr); \ + }) + +#define POP_VALUE_FROM_SS_AND_CHECK(value) \ + reg_t shadow_return_addr; \ + if (xlen == 32) \ + shadow_return_addr = MMU.ss_load(STATE.ssp->read()); \ + else \ + shadow_return_addr = MMU.ss_load(STATE.ssp->read()); \ + software_check(value == shadow_return_addr, SHADOW_STACK_FAULT); \ + STATE.ssp->write(STATE.ssp->read() + xlen / 8); + +#endif -- cgit v1.1