diff options
author | Andrew Waterman <andrew@sifive.com> | 2020-05-10 01:43:04 -0700 |
---|---|---|
committer | Andrew Waterman <andrew@sifive.com> | 2020-05-10 01:43:04 -0700 |
commit | 9430a823731b068c16972f5d790c55661e183acf (patch) | |
tree | dc5a0d9c818cb4301bb4e754c76b37c4350be56c /riscv | |
parent | 90e67e05789a0b0de95c6e0910d86658847c06bb (diff) | |
parent | 0212b6d66f9f321732480b20b368ee0882415314 (diff) | |
download | spike-9430a823731b068c16972f5d790c55661e183acf.zip spike-9430a823731b068c16972f5d790c55661e183acf.tar.gz spike-9430a823731b068c16972f5d790c55661e183acf.tar.bz2 |
Merge branch 'configurable_PMP'
Diffstat (limited to 'riscv')
-rw-r--r-- | riscv/dts.cc | 97 | ||||
-rw-r--r-- | riscv/dts.h | 6 | ||||
-rw-r--r-- | riscv/mmu.cc | 14 | ||||
-rw-r--r-- | riscv/processor.cc | 67 | ||||
-rw-r--r-- | riscv/processor.h | 14 | ||||
-rw-r--r-- | riscv/riscv.mk.in | 1 | ||||
-rw-r--r-- | riscv/sim.cc | 81 | ||||
-rw-r--r-- | riscv/sim.h | 11 |
8 files changed, 252 insertions, 39 deletions
diff --git a/riscv/dts.cc b/riscv/dts.cc index 5dbed86..c12ff06 100644 --- a/riscv/dts.cc +++ b/riscv/dts.cc @@ -1,6 +1,7 @@ // See LICENSE for license details. #include "dts.h" +#include "libfdt.h" #include <iostream> #include <sstream> #include <signal.h> @@ -43,6 +44,8 @@ std::string make_dts(size_t insns_per_rtc_tick, size_t cpu_hz, " compatible = \"riscv\";\n" " riscv,isa = \"" << procs[i]->get_isa_string() << "\";\n" " mmu-type = \"riscv," << (procs[i]->get_max_xlen() <= 32 ? "sv32" : "sv48") << "\";\n" + " riscv,pmpregions = <16>;\n" + " riscv,pmpgranularity = <4>;\n" " clock-frequency = <" << cpu_hz << ">;\n" " CPU" << i << "_intc: interrupt-controller {\n" " #interrupt-cells = <1>;\n" @@ -165,3 +168,97 @@ std::string dts_compile(const std::string& dts) return dtb.str(); } + + +static int fdt_get_node_addr_size(void *fdt, int node, unsigned long *addr, + unsigned long *size, const char *field) +{ + int parent, len, i; + int cell_addr, cell_size; + const fdt32_t *prop_addr, *prop_size; + uint64_t temp = 0; + + parent = fdt_parent_offset(fdt, node); + if (parent < 0) + return parent; + + cell_addr = fdt_address_cells(fdt, parent); + if (cell_addr < 1) + return -ENODEV; + + cell_size = fdt_size_cells(fdt, parent); + if (cell_size < 0) + return -ENODEV; + + if (!field) + return -ENODEV; + + prop_addr = (fdt32_t *)fdt_getprop(fdt, node, field, &len); + if (!prop_addr) + return -ENODEV; + prop_size = prop_addr + cell_addr; + + if (addr) { + for (i = 0; i < cell_addr; i++) + temp = (temp << 32) | fdt32_to_cpu(*prop_addr++); + *addr = temp; + } + temp = 0; + + if (size) { + for (i = 0; i < cell_size; i++) + temp = (temp << 32) | fdt32_to_cpu(*prop_size++); + *size = temp; + } + + return 0; +} + +int fdt_parse_clint(void *fdt, unsigned long *clint_addr, + const char *compatible) +{ + int nodeoffset, rc; + + nodeoffset = fdt_node_offset_by_compatible(fdt, -1, compatible); + if (nodeoffset < 0) + return nodeoffset; + + rc = fdt_get_node_addr_size(fdt, nodeoffset, clint_addr, NULL, "reg"); + if (rc < 0 || !clint_addr) + return -ENODEV; + + return 0; +} + +int fdt_parse_pmp_num(void *fdt, unsigned long *pmp_num, const char *compatible) +{ + int nodeoffset, rc; + + nodeoffset = fdt_node_offset_by_compatible(fdt, -1, compatible); + if (nodeoffset < 0) + return nodeoffset; + + rc = fdt_get_node_addr_size(fdt, nodeoffset, pmp_num, NULL, + "riscv,pmpregions"); + if (rc < 0 || !pmp_num) + return -ENODEV; + + return 0; +} + +int fdt_parse_pmp_alignment(void *fdt, unsigned long *pmp_align, + const char *compatible) +{ + int nodeoffset, rc; + + nodeoffset = fdt_node_offset_by_compatible(fdt, -1, compatible); + if (nodeoffset < 0) + return nodeoffset; + + rc = fdt_get_node_addr_size(fdt, nodeoffset, pmp_align, NULL, + "riscv,pmpgranularity"); + if (rc < 0 || !pmp_align) + return -ENODEV; + + return 0; +} diff --git a/riscv/dts.h b/riscv/dts.h index b9ddb8e..08d8f9d 100644 --- a/riscv/dts.h +++ b/riscv/dts.h @@ -13,4 +13,10 @@ std::string make_dts(size_t insns_per_rtc_tick, size_t cpu_hz, std::string dts_compile(const std::string& dts); +int fdt_parse_clint(void *fdt, unsigned long *clint_addr, + const char *compatible); +int fdt_parse_pmp_num(void *fdt, unsigned long *pmp_num, + const char *compatible); +int fdt_parse_pmp_alignment(void *fdt, unsigned long *pmp_align, + const char *compatible); #endif diff --git a/riscv/mmu.cc b/riscv/mmu.cc index 1e8dd8b..b038f23 100644 --- a/riscv/mmu.cc +++ b/riscv/mmu.cc @@ -200,19 +200,19 @@ tlb_entry_t mmu_t::refill_tlb(reg_t vaddr, reg_t paddr, char* host_addr, access_ reg_t mmu_t::pmp_ok(reg_t addr, reg_t len, access_type type, reg_t mode) { - if (!proc) + if (!proc || proc->n_pmp == 0) return true; reg_t base = 0; - for (size_t i = 0; i < proc->state.n_pmp; i++) { - reg_t tor = proc->state.pmpaddr[i] << PMP_SHIFT; + for (size_t i = 0; i < proc->n_pmp; i++) { + reg_t tor = (proc->state.pmpaddr[i] & proc->pmp_tor_mask()) << PMP_SHIFT; uint8_t cfg = proc->state.pmpcfg[i]; if (cfg & PMP_A) { bool is_tor = (cfg & PMP_A) == PMP_TOR; bool is_na4 = (cfg & PMP_A) == PMP_NA4; - reg_t mask = (proc->state.pmpaddr[i] << 1) | (!is_na4); + reg_t mask = (proc->state.pmpaddr[i] << 1) | (!is_na4) | ~proc->pmp_tor_mask(); mask = ~(mask & ~(mask + 1)) << PMP_SHIFT; // Check each 4-byte sector of the access @@ -255,8 +255,8 @@ reg_t mmu_t::pmp_homogeneous(reg_t addr, reg_t len) return true; reg_t base = 0; - for (size_t i = 0; i < proc->state.n_pmp; i++) { - reg_t tor = proc->state.pmpaddr[i] << PMP_SHIFT; + for (size_t i = 0; i < proc->n_pmp; i++) { + reg_t tor = (proc->state.pmpaddr[i] & proc->pmp_tor_mask()) << PMP_SHIFT; uint8_t cfg = proc->state.pmpcfg[i]; if (cfg & PMP_A) { @@ -270,7 +270,7 @@ reg_t mmu_t::pmp_homogeneous(reg_t addr, reg_t len) bool tor_homogeneous = ends_before_lower || begins_after_upper || (begins_after_lower && ends_before_upper); - reg_t mask = (proc->state.pmpaddr[i] << 1) | (!is_na4); + reg_t mask = (proc->state.pmpaddr[i] << 1) | (!is_na4) | ~proc->pmp_tor_mask(); mask = ~(mask & ~(mask + 1)) << PMP_SHIFT; bool mask_homogeneous = ~(mask << 1) & len; bool napot_homogeneous = mask_homogeneous || ((addr ^ tor) / len) != 0; diff --git a/riscv/processor.cc b/riscv/processor.cc index bf3c40a..2dc930b 100644 --- a/riscv/processor.cc +++ b/riscv/processor.cc @@ -43,6 +43,8 @@ processor_t::processor_t(const char* isa, const char* priv, const char* varch, for (auto disasm_insn : ext->get_disasms()) disassembler->add_insn(disasm_insn); + set_pmp_granularity(1 << PMP_SHIFT); + set_pmp_num(state.max_pmp); reset(); } @@ -421,10 +423,12 @@ void processor_t::reset() set_csr(CSR_MSTATUS, state.mstatus); VU.reset(); - // For backwards compatibility with software that is unaware of PMP, - // initialize PMP to permit unprivileged access to all of memory. - set_csr(CSR_PMPADDR0, ~reg_t(0)); - set_csr(CSR_PMPCFG0, PMP_R | PMP_W | PMP_X | PMP_NAPOT); + if (n_pmp > 0) { + // For backwards compatibility with software that is unaware of PMP, + // initialize PMP to permit unprivileged access to all of memory. + set_csr(CSR_PMPADDR0, ~reg_t(0)); + set_csr(CSR_PMPCFG0, PMP_R | PMP_W | PMP_X | PMP_NAPOT); + } if (ext) ext->reset(); // reset the extension @@ -443,6 +447,26 @@ static int ctz(reg_t val) return res; } +void processor_t::set_pmp_num(reg_t n) +{ + // check the number of pmp is in a reasonable range + if (n > state.max_pmp) { + fprintf(stderr, "error: bad number of pmp regions: '%ld' from the dtb\n", (unsigned long)n); + abort(); + } + n_pmp = n; +} + +void processor_t::set_pmp_granularity(reg_t gran) { + // check the pmp granularity is set from dtb(!=0) and is power of 2 + if (gran < (1 << PMP_SHIFT) || (gran & (gran - 1)) != 0) { + fprintf(stderr, "error: bad pmp granularity '%ld' from the dtb\n", (unsigned long)gran); + abort(); + } + + lg_pmp_granularity = ctz(gran); +} + void processor_t::take_interrupt(reg_t pending_interrupts) { reg_t mie = get_field(state.mstatus, MSTATUS_MIE); @@ -612,22 +636,32 @@ void processor_t::set_csr(int which, reg_t val) reg_t delegable_ints = supervisor_ints | coprocessor_ints; reg_t all_ints = delegable_ints | MIP_MSIP | MIP_MTIP; - if (which >= CSR_PMPADDR0 && which < CSR_PMPADDR0 + state.n_pmp) { + if (which >= CSR_PMPADDR0 && which < CSR_PMPADDR0 + state.max_pmp) { + // If no PMPs are configured, disallow access to all. Otherwise, allow + // access to all, but unimplemented ones are hardwired to zero. + if (n_pmp == 0) + return; + size_t i = which - CSR_PMPADDR0; bool locked = state.pmpcfg[i] & PMP_L; - bool next_locked = i+1 < state.n_pmp && (state.pmpcfg[i+1] & PMP_L); - bool next_tor = i+1 < state.n_pmp && (state.pmpcfg[i+1] & PMP_A) == PMP_TOR; - if (!locked && !(next_locked && next_tor)) + bool next_locked = i+1 < state.max_pmp && (state.pmpcfg[i+1] & PMP_L); + bool next_tor = i+1 < state.max_pmp && (state.pmpcfg[i+1] & PMP_A) == PMP_TOR; + if (i < n_pmp && !locked && !(next_locked && next_tor)) state.pmpaddr[i] = val & ((reg_t(1) << (MAX_PADDR_BITS - PMP_SHIFT)) - 1); mmu->flush_tlb(); } - if (which >= CSR_PMPCFG0 && which < CSR_PMPCFG0 + state.n_pmp / 4) { + if (which >= CSR_PMPCFG0 && which < CSR_PMPCFG0 + state.max_pmp / 4) { + if (n_pmp == 0) + return; + for (size_t i0 = (which - CSR_PMPCFG0) * 4, i = i0; i < i0 + xlen / 8; i++) { - if (!(state.pmpcfg[i] & PMP_L)) { + if (i < n_pmp && !(state.pmpcfg[i] & PMP_L)) { uint8_t cfg = (val >> (8 * (i - i0))) & (PMP_R | PMP_W | PMP_X | PMP_A | PMP_L); cfg &= ~PMP_W | ((cfg & PMP_R) ? PMP_W : 0); // Disallow R=0 W=1 + if (lg_pmp_granularity != PMP_SHIFT && (cfg & PMP_A) == PMP_NA4) + cfg |= PMP_NAPOT; // Disallow A=NA4 when granularity > 4 state.pmpcfg[i] = cfg; } } @@ -890,14 +924,19 @@ reg_t processor_t::get_csr(int which) if (which >= CSR_MHPMEVENT3 && which <= CSR_MHPMEVENT31) return 0; - if (which >= CSR_PMPADDR0 && which < CSR_PMPADDR0 + state.n_pmp) - return state.pmpaddr[which - CSR_PMPADDR0]; + if (which >= CSR_PMPADDR0 && which < CSR_PMPADDR0 + state.max_pmp) { + reg_t i = which - CSR_PMPADDR0; + if ((state.pmpcfg[i] & PMP_A) >= PMP_NAPOT) + return state.pmpaddr[i] | (~pmp_tor_mask() >> 1); + else + return state.pmpaddr[i] & pmp_tor_mask(); + } - if (which >= CSR_PMPCFG0 && which < CSR_PMPCFG0 + state.n_pmp / 4) { + if (which >= CSR_PMPCFG0 && which < CSR_PMPCFG0 + state.max_pmp / 4) { require((which & ((xlen / 32) - 1)) == 0); reg_t res = 0; - for (size_t i0 = (which - CSR_PMPCFG0) * 4, i = i0; i < i0 + xlen / 8 && i < state.n_pmp; i++) + for (size_t i0 = (which - CSR_PMPCFG0) * 4, i = i0; i < i0 + xlen / 8 && i < state.max_pmp; i++) res |= reg_t(state.pmpcfg[i]) << (8 * (i - i0)); return res; } diff --git a/riscv/processor.h b/riscv/processor.h index f9ec1f1..7ff79ef 100644 --- a/riscv/processor.h +++ b/riscv/processor.h @@ -193,9 +193,9 @@ struct state_t reg_t tdata2[num_triggers]; bool debug_mode; - static const int n_pmp = 16; - uint8_t pmpcfg[n_pmp]; - reg_t pmpaddr[n_pmp]; + static const int max_pmp = 16; + uint8_t pmpcfg[max_pmp]; + reg_t pmpaddr[max_pmp]; uint32_t fflags; uint32_t frm; @@ -389,6 +389,9 @@ public: void trigger_updated(); + void set_pmp_num(reg_t pmp_num); + void set_pmp_granularity(reg_t pmp_granularity); + private: simif_t* sim; mmu_t* mmu; // main memory is always accessed via the mmu @@ -419,6 +422,8 @@ private: void disasm(insn_t insn); // disassemble and print an instruction int paddr_bits(); + reg_t pmp_tor_mask() { return -(reg_t(1) << (lg_pmp_granularity - PMP_SHIFT)); } + void enter_debug_mode(uint8_t cause); friend class mmu_t; @@ -434,6 +439,9 @@ private: // Track repeated executions for processor_t::disasm() uint64_t last_pc, last_bits, executions; + reg_t n_pmp; + reg_t lg_pmp_granularity; + public: class vectorUnit_t { public: diff --git a/riscv/riscv.mk.in b/riscv/riscv.mk.in index bbcba5e..fbe6786 100644 --- a/riscv/riscv.mk.in +++ b/riscv/riscv.mk.in @@ -2,6 +2,7 @@ get_insn_list = $(shell grep ^DECLARE_INSN $(1) | sed 's/DECLARE_INSN(\(.*\),.*, get_opcode = $(shell grep ^DECLARE_INSN.*\\\<$(2)\\\> $(1) | sed 's/DECLARE_INSN(.*,\(.*\),.*)/\1/') riscv_subproject_deps = \ + fdt \ softfloat \ riscv_install_prog_srcs = \ diff --git a/riscv/sim.cc b/riscv/sim.cc index 0b29720..b25d79e 100644 --- a/riscv/sim.cc +++ b/riscv/sim.cc @@ -5,6 +5,7 @@ #include "dts.h" #include "remote_bitbang.h" #include "byteorder.h" +#include <fstream> #include <map> #include <iostream> #include <sstream> @@ -33,14 +34,25 @@ sim_t::sim_t(const char* isa, const char* priv, const char* varch, const std::vector<std::string>& args, std::vector<int> const hartids, const debug_module_config_t &dm_config, - const char *log_path) - : htif_t(args), mems(mems), plugin_devices(plugin_devices), + const char *log_path, + bool dtb_enabled, const char *dtb_file) + : htif_t(args), + mems(mems), + plugin_devices(plugin_devices), procs(std::max(nprocs, size_t(1))), - initrd_start(initrd_start), initrd_end(initrd_end), start_pc(start_pc), + initrd_start(initrd_start), + initrd_end(initrd_end), + start_pc(start_pc), + dtb_file(dtb_file ? dtb_file : ""), + dtb_enabled(dtb_enabled), log_file(log_path), - current_step(0), current_proc(0), debug(false), histogram_enabled(false), - log(false), dtb_enabled(true), - remote_bitbang(NULL), debug_module(this, dm_config) + current_step(0), + current_proc(0), + debug(false), + histogram_enabled(false), + log(false), + remote_bitbang(NULL), + debug_module(this, dm_config) { signal(SIGINT, &handle_signal); @@ -68,8 +80,24 @@ sim_t::sim_t(const char* isa, const char* priv, const char* varch, log_file.get()); } + make_dtb(); + clint.reset(new clint_t(procs, CPU_HZ / INSNS_PER_RTC_TICK, real_time_clint)); - bus.add_device(CLINT_BASE, clint.get()); + reg_t clint_base; + if (fdt_parse_clint((void *)dtb.c_str(), &clint_base, "riscv,clint0")) { + bus.add_device(CLINT_BASE, clint.get()); + } else { + bus.add_device(clint_base, clint.get()); + } + + for (size_t i = 0; i < nprocs; i++) { + reg_t pmp_num = 0, pmp_granularity = 0; + fdt_parse_pmp_num((void *)dtb.c_str(), &pmp_num, "riscv"); + fdt_parse_pmp_alignment((void *)dtb.c_str(), &pmp_granularity, "riscv"); + + procs[i]->set_pmp_num(pmp_num); + procs[i]->set_pmp_granularity(pmp_granularity); + } } sim_t::~sim_t() @@ -190,6 +218,25 @@ bool sim_t::mmio_store(reg_t addr, size_t len, const uint8_t* bytes) void sim_t::make_dtb() { + if (!dtb_file.empty()) { + std::ifstream fin(dtb_file.c_str(), std::ios::binary); + if (!fin.good()) { + std::cerr << "can't find dtb file: " << dtb_file << std::endl; + exit(-1); + } + + std::stringstream strstream; + strstream << fin.rdbuf(); + + dtb = strstream.str(); + } else { + dts = make_dts(INSNS_PER_RTC_TICK, CPU_HZ, initrd_start, initrd_end, procs, mems); + dtb = dts_compile(dts); + } +} + +void sim_t::set_rom() +{ const int reset_vec_size = 8; start_pc = start_pc == reg_t(-1) ? get_entry_point() : start_pc; @@ -211,8 +258,22 @@ void sim_t::make_dtb() std::vector<char> rom((char*)reset_vec, (char*)reset_vec + sizeof(reset_vec)); - dts = make_dts(INSNS_PER_RTC_TICK, CPU_HZ, initrd_start, initrd_end, procs, mems); - std::string dtb = dts_compile(dts); + std::string dtb; + if (!dtb_file.empty()) { + std::ifstream fin(dtb_file.c_str(), std::ios::binary); + if (!fin.good()) { + std::cerr << "can't find dtb file: " << dtb_file << std::endl; + exit(-1); + } + + std::stringstream strstream; + strstream << fin.rdbuf(); + + dtb = strstream.str(); + } else { + dts = make_dts(INSNS_PER_RTC_TICK, CPU_HZ, initrd_start, initrd_end, procs, mems); + dtb = dts_compile(dts); + } rom.insert(rom.end(), dtb.begin(), dtb.end()); const int align = 0x1000; @@ -237,7 +298,7 @@ char* sim_t::addr_to_mem(reg_t addr) { void sim_t::reset() { if (dtb_enabled) - make_dtb(); + set_rom(); } void sim_t::idle() diff --git a/riscv/sim.h b/riscv/sim.h index 91aedab..eee24e5 100644 --- a/riscv/sim.h +++ b/riscv/sim.h @@ -29,7 +29,8 @@ public: reg_t start_pc, std::vector<std::pair<reg_t, mem_t*>> mems, std::vector<std::pair<reg_t, abstract_device_t*>> plugin_devices, const std::vector<std::string>& args, const std::vector<int> hartids, - const debug_module_config_t &dm_config, const char *log_path); + const debug_module_config_t &dm_config, const char *log_path, + bool dtb_enabled, const char *dtb_file); ~sim_t(); // run the simulation to completion @@ -46,9 +47,6 @@ public: void configure_log(bool enable_log, bool enable_commitlog); void set_procs_debug(bool value); - void set_dtb_enabled(bool value) { - this->dtb_enabled = value; - } void set_remote_bitbang(remote_bitbang_t* remote_bitbang) { this->remote_bitbang = remote_bitbang; } @@ -68,6 +66,9 @@ private: reg_t initrd_end; reg_t start_pc; std::string dts; + std::string dtb; + std::string dtb_file; + bool dtb_enabled; std::unique_ptr<rom_device_t> boot_rom; std::unique_ptr<clint_t> clint; bus_t bus; @@ -83,7 +84,6 @@ private: bool debug; bool histogram_enabled; // provide a histogram of PCs bool log; - bool dtb_enabled; remote_bitbang_t* remote_bitbang; // memory-mapped I/O routines @@ -91,6 +91,7 @@ private: bool mmio_load(reg_t addr, size_t len, uint8_t* bytes); bool mmio_store(reg_t addr, size_t len, const uint8_t* bytes); void make_dtb(); + void set_rom(); // presents a prompt for introspection into the simulation void interactive(); |