diff options
52 files changed, 687 insertions, 123 deletions
diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index ecb0a6c..88facc8 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -18,7 +18,7 @@ on: jobs: test: name: Test Spike build (Ubuntu) - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v2 with: diff --git a/.github/workflows/debug-smoke.yml b/.github/workflows/debug-smoke.yml index 1ae54ab..090e4d9 100644 --- a/.github/workflows/debug-smoke.yml +++ b/.github/workflows/debug-smoke.yml @@ -13,7 +13,7 @@ on: jobs: test: name: Test debug (Ubuntu) - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v2 diff --git a/Makefile.in b/Makefile.in index f236576..f90159e 100644 --- a/Makefile.in +++ b/Makefile.in @@ -69,7 +69,7 @@ install_exes_dir := $(INSTALLDIR)/bin sprojs := @subprojects@ sprojs_enabled := @subprojects_enabled@ -sprojs_include := -I. -I$(src_dir) $(addprefix -I$(src_dir)/, $(sprojs_enabled)) +sprojs_include := -iquote . -I$(src_dir) $(addprefix -iquote $(src_dir)/, $(sprojs_enabled)) VPATH := $(addprefix $(src_dir)/, $(sprojs_enabled)) #------------------------------------------------------------------------- @@ -94,7 +94,7 @@ VPATH := $(addprefix $(src_dir)/, $(sprojs_enabled)) # highest. default-CFLAGS := -DPREFIX=\"$(prefix)\" -Wall -Wno-unused -Wno-nonportable-include-path -g -O2 -fPIC -default-CXXFLAGS := $(default-CFLAGS) -std=c++17 +default-CXXFLAGS := $(default-CFLAGS) -std=c++2a mcppbs-CPPFLAGS := @CPPFLAGS@ mcppbs-CFLAGS := $(default-CFLAGS) @CFLAGS@ @@ -39,7 +39,6 @@ Spike supports the following RISC-V ISA features: - Svinval extension, v1.0 - Sdext extension, v1.0-STABLE - Sdtrig extension, v1.0-STABLE - - 4 triggers support type={2, 3, 4, 5, 6, 15} (mcontrol, icount, itrigger, etrigger, mcontrol6, disabled) - Smepmp extension v1.0 - Smstateen extension, v1.0 - Sscofpmf v0.5.2 @@ -75,7 +74,7 @@ Build Steps We assume that the RISCV environment variable is set to the RISC-V tools install path. - $ apt-get install device-tree-compiler + $ apt-get install device-tree-compiler libboost-regex-dev $ mkdir build $ cd build $ ../configure --prefix=$RISCV diff --git a/ci-tests/create-ci-binary-tarball b/ci-tests/create-ci-binary-tarball index b4fa545..abc9ee0 100755 --- a/ci-tests/create-ci-binary-tarball +++ b/ci-tests/create-ci-binary-tarball @@ -12,9 +12,13 @@ mkdir -p build/hello && cd "$_" riscv64-unknown-elf-gcc -O2 -o hello `git rev-parse --show-toplevel`/ci-tests/hello.c cd - +mkdir -p build/dummy-slliuw && cd "$_" +riscv64-unknown-elf-gcc -O2 -o dummy-slliuw `git rev-parse --show-toplevel`/ci-tests/dummy-slliuw.c +cd - + mv build/pk/pk . mv build/hello/hello . +mv build/dummy-slliuw/dummy-slliuw . +tar -cf spike-ci.tar pk hello dummy-slliuw -tar -cf spike-ci.tar pk hello - -rm pk hello +rm pk hello dummy-slliuw diff --git a/ci-tests/dummy-slliuw.c b/ci-tests/dummy-slliuw.c new file mode 100644 index 0000000..05b7a40 --- /dev/null +++ b/ci-tests/dummy-slliuw.c @@ -0,0 +1,8 @@ +#include <stdio.h> + +int main() { + // as if slli.uw zero, t1, 3 + asm(".4byte 0x0833101b"); + printf("Executed successfully\n"); + return 0; +} diff --git a/ci-tests/test-customext.cc b/ci-tests/test-customext.cc new file mode 100644 index 0000000..5ef6c4f --- /dev/null +++ b/ci-tests/test-customext.cc @@ -0,0 +1,90 @@ +#include <riscv/extension.h> +#include <riscv/sim.h> + +struct : public arg_t { + std::string to_string(insn_t insn) const { return xpr_name[insn.rd()]; } +} xrd; + +struct : public arg_t { + std::string to_string(insn_t insn) const { return xpr_name[insn.rs1()]; } +} xrs1; + +struct : public arg_t { + std::string to_string(insn_t insn) const { return xpr_name[insn.rs2()]; } +} xrs2; + +struct : public arg_t { + std::string to_string(insn_t insn) const { + return std::to_string((int)insn.shamt()); + } +} shamt; + +static reg_t do_nop4([[maybe_unused]] processor_t *p, + [[maybe_unused]] insn_t insn, reg_t pc) { + return pc + 4; +} + +// dummy extension that uses the same prefix as standard zba extension +struct xslliuw_dummy_t : public extension_t { + const char *name() { return "dummyslliuw"; } + + xslliuw_dummy_t() {} + + std::vector<insn_desc_t> get_instructions() { + std::vector<insn_desc_t> insns; + insns.push_back(insn_desc_t{MATCH_SLLI_UW, MASK_SLLI_UW, do_nop4, do_nop4, + do_nop4, do_nop4, do_nop4, do_nop4, do_nop4, + do_nop4}); + return insns; + } + + std::vector<disasm_insn_t *> get_disasms() { + std::vector<disasm_insn_t *> insns; + insns.push_back(new disasm_insn_t("dummy_slliuw", MATCH_SLLI_UW, + MASK_SLLI_UW, {&xrd, &xrs1, &shamt})); + return insns; + } +}; + +REGISTER_EXTENSION(dummyslliuw, []() { return new xslliuw_dummy_t; }) + +// Copied from spike main. +// TODO: This should really be provided in libriscv +static std::vector<std::pair<reg_t, abstract_mem_t *>> +make_mems(const std::vector<mem_cfg_t> &layout) { + std::vector<std::pair<reg_t, abstract_mem_t *>> mems; + mems.reserve(layout.size()); + for (const auto &cfg : layout) { + mems.push_back(std::make_pair(cfg.get_base(), new mem_t(cfg.get_size()))); + } + return mems; +} + +int main(int argc, char **argv) { + cfg_t cfg; + cfg.isa = "RV64IMAFDCV_xdummyslliuw"; + std::vector<device_factory_t *> plugin_devices; + if (argc != 3) { + std::cerr << "Error: invalid arguments\n"; + exit(1); + } + std::vector<std::string> htif_args{argv[1] /* pk */, argv[2] /* executable */}; + debug_module_config_t dm_config = {.progbufsize = 2, + .max_sba_data_width = 0, + .require_authentication = false, + .abstract_rti = 0, + .support_hasel = true, + .support_abstract_csr_access = true, + .support_abstract_fpr_access = true, + .support_haltgroups = true, + .support_impebreak = true}; + std::vector<std::pair<reg_t, abstract_mem_t *>> mems = + make_mems(cfg.mem_layout); + sim_t sim(&cfg, false, mems, plugin_devices, htif_args, dm_config, + nullptr, // log_path + true, // dtb_enabled + nullptr, // dtb_file + false, // socket_enabled + nullptr); // cmd_file + sim.run(); +} diff --git a/ci-tests/test-spike b/ci-tests/test-spike index 725ac64..0540739 100755 --- a/ci-tests/test-spike +++ b/ci-tests/test-spike @@ -14,4 +14,7 @@ time ../install/bin/spike --isa=rv64gc pk hello | grep "Hello, world! Pi is app # check that including sim.h in an external project works g++ -std=c++17 -I../install/include -L../install/lib $DIR/testlib.c -lriscv -o test-libriscv -LD_LIBRARY_PATH=../install/lib ./test-libriscv | grep "Hello, world! Pi is approximately 3.141588." +g++ -std=c++17 -I../install/include -L../install/lib $DIR/test-customext.cc -lriscv -o test-customext + +LD_LIBRARY_PATH=../install/lib ./test-libriscv pk hello| grep "Hello, world! Pi is approximately 3.141588." +LD_LIBRARY_PATH=../install/lib ./test-customext pk dummy-slliuw | grep "Executed successfully" diff --git a/ci-tests/testlib.c b/ci-tests/testlib.c index 5e39c1b..9cdbe07 100644 --- a/ci-tests/testlib.c +++ b/ci-tests/testlib.c @@ -12,11 +12,16 @@ static std::vector<std::pair<reg_t, abstract_mem_t*>> make_mems(const std::vecto return mems; } -int main() -{ +int main(int argc, char **argv) { cfg_t cfg; std::vector<device_factory_t*> plugin_devices; - std::vector<std::string> htif_args {"pk", "hello"}; + + if (argc != 3) { + std::cerr << "Error: invalid arguments\n"; + exit(1); + } + std::vector<std::string> htif_args{argv[1] /* pk */, + argv[2] /* executable */}; debug_module_config_t dm_config; std::vector<std::pair<reg_t, abstract_mem_t*>> mems = make_mems(cfg.mem_layout); diff --git a/disasm/disasm.cc b/disasm/disasm.cc index 65c15f5..187a1d5 100644 --- a/disasm/disasm.cc +++ b/disasm/disasm.cc @@ -267,6 +267,18 @@ struct : public arg_t { } rvc_sp; struct : public arg_t { + std::string to_string(insn_t UNUSED insn) const { + return xpr_name[X_RA]; + } +} rvc_ra; + +struct : public arg_t { + std::string to_string(insn_t UNUSED insn) const { + return xpr_name[X_T0]; + } +} rvc_t0; + +struct : public arg_t { std::string to_string(insn_t insn) const { return std::to_string((int)insn.rvc_imm()); } @@ -868,6 +880,12 @@ void disassembler_t::add_instructions(const isa_parser_t* isa) DEFINE_XAMO(amocas_h) } + if (isa->extension_enabled(EXT_ZICFILP)) { + // lpad encodes as `auipc x0, label`, so it needs to be added before auipc + // for higher disassembling priority + DISASM_INSN("lpad", lpad, 0, {&bigimm}); + } + add_insn(new disasm_insn_t("j", match_jal, mask_jal | mask_rd, {&jump_target})); add_insn(new disasm_insn_t("jal", match_jal | match_rd_ra, mask_jal | mask_rd, {&jump_target})); add_insn(new disasm_insn_t("jal", match_jal, mask_jal, {&xrd, &jump_target})); @@ -2178,6 +2196,13 @@ void disassembler_t::add_instructions(const isa_parser_t* isa) } if (isa->extension_enabled(EXT_ZIMOP)) { + #define DISASM_MOP_R(name, rs1, rd) \ + add_insn(new disasm_insn_t(#name, match_##name | (rs1 << 15) | (rd << 7), \ + 0xFFFFFFFF, {&xrd, &xrs1})); + + #define DISASM_MOP_RR(name, rs1, rd, rs2) \ + add_insn(new disasm_insn_t(#name, match_##name | (rs1 << 15) | (rd << 7) | (rs2 << 20), \ + 0xFFFFFFFF, {&xrd, &xrs1, &xrs2})); DEFINE_R1TYPE(mop_r_0); DEFINE_R1TYPE(mop_r_1); DEFINE_R1TYPE(mop_r_2); @@ -2206,7 +2231,15 @@ void disassembler_t::add_instructions(const isa_parser_t* isa) DEFINE_R1TYPE(mop_r_25); DEFINE_R1TYPE(mop_r_26); DEFINE_R1TYPE(mop_r_27); - DEFINE_R1TYPE(mop_r_28); + if (!isa->extension_enabled(EXT_ZICFISS)) { + DEFINE_R1TYPE(mop_r_28); + } else { + // Add code points of mop_r_28 not used by Zicfiss + for (unsigned rd_val = 0; rd_val <= 31; ++rd_val) + for (unsigned rs1_val = 0; rs1_val <= 31; ++rs1_val) + if ((rd_val != 0 && rs1_val !=0) || (rd_val == 0 && !(rs1_val == 1 || rs1_val == 5))) + DISASM_MOP_R(mop_r_28, rs1_val, rd_val); + } DEFINE_R1TYPE(mop_r_29); DEFINE_R1TYPE(mop_r_30); DEFINE_R1TYPE(mop_r_31); @@ -2218,12 +2251,24 @@ void disassembler_t::add_instructions(const isa_parser_t* isa) DEFINE_RTYPE(mop_rr_5); DEFINE_RTYPE(mop_rr_6); DEFINE_RTYPE(mop_rr_7); + if (!isa->extension_enabled(EXT_ZICFISS)) { + DEFINE_RTYPE(mop_rr_7); + } else { + // Add code points of mop_rr_7 not used by Zicfiss + for (unsigned rd_val = 0; rd_val <= 31; ++rd_val) + for (unsigned rs1_val = 0; rs1_val <= 31; ++rs1_val) + for (unsigned rs2_val = 0; rs2_val <= 31; ++rs2_val) + if ((rs2_val != 1 && rs2_val != 5) || rd_val != 0 || rs1_val != 0) + DISASM_MOP_RR(mop_rr_7, rs1_val, rd_val, rs2_val); + } } if (isa->extension_enabled(EXT_ZCMOP)) { - DISASM_INSN("c.mop.1", c_mop_1, 0, {}); + if (!isa->extension_enabled(EXT_ZICFISS)) + DISASM_INSN("c.mop.1", c_mop_1, 0, {}); DISASM_INSN("c.mop.3", c_mop_3, 0, {}); - DISASM_INSN("c.mop.5", c_mop_5, 0, {}); + if (!isa->extension_enabled(EXT_ZICFISS)) + DISASM_INSN("c.mop.5", c_mop_5, 0, {}); DISASM_INSN("c.mop.7", c_mop_7, 0, {}); DISASM_INSN("c.mop.9", c_mop_9, 0, {}); DISASM_INSN("c.mop.11", c_mop_11, 0, {}); @@ -2386,6 +2431,23 @@ void disassembler_t::add_instructions(const isa_parser_t* isa) DEFINE_XSTORE_BASE(sw_rl); DEFINE_XSTORE_BASE(sd_rl); } + + if(isa->extension_enabled(EXT_ZICFISS)) { + DISASM_INSN("sspush", sspush_x1, 0, {&xrs2}); + DISASM_INSN("sspush", sspush_x5, 0, {&xrs2}); + DISASM_INSN("sspopchk", sspopchk_x1, 0, {&xrs1}); + DISASM_INSN("sspopchk", sspopchk_x5, 0, {&xrs1}); + DISASM_INSN("ssrdp", ssrdp, 0, {&xrd}); + DEFINE_XAMO(ssamoswap_w); + + if(isa->get_max_xlen() == 64) + DEFINE_XAMO(ssamoswap_d) + + if (isa->extension_enabled(EXT_ZCA)) { + DISASM_INSN("c.sspush", c_sspush_x1, 0, {&rvc_ra}); + DISASM_INSN("c.sspopchk", c_sspopchk_x5, 0, {&rvc_t0}); + } + } } disassembler_t::disassembler_t(const isa_parser_t *isa) diff --git a/disasm/isa_parser.cc b/disasm/isa_parser.cc index 7a133e4..62466d0 100644 --- a/disasm/isa_parser.cc +++ b/disasm/isa_parser.cc @@ -29,7 +29,7 @@ static void bad_priv_string(const char* priv) isa_parser_t::isa_parser_t(const char* str, const char *priv) { isa_string = strtolower(str); - const char* all_subsets = "mafdqcpvh"; + const char* all_subsets = "mafdqcpvhb"; if (isa_string.compare(0, 4, "rv32") == 0) max_xlen = 32; @@ -306,6 +306,10 @@ isa_parser_t::isa_parser_t(const char* str, const char *priv) extension_table[EXT_ZALASR] = true; } else if (ext_str == "ssqosid") { extension_table[EXT_SSQOSID] = true; + } else if (ext_str == "zicfilp") { + extension_table[EXT_ZICFILP] = true; + } else if (ext_str == "zicfiss") { + extension_table[EXT_ZICFISS] = true; } else if (ext_str[0] == 'x') { extension_table['X'] = true; if (ext_str.size() == 1) { @@ -337,12 +341,16 @@ isa_parser_t::isa_parser_t(const char* str, const char *priv) if (extension_table['A']) { extension_table[EXT_ZAAMO] = true; extension_table[EXT_ZALRSC] = true; + } else if (extension_table[EXT_ZAAMO] && extension_table[EXT_ZALRSC]) { + extension_table['A'] = true; } if (extension_table['B']) { extension_table[EXT_ZBA] = true; extension_table[EXT_ZBB] = true; extension_table[EXT_ZBS] = true; + } else if (extension_table[EXT_ZBA] && extension_table[EXT_ZBB] && extension_table[EXT_ZBS]) { + extension_table['B'] = true; } if (extension_table['C']) { @@ -391,6 +399,16 @@ isa_parser_t::isa_parser_t(const char* str, const char *priv) (extension_table[EXT_ZVKG] || extension_table[EXT_ZVKNED] || extension_table[EXT_ZVKSH])) { bad_isa_string(str, "'Zvkg', 'Zvkned', and 'Zvksh' extensions are incompatible with 'Zpn' extension in rv64"); } + + // When SSE is 0, Zicfiss behavior is defined by Zicmop + if (extension_table[EXT_ZICFISS] && !extension_table[EXT_ZIMOP]) { + bad_isa_string(str, "'Zicfiss' extension requires 'Zimop' extension"); + } + + if (extension_table[EXT_ZICFISS] && extension_table[EXT_ZCA] && + !extension_table[EXT_ZCMOP]) { + bad_isa_string(str, "'Zicfiss' extension requires 'Zcmop' extension when `Zca` is supported"); + } #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] || diff --git a/fdt/fdt.mk.in b/fdt/fdt.mk.in index 99e7639..64d06ac 100644 --- a/fdt/fdt.mk.in +++ b/fdt/fdt.mk.in @@ -15,3 +15,5 @@ fdt_c_srcs = \ fdt_empty_tree.c \ fdt_addresses.c \ fdt_overlay.c \ + +fdt_CFLAGS = -I$(src_dir)/fdt diff --git a/fesvr/syscall.cc b/fesvr/syscall.cc index e277be1..162cbfc 100644 --- a/fesvr/syscall.cc +++ b/fesvr/syscall.cc @@ -161,6 +161,7 @@ syscall_t::syscall_t(htif_t* htif) table[64] = &syscall_t::sys_write; table[67] = &syscall_t::sys_pread; table[68] = &syscall_t::sys_pwrite; + table[78] = &syscall_t::sys_readlinkat; table[79] = &syscall_t::sys_fstatat; table[80] = &syscall_t::sys_fstat; table[93] = &syscall_t::sys_exit; @@ -512,3 +513,16 @@ void syscall_t::set_chroot(const char* where) chroot = buf2; } + +reg_t syscall_t::sys_readlinkat(reg_t dirfd, reg_t ppathname, reg_t ppathname_size, + reg_t pbuf, reg_t bufsiz, reg_t a5, reg_t a6) +{ + std::vector<char> pathname(ppathname_size); + memif->read(ppathname, ppathname_size, pathname.data()); + + std::vector<char> buf(bufsiz); + ssize_t ret = sysret_errno(AT_SYSCALL(readlinkat, dirfd, pathname.data(), buf.data(), bufsiz)); + if (ret > 0) + memif->write(pbuf, ret, buf.data()); + return ret; +} diff --git a/fesvr/syscall.h b/fesvr/syscall.h index c002e6c..f711170 100644 --- a/fesvr/syscall.h +++ b/fesvr/syscall.h @@ -70,6 +70,7 @@ class syscall_t : public device_t reg_t sys_getcwd(reg_t, reg_t, reg_t, reg_t, reg_t, reg_t, reg_t); reg_t sys_getmainvars(reg_t, reg_t, reg_t, reg_t, reg_t, reg_t, reg_t); reg_t sys_chdir(reg_t, reg_t, reg_t, reg_t, reg_t, reg_t, reg_t); + reg_t sys_readlinkat(reg_t, reg_t, reg_t, reg_t, reg_t, reg_t, reg_t); }; #endif diff --git a/riscv/csrs.cc b/riscv/csrs.cc index b76b496..31e05f2 100644 --- a/riscv/csrs.cc +++ b/riscv/csrs.cc @@ -180,7 +180,7 @@ 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) const noexcept { +bool pmpaddr_csr_t::access_ok(access_type type, reg_t mode, bool hlvx) const noexcept { const bool cfgx = cfg & PMP_X; const bool cfgw = cfg & PMP_W; const bool cfgr = cfg & PMP_R; @@ -191,7 +191,7 @@ bool pmpaddr_csr_t::access_ok(access_type type, reg_t mode) const noexcept { const bool typer = type == LOAD; const bool typex = type == FETCH; const bool typew = type == STORE; - const bool normal_rwx = (typer && cfgr) || (typew && cfgw) || (typex && cfgx); + const bool normal_rwx = (typer && cfgr && (!hlvx || cfgx)) || (typew && cfgw) || (typex && cfgx); const bool mseccfg_mml = state->mseccfg->get_mml(); if (mseccfg_mml) { @@ -285,7 +285,8 @@ mseccfg_csr_t::mseccfg_csr_t(processor_t* const proc, const reg_t addr): void mseccfg_csr_t::verify_permissions(insn_t insn, bool write) const { basic_csr_t::verify_permissions(insn, write); - if (!proc->extension_enabled(EXT_SMEPMP)) + if (!proc->extension_enabled(EXT_SMEPMP) && + !proc->extension_enabled(EXT_ZICFILP)) throw trap_illegal_instruction(insn.bits()); } @@ -322,6 +323,11 @@ bool mseccfg_csr_t::unlogged_write(const reg_t val) noexcept { proc->get_mmu()->flush_tlb(); + if (proc->extension_enabled(EXT_ZICFILP)) { + new_val &= ~MSECCFG_MLPE; + new_val |= (val & MSECCFG_MLPE); + } + return basic_csr_t::unlogged_write(new_val); } @@ -414,6 +420,7 @@ reg_t base_status_csr_t::compute_sstatus_write_mask() const noexcept { | (has_fs ? SSTATUS_FS : 0) | (proc->any_custom_extensions() ? SSTATUS_XS : 0) | (has_vs ? SSTATUS_VS : 0) + | (proc->extension_enabled(EXT_ZICFILP) ? SSTATUS_SPELP : 0) ; } @@ -497,7 +504,9 @@ bool mstatus_csr_t::unlogged_write(const reg_t val) noexcept { | (proc->extension_enabled('S') ? MSTATUS_TSR : 0) | (has_page ? MSTATUS_TVM : 0) | (has_gva ? MSTATUS_GVA : 0) - | (has_mpv ? MSTATUS_MPV : 0); + | (has_mpv ? MSTATUS_MPV : 0) + | (proc->extension_enabled(EXT_ZICFILP) ? (MSTATUS_SPELP | MSTATUS_MPELP) : 0) + ; const reg_t requested_mpp = proc->legalize_privilege(get_field(val, MSTATUS_MPP)); const reg_t adjusted_val = set_field(val, MSTATUS_MPP, requested_mpp); @@ -728,6 +737,10 @@ mip_csr_t::mip_csr_t(processor_t* const proc, const reg_t addr): mip_or_mie_csr_t(proc, addr) { } +reg_t mip_csr_t::read() const noexcept { + return val | state->hvip->basic_csr_t::read(); +} + void mip_csr_t::backdoor_write_with_mask(const reg_t mask, const reg_t val) noexcept { this->val = (this->val & ~mask) | (val & mask); } @@ -893,6 +906,7 @@ bool medeleg_csr_t::unlogged_write(const reg_t val) noexcept { | (1 << CAUSE_LOAD_PAGE_FAULT) | (1 << CAUSE_STORE_PAGE_FAULT) | (proc->extension_enabled('H') ? hypervisor_exceptions : 0) + | (1 << CAUSE_SOFTWARE_CHECK_FAULT) ; return basic_csr_t::unlogged_write((read() & ~mask) | (val & mask)); } @@ -1075,7 +1089,8 @@ void time_counter_csr_t::sync(const reg_t val) noexcept { if (proc->extension_enabled(EXT_SSTC)) { const reg_t mip_val = (shadow_val >= state->stimecmp->read() ? MIP_STIP : 0) | (shadow_val + state->htimedelta->read() >= state->vstimecmp->read() ? MIP_VSTIP : 0); - state->mip->backdoor_write_with_mask(MIP_STIP | MIP_VSTIP, mip_val); + const reg_t mask = ((state->menvcfg->read() & MENVCFG_STCE) ? MIP_STIP : 0) | ((state->henvcfg->read() & HENVCFG_STCE) ? MIP_VSTIP : 0); + state->mip->backdoor_write_with_mask(mask, mip_val); } } @@ -1279,7 +1294,8 @@ dcsr_csr_t::dcsr_csr_t(processor_t* const proc, const reg_t addr): ebreakvu(false), halt(false), v(false), - cause(0) { + cause(0), + pelp(elp_t::NO_LP_EXPECTED) { } void dcsr_csr_t::verify_permissions(insn_t insn, bool write) const { @@ -1302,6 +1318,7 @@ reg_t dcsr_csr_t::read() const noexcept { result = set_field(result, DCSR_STEP, step); result = set_field(result, DCSR_PRV, prv); result = set_field(result, CSR_DCSR_V, v); + result = set_field(result, DCSR_PELP, pelp); return result; } @@ -1316,13 +1333,17 @@ bool dcsr_csr_t::unlogged_write(const reg_t val) noexcept { ebreakvu = proc->extension_enabled('H') ? get_field(val, CSR_DCSR_EBREAKVU) : false; halt = get_field(val, DCSR_HALT); v = proc->extension_enabled('H') ? get_field(val, CSR_DCSR_V) : false; + pelp = proc->extension_enabled(EXT_ZICFILP) ? + static_cast<elp_t>(get_field(val, DCSR_PELP)) : elp_t::NO_LP_EXPECTED; return true; } -void dcsr_csr_t::write_cause_and_prv(uint8_t cause, reg_t prv, bool v) noexcept { +void dcsr_csr_t::update_fields(const uint8_t cause, const reg_t prv, + const bool v, const elp_t pelp) noexcept { this->cause = cause; this->prv = prv; this->v = v; + this->pelp = pelp; log_write(); } @@ -1528,12 +1549,18 @@ void henvcfg_csr_t::verify_permissions(insn_t insn, bool write) const { masked_csr_t::verify_permissions(insn, write); } +bool henvcfg_csr_t::unlogged_write(const reg_t val) noexcept { + const reg_t mask = menvcfg->read() | ~(MENVCFG_PBMTE | MENVCFG_STCE | MENVCFG_ADUE); + return envcfg_csr_t::unlogged_write((masked_csr_t::read() & ~mask) | (val & mask)); +} + stimecmp_csr_t::stimecmp_csr_t(processor_t* const proc, const reg_t addr, const reg_t imask): basic_csr_t(proc, addr, 0), intr_mask(imask) { } bool stimecmp_csr_t::unlogged_write(const reg_t val) noexcept { - state->mip->backdoor_write_with_mask(intr_mask, state->time->read() >= val ? intr_mask : 0); + const reg_t mask = ((state->menvcfg->read() & MENVCFG_STCE) ? MIP_STIP : 0) | ((state->henvcfg->read() & HENVCFG_STCE) ? MIP_VSTIP : 0); + state->mip->backdoor_write_with_mask(mask, state->time->read() >= val ? intr_mask : 0); return basic_csr_t::unlogged_write(val); } @@ -1717,3 +1744,26 @@ void srmcfg_csr_t::verify_permissions(insn_t insn, bool write) const { if (state->v) throw trap_virtual_instruction(insn.bits()); } + +hvip_csr_t::hvip_csr_t(processor_t* const proc, const reg_t addr, const reg_t init): + basic_csr_t(proc, addr, init) { +} + +reg_t hvip_csr_t::read() const noexcept { + return basic_csr_t::read() | (state->mip->read() & MIP_VSSIP); // hvip.VSSIP is an alias of mip.VSSIP +} + +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 ad3f7a3..c924a52 100644 --- a/riscv/csrs.h +++ b/riscv/csrs.h @@ -21,6 +21,11 @@ class processor_t; struct state_t; +enum struct elp_t { + NO_LP_EXPECTED = 0, + LP_EXPECTED = 1, +}; + // Parent, abstract class for all CSRs class csr_t { public: @@ -97,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) const noexcept; + bool access_ok(access_type type, reg_t mode, bool hlvx) const noexcept; // To check lock bit status from outside like mseccfg bool is_locked() const noexcept { @@ -349,7 +354,7 @@ typedef std::shared_ptr<misa_csr_t> misa_csr_t_p; class mip_or_mie_csr_t: public csr_t { public: mip_or_mie_csr_t(processor_t* const proc, const reg_t addr); - virtual reg_t read() const noexcept override final; + virtual reg_t read() const noexcept override; void write_with_mask(const reg_t mask, const reg_t val) noexcept; @@ -364,6 +369,7 @@ class mip_or_mie_csr_t: public csr_t { class mip_csr_t: public mip_or_mie_csr_t { public: mip_csr_t(processor_t* const proc, const reg_t addr); + virtual reg_t read() const noexcept override final; // Does not log. Used by external things (clint) that wiggle bits in mip. void backdoor_write_with_mask(const reg_t mask, const reg_t val) noexcept; @@ -484,6 +490,9 @@ class henvcfg_csr_t final: public envcfg_csr_t { virtual void verify_permissions(insn_t insn, bool write) const override; + protected: + virtual bool unlogged_write(const reg_t val) noexcept override; + private: csr_t_p menvcfg; }; @@ -675,7 +684,8 @@ class dcsr_csr_t: public csr_t { dcsr_csr_t(processor_t* const proc, const reg_t addr); virtual void verify_permissions(insn_t insn, bool write) const override; virtual reg_t read() const noexcept override; - void write_cause_and_prv(uint8_t cause, reg_t prv, bool v) noexcept; + void update_fields(const uint8_t cause, const reg_t prv, + const bool v, const elp_t pelp) noexcept; protected: virtual bool unlogged_write(const reg_t val) noexcept override; public: @@ -689,6 +699,7 @@ class dcsr_csr_t: public csr_t { bool halt; bool v; uint8_t cause; + elp_t pelp; }; typedef std::shared_ptr<dcsr_csr_t> dcsr_csr_t_p; @@ -850,4 +861,21 @@ class srmcfg_csr_t: public masked_csr_t { srmcfg_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; }; + +class hvip_csr_t : public basic_csr_t { + public: + hvip_csr_t(processor_t* const proc, const reg_t addr, const reg_t init); + reg_t read() const noexcept override; + protected: + virtual bool unlogged_write(const reg_t val) noexcept override; +}; + +typedef std::shared_ptr<hvip_csr_t> 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/decode_macros.h b/riscv/decode_macros.h index e31da5c..4050000 100644 --- a/riscv/decode_macros.h +++ b/riscv/decode_macros.h @@ -321,3 +321,23 @@ inline long double to_f(float128_t f) { long double r; memcpy(&r, &f, sizeof(r)) reg_t h##field = get_field(STATE.henvcfg->read(), HENVCFG_##field) #endif + +#define software_check(x, tval) (unlikely(!(x)) ? throw trap_software_check(tval) : (void) 0) +#define ZICFILP_xLPE(v, prv) \ + ({ \ + reg_t lpe = 0ULL; \ + if (p->extension_enabled(EXT_ZICFILP)) { \ + DECLARE_XENVCFG_VARS(LPE); \ + const reg_t msecLPE = get_field(STATE.mseccfg->read(), MSECCFG_MLPE); \ + switch (prv) { \ + case PRV_U: lpe = p->extension_enabled('S') ? sLPE : mLPE; break; \ + case PRV_S: lpe = (v) ? hLPE : mLPE; break; \ + case PRV_M: lpe = msecLPE; break; \ + default: abort(); \ + } \ + } \ + lpe; \ + }) +#define ZICFILP_IS_LP_EXPECTED(reg_num) \ + (((reg_num) != 1 && (reg_num) != 5 && (reg_num) != 7) ? \ + elp_t::LP_EXPECTED : elp_t::NO_LP_EXPECTED) diff --git a/riscv/encoding.h b/riscv/encoding.h index 249b3fa..d600fa2 100644 --- a/riscv/encoding.h +++ b/riscv/encoding.h @@ -4,7 +4,7 @@ /* * This file is auto-generated by running 'make' in - * https://github.com/riscv/riscv-opcodes (a014979) + * https://github.com/riscv/riscv-opcodes (4ad822d) */ #ifndef RISCV_CSR_ENCODING_H @@ -346,6 +346,10 @@ #define SRMCFG_RCID 0x00000FFF #define SRMCFG_MCID 0x0FFF0000 +/* software check exception xtval codes */ +#define LANDING_PAD_FAULT 2 +#define SHADOW_STACK_FAULT 3 + #ifdef __riscv #if __riscv_xlen == 64 @@ -1464,6 +1468,8 @@ #define MASK_LH_AQ 0xfdf0707f #define MATCH_LHU 0x5003 #define MASK_LHU 0x707f +#define MATCH_LPAD 0x17 +#define MASK_LPAD 0xfff #define MATCH_LQ 0x300f #define MASK_LQ 0x707f #define MATCH_LR_D 0x1000302f @@ -4245,6 +4251,7 @@ DECLARE_INSN(ldu, MATCH_LDU, MASK_LDU) DECLARE_INSN(lh, MATCH_LH, MASK_LH) DECLARE_INSN(lh_aq, MATCH_LH_AQ, MASK_LH_AQ) DECLARE_INSN(lhu, MATCH_LHU, MASK_LHU) +DECLARE_INSN(lpad, MATCH_LPAD, MASK_LPAD) DECLARE_INSN(lq, MATCH_LQ, MASK_LQ) DECLARE_INSN(lr_d, MATCH_LR_D, MASK_LR_D) DECLARE_INSN(lr_w, MATCH_LR_W, MASK_LR_W) diff --git a/riscv/execute.cc b/riscv/execute.cc index 4f5860b..b2532c9 100644 --- a/riscv/execute.cc +++ b/riscv/execute.cc @@ -280,6 +280,7 @@ void processor_t::step(size_t n) in_wfi = false; insn_fetch_t fetch = mmu->load_insn(pc); + execute_insn_prehook(fetch.insn); if (debug && !state.serialized) disasm(fetch.insn); pc = execute_insn_logged(this, pc, fetch); @@ -291,6 +292,7 @@ void processor_t::step(size_t n) // Main simulation loop, fast path. for (auto ic_entry = _mmu->access_icache(pc); ; ) { auto fetch = ic_entry->data; + execute_insn_prehook(fetch.insn); pc = execute_insn_fast(this, pc, fetch); ic_entry = ic_entry->next; if (unlikely(ic_entry->tag != pc)) diff --git a/riscv/insns/c_jalr.h b/riscv/insns/c_jalr.h index 0a00f1c..12bc7f9 100644 --- a/riscv/insns/c_jalr.h +++ b/riscv/insns/c_jalr.h @@ -3,3 +3,7 @@ require(insn.rvc_rs1() != 0); reg_t tmp = npc; set_pc(RVC_RS1 & ~reg_t(1)); WRITE_REG(X_RA, tmp); + +if (ZICFILP_xLPE(STATE.v, STATE.prv)) { + STATE.elp = ZICFILP_IS_LP_EXPECTED(insn.rvc_rs1()); +} diff --git a/riscv/insns/c_jr.h b/riscv/insns/c_jr.h index 020cafd..c5162e9 100644 --- a/riscv/insns/c_jr.h +++ b/riscv/insns/c_jr.h @@ -1,3 +1,7 @@ require_extension(EXT_ZCA); require(insn.rvc_rs1() != 0); set_pc(RVC_RS1 & ~reg_t(1)); + +if (ZICFILP_xLPE(STATE.v, STATE.prv)) { + STATE.elp = ZICFILP_IS_LP_EXPECTED(insn.rvc_rs1()); +} diff --git a/riscv/insns/c_lui.h b/riscv/insns/c_lui.h index 3e0e02f..98a8898 100644 --- a/riscv/insns/c_lui.h +++ b/riscv/insns/c_lui.h @@ -4,8 +4,14 @@ if (insn.rvc_rd() == 2) { // c.addi16sp WRITE_REG(X_SP, sext_xlen(RVC_SP + insn.rvc_addi16sp_imm())); } else if (insn.rvc_imm() != 0) { // c.lui WRITE_RD(insn.rvc_imm() << 12); -} else if ((insn.rvc_rd() & 1) != 0) { // c.mop.N - #include "c_mop_N.h" +} else if ((insn.rvc_rd() & 0x11) == 1) { // c.mop.N + 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/dret.h b/riscv/insns/dret.h index 2abcc7d..bdcf3db 100644 --- a/riscv/insns/dret.h +++ b/riscv/insns/dret.h @@ -1,5 +1,8 @@ require(STATE.debug_mode); set_pc_and_serialize(STATE.dpc->read()); +if (ZICFILP_xLPE(STATE.dcsr->v, STATE.dcsr->prv)) { + STATE.elp = STATE.dcsr->pelp; +} p->set_privilege(STATE.dcsr->prv, STATE.dcsr->v); if (STATE.prv < PRV_M) STATE.mstatus->write(STATE.mstatus->read() & ~MSTATUS_MPRV); diff --git a/riscv/insns/jalr.h b/riscv/insns/jalr.h index 386e8db..0622a22 100644 --- a/riscv/insns/jalr.h +++ b/riscv/insns/jalr.h @@ -1,3 +1,7 @@ reg_t tmp = npc; set_pc((RS1 + insn.i_imm()) & ~reg_t(1)); WRITE_RD(tmp); + +if (ZICFILP_xLPE(STATE.v, STATE.prv)) { + STATE.elp = ZICFILP_IS_LP_EXPECTED(insn.rs1()); +} diff --git a/riscv/insns/lpad.h b/riscv/insns/lpad.h new file mode 100644 index 0000000..733e91d --- /dev/null +++ b/riscv/insns/lpad.h @@ -0,0 +1,6 @@ +if (ZICFILP_xLPE(STATE.v, STATE.prv) && STATE.elp == elp_t::LP_EXPECTED) { + software_check(pc % 4 == 0 && + ((READ_REG(7) & 0xFFFFF000ULL) == (static_cast<uint64_t>(insn.u_imm()) & 0xFFFFF000ULL) || insn.u_imm() == 0LL), + LANDING_PAD_FAULT); + STATE.elp = elp_t::NO_LP_EXPECTED; +} diff --git a/riscv/insns/mret.h b/riscv/insns/mret.h index 7cb1f62..1133e86 100644 --- a/riscv/insns/mret.h +++ b/riscv/insns/mret.h @@ -9,6 +9,10 @@ s = set_field(s, MSTATUS_MIE, get_field(s, MSTATUS_MPIE)); s = set_field(s, MSTATUS_MPIE, 1); s = set_field(s, MSTATUS_MPP, p->extension_enabled('U') ? PRV_U : PRV_M); s = set_field(s, MSTATUS_MPV, 0); +if (ZICFILP_xLPE(prev_virt, prev_prv)) { + STATE.elp = static_cast<elp_t>(get_field(s, MSTATUS_MPELP)); + s = set_field(s, MSTATUS_MPELP, elp_t::NO_LP_EXPECTED); +} STATE.mstatus->write(s); if (STATE.mstatush) STATE.mstatush->write(s >> 32); // log mstatush change p->set_privilege(prev_prv, prev_virt); diff --git a/riscv/insns/sret.h b/riscv/insns/sret.h index 4c7305d..aeaf087 100644 --- a/riscv/insns/sret.h +++ b/riscv/insns/sret.h @@ -13,7 +13,6 @@ reg_t prev_prv = get_field(s, MSTATUS_SPP); s = set_field(s, MSTATUS_SIE, get_field(s, MSTATUS_SPIE)); s = set_field(s, MSTATUS_SPIE, 1); s = set_field(s, MSTATUS_SPP, PRV_U); -STATE.sstatus->write(s); bool prev_virt = STATE.v; if (!STATE.v) { if (p->extension_enabled('H')) { @@ -24,4 +23,9 @@ if (!STATE.v) { STATE.mstatus->write(set_field(STATE.mstatus->read(), MSTATUS_MPRV, 0)); } +if (ZICFILP_xLPE(prev_virt, prev_prv)) { + STATE.elp = static_cast<elp_t>(get_field(s, SSTATUS_SPELP)); + s = set_field(s, SSTATUS_SPELP, elp_t::NO_LP_EXPECTED); +} +STATE.sstatus->write(s); p->set_privilege(prev_prv, prev_virt); 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<uint64_t>(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<uint32_t>(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/insns/vcompress_vm.h b/riscv/insns/vcompress_vm.h index 7195345..d35b8ba 100644 --- a/riscv/insns/vcompress_vm.h +++ b/riscv/insns/vcompress_vm.h @@ -30,4 +30,4 @@ VI_GENERAL_LOOP_BASE ++pos; } -VI_LOOP_END; +VI_LOOP_END_BASE; diff --git a/riscv/insns/wfi.h b/riscv/insns/wfi.h index 3411da0..5382072 100644 --- a/riscv/insns/wfi.h +++ b/riscv/insns/wfi.h @@ -1,9 +1,7 @@ -if (STATE.v && STATE.prv == PRV_U) { - require_novirt(); -} else if (get_field(STATE.mstatus->read(), MSTATUS_TW)) { +if (get_field(STATE.mstatus->read(), MSTATUS_TW)) { require_privilege(PRV_M); -} else if (STATE.v) { // VS-mode - if (get_field(STATE.hstatus->read(), HSTATUS_VTW)) +} else if (STATE.v) { + if (STATE.prv == PRV_U || get_field(STATE.hstatus->read(), HSTATUS_VTW)) require_novirt(); } else if (p->extension_enabled('S')) { // When S-mode is implemented, then executing WFI in diff --git a/riscv/interactive.cc b/riscv/interactive.cc index d9fb39b..71e26e6 100644 --- a/riscv/interactive.cc +++ b/riscv/interactive.cc @@ -413,7 +413,7 @@ void sim_t::interactive_run(const std::string& cmd, const std::vector<std::strin step(1); if (actual_steps < steps) { - next_interactive_action = [=](){ interactive_run(cmd, {std::to_string(steps - actual_steps)}, noisy); }; + next_interactive_action = [=, this](){ interactive_run(cmd, {std::to_string(steps - actual_steps)}, noisy); }; return; } @@ -721,7 +721,7 @@ void sim_t::interactive_until(const std::string& cmd, const std::vector<std::str if (args.size() < 3) throw trap_interactive(); - if (args.size() == 3) + if (args.size() == 4 || (args[0] == "pc" && args.size() == 3)) //dont check mem with arg len = 3 get_core(args[1]); // make sure that argument is a valid core number char *end; @@ -732,7 +732,9 @@ void sim_t::interactive_until(const std::string& cmd, const std::vector<std::str throw trap_interactive(); // mask bits above max_xlen - int max_xlen = procs[strtol(args[1].c_str(),NULL,10)]->get_isa().get_max_xlen(); + bool until_mem_paddr = args[0] == "mem" && args.size() == 3; + size_t procnum = until_mem_paddr ? 0 : strtol(args[1].c_str(), NULL, 10); + int max_xlen = procs[procnum]->get_isa().get_max_xlen(); if (max_xlen == 32) val &= 0xFFFFFFFF; std::vector<std::string> args2; @@ -766,7 +768,7 @@ void sim_t::interactive_until(const std::string& cmd, const std::vector<std::str step(1); } - next_interactive_action = [=](){ interactive_until(cmd, args, noisy); }; + next_interactive_action = [=, this](){ interactive_until(cmd, args, noisy); }; } void sim_t::interactive_dumpmems(const std::string& cmd, const std::vector<std::string>& args) diff --git a/riscv/isa_parser.h b/riscv/isa_parser.h index f310b97..72e8211 100644 --- a/riscv/isa_parser.h +++ b/riscv/isa_parser.h @@ -87,6 +87,8 @@ typedef enum { EXT_ZCMOP, 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 165be53..d10e23a 100644 --- a/riscv/mmu.cc +++ b/riscv/mmu.cc @@ -63,14 +63,14 @@ 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, access_info.flags.hlvx)) throw_access_exception(virt, addr, type); return paddr; } tlb_entry_t mmu_t::fetch_slow_path(reg_t vaddr) { - auto access_info = generate_access_info(vaddr, FETCH, {false, false, false}); + auto access_info = generate_access_info(vaddr, FETCH, {}); check_triggers(triggers::OPERATION_EXECUTE, vaddr, access_info.effective_virt); tlb_entry_t result; @@ -340,7 +340,7 @@ tlb_entry_t mmu_t::refill_tlb(reg_t vaddr, reg_t paddr, char* host_addr, access_ return entry; } -bool mmu_t::pmp_ok(reg_t addr, reg_t len, access_type type, reg_t mode) +bool mmu_t::pmp_ok(reg_t addr, reg_t len, access_type type, reg_t mode, bool hlvx) { if (!proc || proc->n_pmp == 0) return true; @@ -361,7 +361,7 @@ bool mmu_t::pmp_ok(reg_t addr, reg_t len, access_type type, reg_t mode) if (!all_match) return false; - return proc->state.pmpaddr[i]->access_ok(type, mode); + return proc->state.pmpaddr[i]->access_ok(type, mode, hlvx); } } @@ -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 a163fe4..3e4ae9a 100644 --- a/riscv/mmu.h +++ b/riscv/mmu.h @@ -39,12 +39,13 @@ struct tlb_entry_t { }; struct xlate_flags_t { - const bool forced_virt : 1; - const bool hlvx : 1; - const bool lr : 1; + 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; } }; @@ -72,7 +73,7 @@ private: mem_access_info_t generate_access_info(reg_t addr, access_type type, xlate_flags_t xlate_flags) { if (!proc) - return {addr, 0, false, {false, false, false}, type}; + return {addr, 0, false, {}, type}; bool virt = proc->state.v; reg_t mode = proc->state.prv; if (type != FETCH) { @@ -94,7 +95,7 @@ public: ~mmu_t(); template<typename T> - T ALWAYS_INLINE load(reg_t addr, xlate_flags_t xlate_flags = {false, false, false}) { + T ALWAYS_INLINE load(reg_t addr, xlate_flags_t xlate_flags = {}) { target_endian<T> res; reg_t vpn = addr >> PGSHIFT; bool aligned = (addr & (sizeof(T) - 1)) == 0; @@ -114,30 +115,29 @@ public: template<typename T> T load_reserved(reg_t addr) { - bool forced_virt = false; - bool hlvx = false; - bool lr = true; - return load<T>(addr, {forced_virt, hlvx, lr}); + return load<T>(addr, {.lr = true}); } template<typename T> T guest_load(reg_t addr) { - bool forced_virt = true; - bool hlvx = false; - bool lr = false; - return load<T>(addr, {forced_virt, hlvx, lr}); + return load<T>(addr, {.forced_virt = true}); } template<typename T> T guest_load_x(reg_t addr) { - bool forced_virt = true; - bool hlvx = true; - bool lr = false; - return load<T>(addr, {forced_virt, hlvx, lr}); + return load<T>(addr, {.forced_virt=true, .hlvx=true}); } + // shadow stack load template<typename T> - void ALWAYS_INLINE store(reg_t addr, T val, xlate_flags_t xlate_flags = {false, false, false}) { + 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<T>(addr, {.forced_virt=false, .hlvx=false, .lr=false, .ss_access=true}); + } + + template<typename T> + void ALWAYS_INLINE store(reg_t addr, T val, xlate_flags_t xlate_flags = {}) { reg_t vpn = addr >> PGSHIFT; bool aligned = (addr & (sizeof(T) - 1)) == 0; bool tlb_hit = tlb_store_tag[vpn % TLB_ENTRIES] == vpn; @@ -155,10 +155,15 @@ public: template<typename T> void guest_store(reg_t addr, T val) { - bool forced_virt = true; - bool hlvx = false; - bool lr = false; - store(addr, val, {forced_virt, hlvx, lr}); + store(addr, val, {.forced_virt=true}); + } + + // shadow stack store + template<typename T> + 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<T>(addr, val, {.forced_virt=false, .hlvx=false, .lr=false, .ss_access=true}); } // AMO/Zicbom faults should be reported as store faults @@ -180,17 +185,30 @@ public: template<typename T, typename op> T amo(reg_t addr, op f) { convert_load_traps_to_store_traps({ - store_slow_path(addr, sizeof(T), nullptr, {false, false, false}, false, true); + store_slow_path(addr, sizeof(T), nullptr, {}, false, true); auto lhs = load<T>(addr); store<T>(addr, f(lhs)); return lhs; }) } + // for shadow stack amoswap + template<typename T> + 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<T>(addr, {forced_virt, hlvx, lr, ss_access}); + store<T>(addr, value, {forced_virt, hlvx, lr, ss_access}); + return data; + } + template<typename T> T amo_compare_and_swap(reg_t addr, T comp, T swap) { convert_load_traps_to_store_traps({ - store_slow_path(addr, sizeof(T), nullptr, {false, false, false}, false, true); + store_slow_path(addr, sizeof(T), nullptr, {}, false, true); auto lhs = load<T>(addr); if (lhs == comp) store<T>(addr, swap); @@ -230,7 +248,7 @@ public: for (size_t offset = 0; offset < blocksz; offset += 1) check_triggers(triggers::OPERATION_STORE, base + offset, false, addr, std::nullopt); convert_load_traps_to_store_traps({ - const reg_t paddr = translate(generate_access_info(addr, LOAD, {false, false, false}), 1); + const reg_t paddr = translate(generate_access_info(addr, LOAD, {}), 1); if (sim->reservable(paddr)) { if (tracer.interested_in_range(paddr, paddr + PGSIZE, LOAD)) tracer.clean_invalidate(paddr, blocksz, clean, inval); @@ -249,10 +267,10 @@ public: { if (vaddr & (size-1)) { // Raise either access fault or misaligned exception - store_slow_path(vaddr, size, nullptr, {false, false, false}, false, true); + store_slow_path(vaddr, size, nullptr, {}, false, true); } - reg_t paddr = translate(generate_access_info(vaddr, STORE, {false, false, false}), 1); + reg_t paddr = translate(generate_access_info(vaddr, STORE, {}), 1); if (sim->reservable(paddr)) return load_reservation_address == paddr; else @@ -431,7 +449,7 @@ private: { const size_t ptesize = sizeof(T); - if (!pmp_ok(pte_paddr, ptesize, LOAD, PRV_S)) + if (!pmp_ok(pte_paddr, ptesize, LOAD, PRV_S, false)) throw_access_exception(virt, addr, trap_type); void* host_pte_addr = sim->addr_to_mem(pte_paddr); @@ -448,7 +466,7 @@ private: { const size_t ptesize = sizeof(T); - if (!pmp_ok(pte_paddr, ptesize, STORE, PRV_S)) + if (!pmp_ok(pte_paddr, ptesize, STORE, PRV_S, false)) throw_access_exception(virt, addr, trap_type); void* host_pte_addr = sim->addr_to_mem(pte_paddr); @@ -481,7 +499,7 @@ private: } reg_t pmp_homogeneous(reg_t addr, reg_t len); - bool pmp_ok(reg_t addr, reg_t len, access_type type, reg_t mode); + bool pmp_ok(reg_t addr, reg_t len, access_type type, reg_t mode, bool hlvx); #ifdef RISCV_ENABLE_DUAL_ENDIAN bool target_big_endian; diff --git a/riscv/ns16550.cc b/riscv/ns16550.cc index a74aa74..e0b3251 100644 --- a/riscv/ns16550.cc +++ b/riscv/ns16550.cc @@ -2,6 +2,7 @@ #include <sstream> #include "devices.h" #include "processor.h" +#include "mmu.h" #include "term.h" #include "sim.h" #include "dts.h" @@ -170,6 +171,9 @@ bool ns16550_t::load(reg_t addr, size_t len, uint8_t* bytes) if (reg_io_width != len) { return false; } + if (addr + len > PGSIZE) { + return false; + } addr >>= reg_shift; addr &= 7; @@ -230,6 +234,9 @@ bool ns16550_t::store(reg_t addr, size_t len, const uint8_t* bytes) if (reg_io_width != len) { return false; } + if (addr + len > PGSIZE) { + return false; + } addr >>= reg_shift; addr &= 7; val = bytes[0]; diff --git a/riscv/overlap_list.h b/riscv/overlap_list.h index 2214be4..6c943bd 100644 --- a/riscv/overlap_list.h +++ b/riscv/overlap_list.h @@ -21,3 +21,11 @@ DECLARE_OVERLAP_INSN(rstsa16, EXT_ZPN) 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 9739ab7..7a63f3c 100644 --- a/riscv/processor.cc +++ b/riscv/processor.cc @@ -94,6 +94,14 @@ processor_t::~processor_t() delete disassembler; } +static void zicfilp_check_if_lpad_required(const elp_t elp, insn_t insn) +{ + if (unlikely(elp == elp_t::LP_EXPECTED)) { + // also see riscv/lpad.h for more checks performed + software_check((insn.bits() & MASK_LPAD) == MATCH_LPAD, LANDING_PAD_FAULT); + } +} + static void bad_option_string(const char *option, const char *value, const char *msg) { @@ -304,15 +312,6 @@ void state_t::reset(processor_t* const proc, reg_t max_isa) 0 // shiftamt ); - auto hvip_accr = std::make_shared<generic_int_accessor_t>( - this, - MIP_VS_MASK, // read_mask - MIP_VS_MASK, // ip_write_mask - MIP_VS_MASK, // ie_write_mask - generic_int_accessor_t::mask_mode_t::NONE, - 0 // shiftamt - ); - auto vsip_vsie_accr = std::make_shared<generic_int_accessor_t>( this, MIP_VS_MASK, // read_mask @@ -327,7 +326,7 @@ void state_t::reset(processor_t* const proc, reg_t max_isa) csrmap[CSR_VSIP] = vsip; csrmap[CSR_SIP] = std::make_shared<virtualized_csr_t>(proc, nonvirtual_sip, vsip); csrmap[CSR_HIP] = std::make_shared<mip_proxy_csr_t>(proc, CSR_HIP, hip_hie_accr); - csrmap[CSR_HVIP] = std::make_shared<mip_proxy_csr_t>(proc, CSR_HVIP, hvip_accr); + csrmap[CSR_HVIP] = hvip = std::make_shared<hvip_csr_t>(proc, CSR_HVIP, 0); auto nonvirtual_sie = std::make_shared<mie_proxy_csr_t>(proc, CSR_SIE, sip_sie_accr); auto vsie = std::make_shared<mie_proxy_csr_t>(proc, CSR_VSIE, vsip_vsie_accr); @@ -383,7 +382,8 @@ void state_t::reset(processor_t* const proc, reg_t max_isa) (1 << CAUSE_USER_ECALL) | (1 << CAUSE_FETCH_PAGE_FAULT) | (1 << CAUSE_LOAD_PAGE_FAULT) | - (1 << CAUSE_STORE_PAGE_FAULT); + (1 << CAUSE_STORE_PAGE_FAULT) | + (1 << CAUSE_SOFTWARE_CHECK_FAULT); csrmap[CSR_HEDELEG] = hedeleg = std::make_shared<masked_csr_t>(proc, CSR_HEDELEG, hedeleg_mask, 0); csrmap[CSR_HCOUNTEREN] = hcounteren = std::make_shared<masked_csr_t>(proc, CSR_HCOUNTEREN, counteren_mask, 0); htimedelta = std::make_shared<basic_csr_t>(proc, CSR_HTIMEDELTA, 0); @@ -417,7 +417,7 @@ void state_t::reset(processor_t* const proc, reg_t max_isa) csrmap[CSR_TDATA3] = std::make_shared<const_csr_t>(proc, CSR_TDATA3, 0); csrmap[CSR_TINFO] = std::make_shared<const_csr_t>(proc, CSR_TINFO, 0); } - unsigned scontext_length = (xlen == 32 ? 16 : 34); // debug spec suggests 16-bit for RV32 and 34-bit for RV64 + unsigned scontext_length = (xlen == 32 ? 16 : 32); // debug spec suggests 16-bit for RV32 and 32-bit for RV64 csrmap[CSR_SCONTEXT] = scontext = std::make_shared<masked_csr_t>(proc, CSR_SCONTEXT, (reg_t(1) << scontext_length) - 1, 0); unsigned hcontext_length = (xlen == 32 ? 6 : 13) + (proc->extension_enabled('H') ? 1 : 0); // debug spec suggest 7-bit (6-bit) for RV32 and 14-bit (13-bit) for RV64 with (without) H extension csrmap[CSR_HCONTEXT] = std::make_shared<masked_csr_t>(proc, CSR_HCONTEXT, (reg_t(1) << hcontext_length) - 1, 0); @@ -452,7 +452,9 @@ void state_t::reset(processor_t* const proc, reg_t max_isa) (proc->extension_enabled(EXT_ZICBOZ) ? MENVCFG_CBZE : 0) | (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_SSTC) ? MENVCFG_STCE : 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<envcfg_csr_t>(proc, CSR_MENVCFG, menvcfg_mask, menvcfg_init); if (xlen == 32) { @@ -462,13 +464,17 @@ void state_t::reset(processor_t* const proc, reg_t max_isa) csrmap[CSR_MENVCFG] = menvcfg; } 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_ZICBOZ) ? SENVCFG_CBZE : 0) | + (proc->extension_enabled(EXT_ZICFILP) ? SENVCFG_LPE : 0) | + (proc->extension_enabled(EXT_ZICFISS) ? SENVCFG_SSE : 0); csrmap[CSR_SENVCFG] = senvcfg = std::make_shared<senvcfg_csr_t>(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_SSTC) ? HENVCFG_STCE : 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<henvcfg_csr_t>(proc, CSR_HENVCFG, henvcfg_mask, henvcfg_init, menvcfg); if (xlen == 32) { @@ -533,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<jvt_csr_t>(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<ssp_csr_t>(proc, CSR_SSP, ssp_mask, 0); + } + // Smcsrind / Sscsrind sscsrind_reg_csr_t::sscsrind_reg_csr_t_p mireg[6]; @@ -599,6 +610,8 @@ void state_t::reset(processor_t* const proc, reg_t max_isa) last_inst_priv = 0; last_inst_xlen = 0; last_inst_flen = 0; + + elp = elp_t::NO_LP_EXPECTED; } void processor_t::set_debug(bool value) @@ -819,8 +832,10 @@ const char* processor_t::get_privilege_string() void processor_t::enter_debug_mode(uint8_t cause) { + const bool has_zicfilp = extension_enabled(EXT_ZICFILP); state.debug_mode = true; - state.dcsr->write_cause_and_prv(cause, state.prv, state.v); + state.dcsr->update_fields(cause, state.prv, state.v, state.elp); + state.elp = elp_t::NO_LP_EXPECTED; set_privilege(PRV_M, false); state.dpc->write(state.pc); state.pc = DEBUG_ROM_ENTRY; @@ -889,6 +904,8 @@ void processor_t::take_trap(trap_t& t, reg_t epc) s = set_field(s, MSTATUS_SPIE, get_field(s, MSTATUS_SIE)); s = set_field(s, MSTATUS_SPP, state.prv); s = set_field(s, MSTATUS_SIE, 0); + s = set_field(s, MSTATUS_SPELP, state.elp); + state.elp = elp_t::NO_LP_EXPECTED; state.sstatus->write(s); set_privilege(PRV_S, true); } else if (state.prv <= PRV_S && bit < max_xlen && ((hsdeleg >> bit) & 1)) { @@ -905,6 +922,8 @@ void processor_t::take_trap(trap_t& t, reg_t epc) s = set_field(s, MSTATUS_SPIE, get_field(s, MSTATUS_SIE)); s = set_field(s, MSTATUS_SPP, state.prv); s = set_field(s, MSTATUS_SIE, 0); + s = set_field(s, MSTATUS_SPELP, state.elp); + state.elp = elp_t::NO_LP_EXPECTED; state.nonvirtual_sstatus->write(s); if (extension_enabled('H')) { s = state.hstatus->read(); @@ -936,6 +955,8 @@ void processor_t::take_trap(trap_t& t, reg_t epc) s = set_field(s, MSTATUS_MIE, 0); s = set_field(s, MSTATUS_MPV, curr_virt); s = set_field(s, MSTATUS_GVA, t.has_gva()); + s = set_field(s, MSTATUS_MPELP, state.elp); + state.elp = elp_t::NO_LP_EXPECTED; state.mstatus->write(s); if (state.mstatush) state.mstatush->write(s >> 32); // log mstatush change set_privilege(PRV_M, false); @@ -970,6 +991,13 @@ const char* processor_t::get_symbol(uint64_t addr) return sim->get_symbol(addr); } +void processor_t::execute_insn_prehook(insn_t insn) +{ + if (extension_enabled(EXT_ZICFILP)) { + zicfilp_check_if_lpad_required(state.elp, insn); + } +} + void processor_t::disasm(insn_t insn) { uint64_t bits = insn.bits(); @@ -1046,6 +1074,24 @@ reg_t illegal_instruction(processor_t UNUSED *p, insn_t insn, reg_t UNUSED pc) throw trap_illegal_instruction(insn.bits() & 0xffffffffULL); } +static insn_desc_t +propagate_instruction_in_vector(std::vector<insn_desc_t> &instructions, + std::vector<insn_desc_t>::iterator it) { + assert(it != instructions.end()); + insn_desc_t desc = *it; + if (it->mask != 0 && it != instructions.begin() && + std::next(it) != instructions.end()) { + if (it->match != std::prev(it)->match && + it->match != std::next(it)->match) { + // move to front of opcode list to reduce miss penalty + while (--it >= instructions.begin()) + *std::next(it) = *it; + instructions[0] = desc; + } + } + return desc; +} + insn_func_t processor_t::decode_insn(insn_t insn) { // look up opcode in hash table @@ -1056,21 +1102,18 @@ insn_func_t processor_t::decode_insn(insn_t insn) if (unlikely(insn.bits() != desc.match)) { // fall back to linear search - int cnt = 0; - insn_desc_t* p = &instructions[0]; - while ((insn.bits() & p->mask) != p->match) - p++, cnt++; - desc = *p; - - if (p->mask != 0 && p > &instructions[0]) { - if (p->match != (p - 1)->match && p->match != (p + 1)->match) { - // move to front of opcode list to reduce miss penalty - while (--p >= &instructions[0]) - *(p + 1) = *p; - instructions[0] = desc; - } + auto matching = [insn_bits = insn.bits()](const insn_desc_t &d) { + return (insn_bits & d.mask) == d.match; + }; + auto p = std::find_if(custom_instructions.begin(), + custom_instructions.end(), matching); + if (p != custom_instructions.end()) { + desc = propagate_instruction_in_vector(custom_instructions, p); + } else { + p = std::find_if(instructions.begin(), instructions.end(), matching); + assert(p != instructions.end()); + desc = propagate_instruction_in_vector(instructions, p); } - opcode_cache[idx] = desc; opcode_cache[idx].match = insn.bits(); } @@ -1078,12 +1121,14 @@ insn_func_t processor_t::decode_insn(insn_t insn) return desc.func(xlen, rve, log_commits_enabled); } -void processor_t::register_insn(insn_desc_t desc) -{ +void processor_t::register_insn(insn_desc_t desc, bool is_custom) { assert(desc.fast_rv32i && desc.fast_rv64i && desc.fast_rv32e && desc.fast_rv64e && desc.logged_rv32i && desc.logged_rv64i && desc.logged_rv32e && desc.logged_rv64e); - instructions.push_back(desc); + if (is_custom) + custom_instructions.push_back(desc); + else + instructions.push_back(desc); } void processor_t::build_opcode_map() @@ -1095,16 +1140,17 @@ void processor_t::build_opcode_map() return lhs.match > rhs.match; } }; + std::sort(instructions.begin(), instructions.end(), cmp()); + std::sort(custom_instructions.begin(), custom_instructions.end(), cmp()); for (size_t i = 0; i < OPCODE_CACHE_SIZE; i++) opcode_cache[i] = insn_desc_t::illegal(); } -void processor_t::register_extension(extension_t* x) -{ +void processor_t::register_extension(extension_t *x) { for (auto insn : x->get_instructions()) - register_insn(insn); + register_custom_insn(insn); build_opcode_map(); for (auto disasm_insn : x->get_disasms()) @@ -1140,7 +1186,7 @@ void processor_t::register_base_instructions() extern reg_t logged_rv32e_##name(processor_t*, insn_t, reg_t); \ extern reg_t logged_rv64e_##name(processor_t*, insn_t, reg_t); \ if (name##_supported) { \ - register_insn((insn_desc_t) { \ + register_base_insn((insn_desc_t) { \ name##_match, \ name##_mask, \ fast_rv32i_##name, \ @@ -1153,10 +1199,8 @@ void processor_t::register_base_instructions() logged_rv64e_##name}); \ } #include "insn_list.h" - #undef DEFINE_INSN - // terminate instruction list with a catch-all - register_insn(insn_desc_t::illegal()); + register_base_insn(insn_desc_t::illegal()); build_opcode_map(); } diff --git a/riscv/processor.h b/riscv/processor.h index 0394e09..f3e5294 100644 --- a/riscv/processor.h +++ b/riscv/processor.h @@ -128,6 +128,7 @@ struct state_t csr_t_p htval; csr_t_p htinst; csr_t_p hgatp; + hvip_csr_t_p hvip; sstatus_csr_t_p sstatus; vsstatus_csr_t_p vsstatus; csr_t_p vstvec; @@ -172,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 @@ -188,6 +191,8 @@ struct state_t reg_t last_inst_priv; int last_inst_xlen; int last_inst_flen; + + elp_t elp; }; // this class represents one processor in a RISC-V machine. @@ -280,7 +285,12 @@ public: FILE *get_log_file() { return log_file; } - void register_insn(insn_desc_t); + void register_base_insn(insn_desc_t insn) { + register_insn(insn, false /* is_custom */); + } + void register_custom_insn(insn_desc_t insn) { + register_insn(insn, true /* is_custom */); + } void register_extension(extension_t*); // MMIO slave interface @@ -309,6 +319,8 @@ public: void clear_waiting_for_interrupt() { in_wfi = false; }; bool is_waiting_for_interrupt() { return in_wfi; }; + void execute_insn_prehook(insn_t insn); + private: const isa_parser_t * const isa; const cfg_t * const cfg; @@ -335,6 +347,7 @@ private: mutable std::bitset<NUM_ISA_EXTENSIONS> extension_assumed_const; std::vector<insn_desc_t> instructions; + std::vector<insn_desc_t> custom_instructions; std::unordered_map<reg_t,uint64_t> pc_histogram; static const size_t OPCODE_CACHE_SIZE = 8191; @@ -345,6 +358,7 @@ private: void take_trap(trap_t& t, reg_t epc); // take an exception void take_trigger_action(triggers::action_t action, reg_t breakpoint_tval, reg_t epc, bool virt); void disasm(insn_t insn); // disassemble and print an instruction + void register_insn(insn_desc_t, bool); int paddr_bits(); void enter_debug_mode(uint8_t cause); diff --git a/riscv/riscv.mk.in b/riscv/riscv.mk.in index 04747c9..f388426 100644 --- a/riscv/riscv.mk.in +++ b/riscv/riscv.mk.in @@ -7,7 +7,7 @@ riscv_subproject_deps = \ fesvr \ softfloat \ -riscv_CFLAGS = -fPIC +riscv_CFLAGS = -fPIC -I$(src_dir)/fdt riscv_install_shared_lib = yes @@ -1392,6 +1392,20 @@ riscv_insn_ext_zimop = \ mop_r_N \ mop_rr_N \ +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) \ @@ -1431,6 +1445,8 @@ riscv_insn_list = \ $(riscv_insn_smrnmi) \ $(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/trap.h b/riscv/trap.h index 54948fd..5eb62cf 100644 --- a/riscv/trap.h +++ b/riscv/trap.h @@ -119,5 +119,6 @@ DECLARE_MEM_GVA_TRAP(CAUSE_FETCH_GUEST_PAGE_FAULT, instruction_guest_page_fault) DECLARE_MEM_GVA_TRAP(CAUSE_LOAD_GUEST_PAGE_FAULT, load_guest_page_fault) DECLARE_INST_TRAP(CAUSE_VIRTUAL_INSTRUCTION, virtual_instruction) DECLARE_MEM_GVA_TRAP(CAUSE_STORE_GUEST_PAGE_FAULT, store_guest_page_fault) +DECLARE_INST_TRAP(CAUSE_SOFTWARE_CHECK_FAULT, software_check) #endif diff --git a/riscv/v_ext_macros.h b/riscv/v_ext_macros.h index b198d54..42d723a 100644 --- a/riscv/v_ext_macros.h +++ b/riscv/v_ext_macros.h @@ -215,8 +215,11 @@ static inline bool is_overlapped_widen(const int astart, int asize, VI_GENERAL_LOOP_BASE \ VI_LOOP_ELEMENT_SKIP(); +#define VI_LOOP_END_BASE \ + } + #define VI_LOOP_END \ - } \ + VI_LOOP_END_BASE \ P.VU.vstart->write(0); #define VI_LOOP_REDUCTION_END(x) \ 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<uint32_t>(push_ssp_addr, push_value); \ + else \ + MMU.ss_store<uint64_t>(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<uint32_t>(STATE.ssp->read()); \ + else \ + shadow_return_addr = MMU.ss_load<uint64_t>(STATE.ssp->read()); \ + software_check(value == shadow_return_addr, SHADOW_STACK_FAULT); \ + STATE.ssp->write(STATE.ssp->read() + xlen / 8); + +#endif diff --git a/spike_main/spike.cc b/spike_main/spike.cc index b5a1ce4..09a1fe8 100644 --- a/spike_main/spike.cc +++ b/spike_main/spike.cc @@ -53,7 +53,8 @@ static void help(int exit_code = 1) fprintf(stderr, " --l2=<S>:<W>:<B> B both powers of 2).\n"); fprintf(stderr, " --big-endian Use a big-endian memory system.\n"); fprintf(stderr, " --misaligned Support misaligned memory accesses\n"); - fprintf(stderr, " --device=<name> Attach MMIO plugin device from an --extlib library\n"); + fprintf(stderr, " --device=<name> Attach MMIO plugin device from an --extlib library,\n"); + fprintf(stderr, " specify --device=<name>,<args> to pass down extra args.\n"); fprintf(stderr, " --log-cache-miss Generate a log of cache miss\n"); fprintf(stderr, " --log-commits Generate a log of commits info\n"); fprintf(stderr, " --extension=<name> Specify RoCC Extension\n"); |