aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/continuous-integration.yml2
-rw-r--r--.github/workflows/debug-smoke.yml2
-rw-r--r--Makefile.in4
-rw-r--r--README.md3
-rwxr-xr-xci-tests/create-ci-binary-tarball10
-rw-r--r--ci-tests/dummy-slliuw.c8
-rw-r--r--ci-tests/test-customext.cc90
-rwxr-xr-xci-tests/test-spike5
-rw-r--r--ci-tests/testlib.c11
-rw-r--r--disasm/disasm.cc68
-rw-r--r--disasm/isa_parser.cc20
-rw-r--r--fdt/fdt.mk.in2
-rw-r--r--fesvr/syscall.cc14
-rw-r--r--fesvr/syscall.h1
-rw-r--r--riscv/csrs.cc66
-rw-r--r--riscv/csrs.h34
-rw-r--r--riscv/decode.h1
-rw-r--r--riscv/decode_macros.h20
-rw-r--r--riscv/encoding.h9
-rw-r--r--riscv/execute.cc2
-rw-r--r--riscv/insns/c_jalr.h4
-rw-r--r--riscv/insns/c_jr.h4
-rw-r--r--riscv/insns/c_lui.h10
-rw-r--r--riscv/insns/c_sspopchk_x5.h5
-rw-r--r--riscv/insns/c_sspush_x1.h5
-rw-r--r--riscv/insns/dret.h3
-rw-r--r--riscv/insns/jalr.h4
-rw-r--r--riscv/insns/lpad.h6
-rw-r--r--riscv/insns/mret.h4
-rw-r--r--riscv/insns/sret.h6
-rw-r--r--riscv/insns/ssamoswap_d.h7
-rw-r--r--riscv/insns/ssamoswap_w.h7
-rw-r--r--riscv/insns/sspopchk_x1.h7
-rw-r--r--riscv/insns/sspopchk_x5.h1
-rw-r--r--riscv/insns/sspush_x1.h7
-rw-r--r--riscv/insns/sspush_x5.h1
-rw-r--r--riscv/insns/ssrdp.h7
-rw-r--r--riscv/insns/vcompress_vm.h2
-rw-r--r--riscv/insns/wfi.h8
-rw-r--r--riscv/interactive.cc10
-rw-r--r--riscv/isa_parser.h2
-rw-r--r--riscv/mmu.cc35
-rw-r--r--riscv/mmu.h80
-rw-r--r--riscv/ns16550.cc7
-rw-r--r--riscv/overlap_list.h8
-rw-r--r--riscv/processor.cc124
-rw-r--r--riscv/processor.h16
-rw-r--r--riscv/riscv.mk.in18
-rw-r--r--riscv/trap.h1
-rw-r--r--riscv/v_ext_macros.h5
-rw-r--r--riscv/zicfiss.h31
-rw-r--r--spike_main/spike.cc3
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@
diff --git a/README.md b/README.md
index 2c5bba9..ef2a1ab 100644
--- a/README.md
+++ b/README.md
@@ -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");