Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions disasm/isa_parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -247,14 +247,20 @@ isa_parser_t::isa_parser_t(const char* str, const char *priv)
} else if (ext_str == "zkr") {
extension_table[EXT_ZKR] = true;
} else if (ext_str == "zkt") {
} else if (ext_str == "smcfiss") {
extension_table[EXT_SMCFISS] = true;
} else if (ext_str == "smepmp") {
extension_table[EXT_SMEPMP] = true;
} else if (ext_str == "smstateen") {
extension_table[EXT_SMSTATEEN] = true;
} else if (ext_str == "smpmpind") {
extension_table[EXT_SMPMPIND] = true;
} else if (ext_str == "smpmpmt") {
extension_table[EXT_SMPMPMT] = true;
} else if (ext_str == "smrnmi") {
extension_table[EXT_SMRNMI] = true;
} else if (ext_str == "smucfiss") {
extension_table[EXT_SMUCFISS] = true;
} else if (ext_str == "sscofpmf") {
extension_table[EXT_SSCOFPMF] = true;
} else if (ext_str == "svadu") {
Expand Down Expand Up @@ -562,6 +568,15 @@ isa_parser_t::isa_parser_t(const char* str, const char *priv)
!extension_table[EXT_ZCMOP]) {
bad_isa_string(str, "'Zicfiss' extension requires 'Zcmop' extension when `Zca` is supported");
}
if (extension_table[EXT_SMCFISS] && !extension_table[EXT_ZICFISS]) {
bad_isa_string(str, "'Smcfiss' extension requires 'Zicfiss'");
}
if (extension_table[EXT_SMCFISS] && !extension_table[EXT_SMPMPIND]) {
bad_isa_string(str, "'Smcfiss' extension requires 'Smpmpind'");
}
if (extension_table[EXT_SMUCFISS] && !extension_table[EXT_SMCFISS]) {
bad_isa_string(str, "'Smucfiss' extension requires 'Smcfiss'");
}
#ifdef WORDS_BIGENDIAN
// Access to the vector registers as element groups is unimplemented on big-endian setups.
if (extension_table[EXT_ZVKG] || extension_table[EXT_ZVKNHA] || extension_table[EXT_ZVKNHB] ||
Expand Down Expand Up @@ -607,6 +622,10 @@ isa_parser_t::isa_parser_t(const char* str, const char *priv)
extension_table['U'] = user;
extension_table['S'] = supervisor;

if (extension_table[EXT_SMUCFISS] && (extension_table['S'] || !extension_table['U'])) {
bad_isa_string(str, "'Smucfiss' extension is only supported for M+U systems");
}

if (extension_table['H'] && !supervisor)
bad_isa_string(str, "'H' extension requires S mode");

Expand Down
20 changes: 18 additions & 2 deletions riscv/csr_init.cc
Original file line number Diff line number Diff line change
Expand Up @@ -441,13 +441,29 @@ void state_t::csr_init(processor_t* const proc, reg_t max_isa)
csr_t_p miselect = std::make_shared<basic_csr_t>(proc, CSR_MISELECT, 0);
add_csr(CSR_MISELECT, miselect);

sscsrind_reg_csr_t::sscsrind_reg_csr_t_p mireg;
sscsrind_reg_csr_t::sscsrind_reg_csr_t_p mireg, mireg2;
add_csr(CSR_MIREG, mireg = std::make_shared<sscsrind_reg_csr_t>(proc, CSR_MIREG, miselect));
if (proc->extension_enabled_const(EXT_SMAIA))
add_iprio_proxy(proc, mireg);
const reg_t mireg_csrs[] = { CSR_MIREG2, CSR_MIREG3, CSR_MIREG4, CSR_MIREG5, CSR_MIREG6 };
add_csr(CSR_MIREG2, mireg2 = std::make_shared<sscsrind_reg_csr_t>(proc, CSR_MIREG2, miselect));
const reg_t mireg_csrs[] = { CSR_MIREG3, CSR_MIREG4, CSR_MIREG5, CSR_MIREG6 };
for (auto csr : mireg_csrs)
add_csr(csr, std::make_shared<sscsrind_reg_csr_t>(proc, csr, miselect));

if (proc->extension_enabled_const(EXT_SMPMPIND)) {
// For indirect access to PMP registers, `miselect` selects the target PMP
// entry; `mireg` accesses its `pmpaddr` register, and `mireg2` accesses its
// `pmpcfg` register. Attempts to access `mireg3` through `mireg6` raise an
// illegal instruction exception.
// The mireg2 (mireg_csrs[0]) proxies to ind_pmpcfg_csr_t objects as there
// is no direct proxy for these registers. In the ind_pmpcfg_csr_t object,
// the address member holds the entry number (i).
for (int i = 0; i < max_pmp; ++i) {
mireg->add_ireg_proxy(MISELECT_PMPIND_START + i, pmpaddr[i]);
auto ind_pmpcfg = std::make_shared<ind_pmpcfg_csr_t>(proc, i);
mireg2->add_ireg_proxy(MISELECT_PMPIND_START + i, ind_pmpcfg);
}
}
}

if (proc->extension_enabled_const(EXT_SSCSRIND)) {
Expand Down
144 changes: 107 additions & 37 deletions riscv/csrs.cc
Original file line number Diff line number Diff line change
Expand Up @@ -182,17 +182,43 @@ bool pmpaddr_csr_t::subset_match(reg_t addr, reg_t len) const noexcept {
return !(is_tor ? tor_homogeneous : napot_homogeneous);
}

bool pmpaddr_csr_t::access_ok(access_type type, reg_t mode, bool hlvx) const noexcept {
bool pmpaddr_csr_t::access_ok(access_type type, reg_t mode, bool hlvx, bool ss_access) const noexcept {
const uint8_t xwr = cfg & (PMP_X | PMP_W | PMP_R);
const bool cfgx = cfg & PMP_X;
const bool cfgw = cfg & PMP_W;
const bool cfgr = cfg & PMP_R;
const bool cfgl = cfg & PMP_L;

const bool prvm = mode == PRV_M;

const bool typer = type == LOAD;
const bool typex = type == FETCH;
const bool typew = type == STORE;
const bool prvm = mode == PRV_M;
const bool prvu = mode == PRV_U;
const bool msse = state->mseccfg->get_msse();
const bool usse = ((state->menvcfg->read() & MENVCFG_SSE) != 0) &&
proc->extension_enabled(EXT_SMUCFISS);
const bool is_m_ss_rgn = E && (xwr == PMP_W) && msse;
const bool is_u_ss_rgn = E && (xwr == (PMP_W | PMP_X)) && usse;
/*
* Shadow-stack access rules (Smcfiss / Smucfiss):
* - Shadow-stack accesses with effective privilege mode M are permitted
* only when accessing an M-mode shadow-stack region. Loads with effective
* privilege mode M are permitted to read an M-mode shadow-stack region.
* - When Smucfiss is implemented, shadow-stack accesses with effective
* privilege mode U are permitted only when accessing a U-mode shadow-stack
* region. Loads with effective privilege mode U are permitted to read a
* U-mode shadow-stack region.
* - All other accesses (stores, instruction fetches, or accesses from an
* incorrect privilege mode) to M-mode or U-mode shadow-stack regions are
* disallowed.
* - When E = 1 and the PMP entry does not denote a valid shadow-stack
* region, all accesses are disallowed.
*/
if (ss_access && prvm) return is_m_ss_rgn;
if (ss_access && prvu && proc->extension_enabled(EXT_SMUCFISS)) return is_u_ss_rgn;
if (is_m_ss_rgn) return prvm && typer && !hlvx;
if (is_u_ss_rgn) return prvu && typer && !hlvx;
if (E) return false;

const bool normal_rwx = (typer && cfgr && (!hlvx || cfgx)) || (typew && cfgw) || (typex && cfgx);
const bool mseccfg_mml = state->mseccfg->get_mml();

Expand Down Expand Up @@ -238,54 +264,90 @@ reg_t pmpcfg_csr_t::read() const noexcept {
return cfg_res;
}

void pmpcfg_csr_t::update_pmpcfg_entry(const size_t index, const uint8_t cfg_in, const bool E) {
const bool locked = (state->pmpaddr[index]->cfg & PMP_L);
const bool rlb = state->mseccfg->get_rlb();
const bool mml = state->mseccfg->get_mml();
uint8_t cfg = cfg_in;
if (rlb || !locked) {
// Drop R=0 W=1 when MML = 0
// Remove the restriction when MML = 1 or, when E=1
if (!mml && !E) {
cfg &= ~PMP_W | ((cfg & PMP_R) ? PMP_W : 0);
}
// Disallow A=NA4 when granularity > 4
if (proc->lg_pmp_granularity != PMP_SHIFT && (cfg & PMP_A) == PMP_NA4)
cfg |= PMP_NAPOT;
// MT value 0x3 is reserved
if (get_field(cfg, PMP_MT) == 0x3)
cfg = set_field(cfg, PMP_MT, 0);
/*
* Adding a rule with executable privileges that either is M-mode-only or a locked Shared-Region
* is not possible and such pmpcfg writes are ignored, leaving pmpcfg unchanged.
* This restriction can be temporarily lifted e.g. during the boot process, by setting mseccfg.RLB.
*/
const bool cfgx = cfg & PMP_X;
const bool cfgw = cfg & PMP_W;
const bool cfgr = cfg & PMP_R;
if (rlb || !(mml && ((cfg & PMP_L) // M-mode-only or a locked Shared-Region
&& !(cfgx && cfgw && cfgr) // RWX = 111 is allowed
&& (cfgx || (cfgw && !cfgr)) // X=1 or RW=01 is not allowed
&& !E // Only apply this restruction when E=0
))) {
state->pmpaddr[index]->cfg = cfg;
state->pmpaddr[index]->E = E;
}
}
return;
}

bool pmpcfg_csr_t::unlogged_write(const reg_t val) noexcept {
if (proc->n_pmp == 0)
return false;

bool write_success = false;
const bool rlb = state->mseccfg->get_rlb();
const bool mml = state->mseccfg->get_mml();
for (size_t i0 = (address - CSR_PMPCFG0) * 4, i = i0; i < i0 + proc->get_xlen() / 8; i++) {
if (i < proc->n_pmp) {
const bool locked = (state->pmpaddr[i]->cfg & PMP_L);
if (rlb || !locked) {
uint8_t all_cfg_fields = (PMP_R | PMP_W | PMP_X | PMP_A |
(proc->extension_enabled(EXT_SMPMPMT) ? PMP_MT : 0) |
uint8_t all_cfg_fields = (PMP_R | PMP_W | PMP_X | PMP_A |
(proc->extension_enabled(EXT_SMPMPMT) ? PMP_MT : 0) |
PMP_L);
uint8_t cfg = (val >> (8 * (i - i0))) & all_cfg_fields;
// Drop R=0 W=1 when MML = 0
// Remove the restriction when MML = 1
if (!mml) {
cfg &= ~PMP_W | ((cfg & PMP_R) ? PMP_W : 0);
}
// Disallow A=NA4 when granularity > 4
if (proc->lg_pmp_granularity != PMP_SHIFT && (cfg & PMP_A) == PMP_NA4)
cfg |= PMP_NAPOT;
// MT value 0x3 is reserved
if (get_field(cfg, PMP_MT) == 0x3)
cfg = set_field(cfg, PMP_MT, 0);
/*
* Adding a rule with executable privileges that either is M-mode-only or a locked Shared-Region
* is not possible and such pmpcfg writes are ignored, leaving pmpcfg unchanged.
* This restriction can be temporarily lifted e.g. during the boot process, by setting mseccfg.RLB.
*/
const bool cfgx = cfg & PMP_X;
const bool cfgw = cfg & PMP_W;
const bool cfgr = cfg & PMP_R;
if (rlb || !(mml && ((cfg & PMP_L) // M-mode-only or a locked Shared-Region
&& !(cfgx && cfgw && cfgr) // RWX = 111 is allowed
&& (cfgx || (cfgw && !cfgr)) // X=1 or RW=01 is not allowed
))) {
state->pmpaddr[i]->cfg = cfg;
}
}
uint8_t cfg = (val >> (8 * (i - i0))) & all_cfg_fields;
update_pmpcfg_entry(i, cfg, 0);
write_success = true;
}
}
proc->get_mmu()->flush_tlb();
return write_success;
}

ind_pmpcfg_csr_t::ind_pmpcfg_csr_t(processor_t* const proc, const reg_t addr):
pmpcfg_csr_t(proc, addr) {
}

reg_t ind_pmpcfg_csr_t::read() const noexcept {
// Bit MXLEN-1 of the pmpcfg register is the extended-attributes (E) field.
// The address member of this object holds the PMP entry index.
return state->pmpaddr[address]->cfg |
((proc->get_const_xlen() == 32)
? (state->pmpaddr[address]->E ? PMPCFG32_E : 0)
: (state->pmpaddr[address]->E ? PMPCFG64_E : 0));
}

bool ind_pmpcfg_csr_t::unlogged_write(const reg_t val) noexcept {
const bool E = (proc->get_const_xlen() == 32)
? ((val & PMPCFG32_E) ? 1 : 0)
: ((val & PMPCFG64_E) ? 1 : 0);
// The low 8 bits of the indirect pmpcfg register alias to the
// PMP configurations accessible via the direct CSRs
const uint8_t cfg = val & 0xFF;

// The address member of this object holds the PMP entry index.
update_pmpcfg_entry(address, cfg, E);

proc->get_mmu()->flush_tlb();
return true;
}

// implement class mseccfg_csr_t
mseccfg_csr_t::mseccfg_csr_t(processor_t* const proc, const reg_t addr):
basic_csr_t(proc, addr, 0) {
Expand Down Expand Up @@ -319,6 +381,9 @@ bool mseccfg_csr_t::get_useed() const noexcept {
bool mseccfg_csr_t::get_sseed() const noexcept {
return (read() & MSECCFG_SSEED);
}
bool mseccfg_csr_t::get_msse() const noexcept {
return (read() & MSECCFG_MSSE);
}

bool mseccfg_csr_t::unlogged_write(const reg_t val) noexcept {
reg_t new_val = read();
Expand Down Expand Up @@ -351,6 +416,11 @@ bool mseccfg_csr_t::unlogged_write(const reg_t val) noexcept {
new_val |= (val & MSECCFG_MLPE);
}

if (proc->extension_enabled(EXT_SMCFISS)) {
new_val &= ~MSECCFG_MSSE;
new_val |= (val & MSECCFG_MSSE);
}

if (proc->extension_enabled(EXT_SMMPM)) {
const reg_t pmm_reserved = 1; // Reserved value of mseccfg.PMM
reg_t pmm = get_field(val, MSECCFG_PMM);
Expand Down
16 changes: 14 additions & 2 deletions riscv/csrs.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ class pmpaddr_csr_t: public csr_t {
bool subset_match(reg_t addr, reg_t len) const noexcept;

// Is the specified access allowed given the pmpcfg privileges?
bool access_ok(access_type type, reg_t mode, bool hlvx) const noexcept;
bool access_ok(access_type type, reg_t mode, bool hlvx, bool ss_access) const noexcept;

// To check lock bit status from outside like mseccfg
bool is_locked() const noexcept {
Expand All @@ -127,8 +127,10 @@ class pmpaddr_csr_t: public csr_t {

bool next_locked_and_tor() const noexcept;
reg_t val;
friend class pmpcfg_csr_t; // so he can access cfg
friend class pmpcfg_csr_t; // so he can access cfg
friend class ind_pmpcfg_csr_t; // so he can access cfg
uint8_t cfg;
bool E; // Extended attribute (E)
const size_t pmpidx;
};

Expand All @@ -141,6 +143,7 @@ class pmpcfg_csr_t: public csr_t {
virtual reg_t read() const noexcept override;
protected:
virtual bool unlogged_write(const reg_t val) noexcept override;
virtual void update_pmpcfg_entry(const size_t index, const uint8_t cfg_in, const bool E);
};

class mseccfg_csr_t: public basic_csr_t {
Expand All @@ -152,6 +155,7 @@ class mseccfg_csr_t: public basic_csr_t {
bool get_rlb() const noexcept;
bool get_useed() const noexcept;
bool get_sseed() const noexcept;
bool get_msse() const noexcept;
protected:
virtual bool unlogged_write(const reg_t val) noexcept override;
};
Expand Down Expand Up @@ -877,6 +881,14 @@ class sscsrind_reg_csr_t : public csr_t {
csr_t_p get_reg() const noexcept;
};

class ind_pmpcfg_csr_t: public pmpcfg_csr_t {
public:
ind_pmpcfg_csr_t(processor_t* const proc, const reg_t addr);
reg_t read() const noexcept override;
protected:
virtual bool unlogged_write(const reg_t val) noexcept override;
};

// smcntrpmf_csr_t caches the previous state of the CSR in case a CSRW instruction
// modifies the state that should not be immediately visible to bump()
class smcntrpmf_csr_t : public masked_csr_t {
Expand Down
7 changes: 7 additions & 0 deletions riscv/encoding.h
Original file line number Diff line number Diff line change
Expand Up @@ -284,11 +284,15 @@
#define MISELECT_IMSIC _RISCV_UL(0x70)
#define MISELECT_IMSIC_TOP _RISCV_UL(0xff)


#define SISELECT_IPRIO _RISCV_UL(0x30)
#define SISELECT_IPRIO_TOP _RISCV_UL(0x3f)
#define SISELECT_IMSIC _RISCV_UL(0x70)
#define SISELECT_IMSIC_TOP _RISCV_UL(0xff)

#define MISELECT_PMPIND_START _RISCV_UL(0x300)
#define MISELECT_PMPIND_TOP _RISCV_UL(0x33F)

#define VSISELECT_IMSIC _RISCV_UL(0x70)
#define VSISELECT_IMSIC_TOP _RISCV_UL(0xff)

Expand Down Expand Up @@ -330,6 +334,7 @@
#define MSECCFG_USEED _RISCV_UL(0x00000100)
#define MSECCFG_SSEED _RISCV_UL(0x00000200)
#define MSECCFG_MLPE _RISCV_UL(0x00000400)
#define MSECCFG_MSSE _RISCV_UL(0x00000800)
#define MSECCFG_PMM _RISCV_ULL(0x0000000300000000)

/* jvt fields */
Expand Down Expand Up @@ -393,6 +398,8 @@

#define SPMP_U _RISCV_UL(0x100)
#define SPMP_SHARED _RISCV_UL(0x200)
#define PMPCFG64_E _RISCV_UL(0x8000000000000000)
#define PMPCFG32_E _RISCV_UL(0x80000000)

#define MCTRCTL_U _RISCV_ULL(0x0000000000000001)
#define MCTRCTL_S _RISCV_ULL(0x0000000000000002)
Expand Down
8 changes: 7 additions & 1 deletion riscv/insns/ssamoswap_d.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,11 @@ require_extension(EXT_ZAAMO);
require_rv64;

DECLARE_XENVCFG_VARS(SSE);
require_envcfg(SSE);

if (p->extension_enabled('S')) {
require_envcfg(SSE);
} else {
require_extension(EXT_SMUCFISS);
require(mSSE);
}
WRITE_RD(MMU.ssamoswap<uint64_t>(RS1, RS2));
6 changes: 6 additions & 0 deletions riscv/insns/ssamoswap_w.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,10 @@ require_extension(EXT_ZAAMO);

DECLARE_XENVCFG_VARS(SSE);
require_envcfg(SSE);
if (p->extension_enabled('S')) {
require_envcfg(SSE);
} else {
require_extension(EXT_SMUCFISS);
require(mSSE);
}
WRITE_RD(sext32(MMU.ssamoswap<uint32_t>(RS1, RS2)));
3 changes: 3 additions & 0 deletions riscv/isa_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,13 @@ typedef enum {
EXT_ZVFHMIN,
EXT_ZVFOFP4MIN,
EXT_ZVFOFP8MIN,
EXT_SMCFISS,
EXT_SMEPMP,
EXT_SMSTATEEN,
EXT_SMPMPIND,
EXT_SMPMPMT,
EXT_SMRNMI,
EXT_SMUCFISS,
EXT_SSCOFPMF,
EXT_SVADU,
EXT_SVADE,
Expand Down
Loading