diff --git a/src/Makefile b/src/Makefile index 5f66b62d7..2e608a827 100644 --- a/src/Makefile +++ b/src/Makefile @@ -350,7 +350,6 @@ LIBCARTESI_OBJS:= \ htif-factory.o \ shadow-state.o \ shadow-state-factory.o \ - shadow-pmas.o \ shadow-pmas-factory.o \ shadow-tlb.o \ shadow-tlb-factory.o \ diff --git a/src/device-state-access.h b/src/device-state-access.h index fd810ab91..101cb0d9a 100644 --- a/src/device-state-access.h +++ b/src/device-state-access.h @@ -146,14 +146,6 @@ class device_state_access : public i_device_state_access { bool do_write_memory(uint64_t paddr, const unsigned char *data, uint64_t length) override { return m_a.write_memory(paddr, data, length); } - - uint64_t do_read_pma_istart(int p) override { - return m_a.read_pma_istart(p); - } - - uint64_t do_read_pma_ilength(int p) override { - return m_a.read_pma_ilength(p); - } }; } // namespace cartesi diff --git a/src/find-pma-entry.h b/src/find-pma-entry.h new file mode 100644 index 000000000..8022421c2 --- /dev/null +++ b/src/find-pma-entry.h @@ -0,0 +1,58 @@ +// Copyright Cartesi and individual authors (see AUTHORS) +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along +// with this program (see COPYING). If not, see . +// + +#ifndef FIND_PMA_ENTRY_H +#define FIND_PMA_ENTRY_H + +#include "compiler-defines.h" +#include + +namespace cartesi { + +/// \brief Returns PMAs entry where a word falls. +/// \tparam T uint8_t, uint16_t, uint32_t, or uint64_t. +/// \tparam STATE_ACCESS Class of machine state accessor object. +/// \param a Machine state accessor object. +/// \param paddr Target physical address of word. +/// \returns PMA entry where word falls, or empty sentinel. +template +auto &find_pma_entry(STATE_ACCESS &a, uint64_t paddr) { + uint64_t index = 0; + while (true) { + auto &pma = a.read_pma_entry(index); + const auto length = pma.get_length(); + // The pmas array always contain a sentinel. + // It is an entry with zero length. + // If we hit it, return it + if (unlikely(length == 0)) { + return pma; + } + // Otherwise, if we found an entry where the access fits, return it + // Note the "strange" order of arithmetic operations. + // This is to ensure there is no overflow. + // Since we know paddr >= start, there is no chance of overflow in the first subtraction. + // Since length is at least 4096 (an entire page), there is no chance of overflow in the second subtraction. + const auto start = pma.get_start(); + if (paddr >= start && paddr - start <= length - sizeof(T)) { + return pma; + } + ++index; + } +} + +} // namespace cartesi + +#endif // FIND_PMA_ENTRY_H diff --git a/src/htif-factory.cpp b/src/htif-factory.cpp index c48add943..151da7f09 100644 --- a/src/htif-factory.cpp +++ b/src/htif-factory.cpp @@ -32,7 +32,7 @@ static bool htif_peek(const pma_entry &pma, const machine & /*m*/, uint64_t page return (page_offset % PMA_PAGE_SIZE) == 0 && page_offset < pma.get_length(); } -pma_entry make_htif_pma_entry(uint64_t start, uint64_t length, htif_runtime_config *context) { +pma_entry make_htif_pma_entry(uint64_t start, uint64_t length) { const pma_entry::flags f{ true, // R true, // W @@ -41,7 +41,7 @@ pma_entry make_htif_pma_entry(uint64_t start, uint64_t length, htif_runtime_conf false, // IW PMA_ISTART_DID::HTIF // DID }; - return make_device_pma_entry("HTIF device", start, length, htif_peek, &htif_driver, context).set_flags(f); + return make_device_pma_entry("HTIF device", start, length, htif_peek, &htif_driver).set_flags(f); } } // namespace cartesi diff --git a/src/htif-factory.h b/src/htif-factory.h index 97dde77c8..9a6807682 100644 --- a/src/htif-factory.h +++ b/src/htif-factory.h @@ -25,7 +25,7 @@ namespace cartesi { /// \brief Creates a PMA entry for the HTIF device -pma_entry make_htif_pma_entry(uint64_t start, uint64_t length, htif_runtime_config *context); +pma_entry make_htif_pma_entry(uint64_t start, uint64_t length); } // namespace cartesi diff --git a/src/htif.cpp b/src/htif.cpp index 24358d6d8..d6490a930 100644 --- a/src/htif.cpp +++ b/src/htif.cpp @@ -19,7 +19,6 @@ #include "htif.h" #include "i-device-state-access.h" #include "interpret.h" -#include "machine-runtime-config.h" #include "os.h" #include "pma-driver.h" @@ -89,17 +88,12 @@ static execute_status htif_yield(i_device_state_access *a, uint64_t cmd, uint64_ return status; } -static execute_status htif_console(htif_runtime_config *runtime_config, i_device_state_access *a, uint64_t cmd, - uint64_t data) { +static execute_status htif_console(i_device_state_access *a, uint64_t cmd, uint64_t data) { // If console command is enabled, perform it and acknowledge if (cmd < 64 && (((a->read_htif_iconsole() >> cmd) & 1) != 0)) { if (cmd == HTIF_CONSOLE_CMD_PUTCHAR) { const uint8_t ch = data & 0xff; - // In microarchitecture runtime_config will always be nullptr, - // therefore the HTIF runtime config is actually ignored. - if ((runtime_config == nullptr) || !runtime_config->no_console_putchar) { - os_putchar(ch); - } + os_putchar(ch); a->write_htif_fromhost(HTIF_BUILD(HTIF_DEV_CONSOLE, cmd, 0, 0)); } else if (cmd == HTIF_CONSOLE_CMD_GETCHAR) { // In blockchain, this command will never be enabled as there is no way to input the same character @@ -115,8 +109,7 @@ static execute_status htif_console(htif_runtime_config *runtime_config, i_device return execute_status::success; } -static execute_status htif_write_tohost(htif_runtime_config *runtime_config, i_device_state_access *a, - uint64_t tohost) { +static execute_status htif_write_tohost(i_device_state_access *a, uint64_t tohost) { // Decode tohost const uint32_t device = HTIF_DEV_FIELD(tohost); const uint32_t cmd = HTIF_CMD_FIELD(tohost); @@ -128,7 +121,7 @@ static execute_status htif_write_tohost(htif_runtime_config *runtime_config, i_d case HTIF_DEV_HALT: return htif_halt(a, cmd, data); case HTIF_DEV_CONSOLE: - return htif_console(runtime_config, a, cmd, data); + return htif_console(a, cmd, data); case HTIF_DEV_YIELD: return htif_yield(a, cmd, data); //??D Unknown HTIF devices are silently ignored @@ -138,10 +131,8 @@ static execute_status htif_write_tohost(htif_runtime_config *runtime_config, i_d } /// \brief HTIF device write callback. See ::pma_write. -static execute_status htif_write(void *context, i_device_state_access *a, uint64_t offset, uint64_t val, +static execute_status htif_write(void * /*context*/, i_device_state_access *a, uint64_t offset, uint64_t val, int log2_size) { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - auto *runtime_config = reinterpret_cast(context); // Our HTIF only supports 64-bit writes if (log2_size != 3) { return execute_status::failure; @@ -150,7 +141,7 @@ static execute_status htif_write(void *context, i_device_state_access *a, uint64 // Only these 64-bit aligned offsets are valid switch (offset) { case htif_tohost_rel_addr: - return htif_write_tohost(runtime_config, a, val); + return htif_write_tohost(a, val); case htif_fromhost_rel_addr: a->write_htif_fromhost(val); return execute_status::success; diff --git a/src/i-device-state-access.h b/src/i-device-state-access.h index 184edda44..efdfaa06e 100644 --- a/src/i-device-state-access.h +++ b/src/i-device-state-access.h @@ -193,18 +193,6 @@ class i_device_state_access { return do_write_memory(paddr, data, length); } - /// \brief Reads the istart field of a PMA entry - /// \param p Index of PMA - uint64_t read_pma_istart(int p) { - return do_read_pma_istart(p); - } - - /// \brief Reads the ilength field of a PMA entry - /// \param p Index of PMA - uint64_t read_pma_ilength(int p) { - return do_read_pma_ilength(p); - } - private: virtual void do_set_mip(uint64_t mask) = 0; virtual void do_reset_mip(uint64_t mask) = 0; @@ -228,8 +216,6 @@ class i_device_state_access { virtual uint64_t do_read_htif_iyield() = 0; virtual bool do_read_memory(uint64_t paddr, unsigned char *data, uint64_t length) = 0; virtual bool do_write_memory(uint64_t paddr, const unsigned char *data, uint64_t length) = 0; - virtual uint64_t do_read_pma_istart(int p) = 0; - virtual uint64_t do_read_pma_ilength(int p) = 0; }; } // namespace cartesi diff --git a/src/i-state-access.h b/src/i-state-access.h index 6fbeb0de5..2d8345ff6 100644 --- a/src/i-state-access.h +++ b/src/i-state-access.h @@ -600,23 +600,10 @@ class i_state_access { // CRTP return derived().do_poll_external_interrupts(mcycle, mcycle_max); } - /// \brief Reads PMA at a given index. - /// \param pma PMA entry. - /// \param i Index of PMA index. - void read_pma(const PMA_ENTRY_TYPE &pma, int i) { - return derived().do_read_pma(pma, i); - } - - /// \brief Reads the istart field of a PMA entry - /// \param p Index of PMA - uint64_t read_pma_istart(int p) { - return derived().do_read_pma_istart(p); - } - - /// \brief Reads the ilength field of a PMA entry - /// \param p Index of PMA - uint64_t read_pma_ilength(int p) { - return derived().do_read_pma_ilength(p); + /// \brief Reads PMA entry at a given index. + /// \param index Index of PMA + PMA_ENTRY_TYPE &read_pma_entry(uint64_t index) { + return derived().do_read_pma_entry(index); } /// \brief Reads a chunk of data from a memory PMA range. @@ -665,32 +652,10 @@ class i_state_access { // CRTP return derived().template do_write_memory_word(paddr, hpage, hoffset, val); } - /// \brief Obtain PMA entry covering a physical memory word - /// \param paddr Target physical address. - /// \returns Corresponding entry if found, or a sentinel entry - /// for an empty range. - /// \tparam T Type of word. - template - PMA_ENTRY_TYPE &find_pma_entry(uint64_t paddr) { - return derived().template do_find_pma_entry(paddr); - } - auto get_host_memory(PMA_ENTRY_TYPE &pma) { return derived().do_get_host_memory(pma); } - PMA_ENTRY_TYPE &get_pma_entry(int index) { - return derived().do_get_pma_entry(index); - } - - auto read_device(PMA_ENTRY_TYPE &pma, uint64_t mcycle, uint64_t offset, uint64_t *pval, int log2_size) { - return derived().do_read_device(pma, mcycle, offset, pval, log2_size); - } - - auto write_device(PMA_ENTRY_TYPE &pma, uint64_t mcycle, uint64_t offset, uint64_t pval, int log2_size) { - return derived().do_write_device(pma, mcycle, offset, pval, log2_size); - } - /// \brief Try to translate a virtual address to a host pointer through the TLB. /// \tparam ETYPE TLB entry type. /// \tparam T Type of word that would be read with the pointer. diff --git a/src/i-uarch-state-access.h b/src/i-uarch-state-access.h index ef51c63d4..5905f0907 100644 --- a/src/i-uarch-state-access.h +++ b/src/i-uarch-state-access.h @@ -95,11 +95,6 @@ class i_uarch_state_access { // CRTP return derived().do_write_word(paddr, data); } - template - pma_entry &find_pma_entry(uint64_t paddr) { - return derived().template do_find_pma_entry(paddr); - } - /// \brief Resets uarch to pristine state void reset_state() { return derived().do_reset_state(); diff --git a/src/interpret.cpp b/src/interpret.cpp index 72d66d736..fc02d88ba 100644 --- a/src/interpret.cpp +++ b/src/interpret.cpp @@ -102,6 +102,7 @@ /// https://gcc.gnu.org/onlinedocs/gcc-7.3.0/gcc/Arrays-and-pointers-implementation.html#Arrays-and-pointers-implementation /// \} +#include "find-pma-entry.h" #include "interpret.h" #include "meta.h" #include "riscv-constants.h" @@ -837,7 +838,7 @@ static NO_INLINE std::pair read_virtual_memory_slow(STATE_ACCESS vaddr); return {false, pc}; } - auto &pma = a.template find_pma_entry(paddr); + auto &pma = find_pma_entry(a, paddr); if (likely(pma.get_istart_R())) { if (likely(pma.get_istart_M())) { unsigned char *hpage = a.template replace_tlb_entry(vaddr, paddr, pma); @@ -848,8 +849,11 @@ static NO_INLINE std::pair read_virtual_memory_slow(STATE_ACCESS if (likely(pma.get_istart_IO())) { const uint64_t offset = paddr - pma.get_start(); uint64_t val{}; + device_state_access da(a, mcycle); // If we do not know how to read, we treat this as a PMA violation - if (likely(a.read_device(pma, mcycle, offset, &val, log2_size::value))) { + const bool status = pma.get_device_noexcept().get_driver()->read(pma.get_device_noexcept().get_context(), + &da, offset, &val, log2_size::value); + if (likely(status)) { *pval = static_cast(val); // device logs its own state accesses return {true, pc}; @@ -913,7 +917,7 @@ static NO_INLINE std::pair write_virtual_memory_slow(S pc = raise_exception(a, pc, MCAUSE_STORE_AMO_PAGE_FAULT, vaddr); return {execute_status::failure, pc}; } - auto &pma = a.template find_pma_entry(paddr); + auto &pma = find_pma_entry(a, paddr); if (likely(pma.get_istart_W())) { if (likely(pma.get_istart_M())) { unsigned char *hpage = a.template replace_tlb_entry(vaddr, paddr, pma); @@ -923,8 +927,9 @@ static NO_INLINE std::pair write_virtual_memory_slow(S } if (likely(pma.get_istart_IO())) { const uint64_t offset = paddr - pma.get_start(); - auto status = - a.write_device(pma, mcycle, offset, static_cast(static_cast(val64)), log2_size::value); + device_state_access da(a, mcycle); + auto status = pma.get_device_noexcept().get_driver()->write(pma.get_device_noexcept().get_context(), &da, + offset, static_cast(static_cast(val64)), log2_size::value); // If we do not know how to write, we treat this as a PMA violation if (likely(status != execute_status::failure)) { return {status, pc}; @@ -5397,7 +5402,7 @@ static FORCE_INLINE fetch_status fetch_translate_pc_slow(STATE_ACCESS &a, uint64 return fetch_status::exception; } // Walk memory map to find the range that contains the physical address - auto &pma = a.template find_pma_entry(paddr); + auto &pma = find_pma_entry(a, paddr); // We only execute directly from RAM (as in "random access memory") // If the range is not memory or not executable, this as a PMA violation if (unlikely(!pma.get_istart_M() || !pma.get_istart_X())) { diff --git a/src/machine.cpp b/src/machine.cpp index 6c6eca5bb..9adc9f115 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -31,7 +31,6 @@ #include #include -#include #include "access-log.h" #include "bracket-note.h" @@ -49,9 +48,9 @@ #include "pma-constants.h" #include "pma-defines.h" #include "pma.h" -#include "record-state-access.h" +#include "record-send-cmio-state-access.h" #include "record-step-state-access.h" -#include "replay-state-access.h" +#include "replay-send-cmio-state-access.h" #include "replay-step-state-access.h" #include "riscv-constants.h" #include "send-cmio-response.h" @@ -86,7 +85,6 @@ namespace cartesi { using namespace std::string_literals; -using namespace boost::adaptors; const pma_entry::flags machine::m_ram_flags{ true, // R @@ -394,7 +392,7 @@ machine::machine(const machine_config &c, const machine_runtime_config &r) : m_c register_pma_entry(make_cmio_rx_buffer_pma_entry(m_c.cmio)); // Register HTIF device - register_pma_entry(make_htif_pma_entry(PMA_HTIF_START, PMA_HTIF_LENGTH, &m_r.htif)); + register_pma_entry(make_htif_pma_entry(PMA_HTIF_START, PMA_HTIF_LENGTH)); // Copy HTIF state to from config to machine write_reg(reg::htif_tohost, m_c.htif.tohost); @@ -426,8 +424,14 @@ machine::machine(const machine_config &c, const machine_runtime_config &r) : m_c // Register state shadow device register_pma_entry(make_shadow_state_pma_entry(PMA_SHADOW_STATE_START, PMA_SHADOW_STATE_LENGTH)); - // Register pma board shadow device - register_pma_entry(make_shadow_pmas_pma_entry(PMA_SHADOW_PMAS_START, PMA_SHADOW_PMAS_LENGTH)); + // Register memory range that holds shadow PMA state, keep pointer to populate later + shadow_pmas_state *shadow_pmas = nullptr; + { + auto shadow_pmas_pma_entry = make_shadow_pmas_pma_entry(PMA_SHADOW_PMAS_START, PMA_SHADOW_PMAS_LENGTH); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + shadow_pmas = reinterpret_cast(shadow_pmas_pma_entry.get_memory().get_host_memory()); + register_pma_entry(std::move(shadow_pmas_pma_entry)); + } // Initialize VirtIO devices if (!m_c.virtio.empty()) { @@ -489,21 +493,30 @@ machine::machine(const machine_config &c, const machine_runtime_config &r) : m_c dtb_init(m_c, dtb.get_memory().get_host_memory(), PMA_DTB_LENGTH); } - // Add sentinel to PMA vector + // Include machine PMAs in set considered by the Merkle tree. + for (auto &pma : m_s.pmas) { + m_merkle_pmas.push_back(&pma); + } + + // Last, add empty sentinels until we reach capacity (need at least one sentinel) register_pma_entry(make_empty_pma_entry("sentinel"s, 0, 0)); - // Initialize the vector of the pmas used by the merkle tree to compute hashes. - // First, add the pmas visible to the big machine, except the sentinel - for (auto &pma : m_s.pmas | sliced(0, m_s.pmas.size() - 1)) { - m_pmas.push_back(&pma); + // NOLINTNEXTLINE(readability-static-accessed-through-instance) + if (m_s.pmas.capacity() != PMA_MAX) { + throw std::logic_error{"PMAs array must be able to hold PMA_MAX entries"}; + } + while (m_s.pmas.size() < PMA_MAX) { + register_pma_entry(make_empty_pma_entry("sentinel"s, 0, 0)); } - // Second, push uarch pmas that are visible only to the microarchitecture interpreter - m_pmas.push_back(&m_uarch.get_state().shadow_state); - m_pmas.push_back(&m_uarch.get_state().ram); + // Populate shadow PMAs + populate_shadow_pmas_state(m_s.pmas, shadow_pmas); - // Last, add sentinel - m_pmas.push_back(&m_s.empty_pma); + // Include uarch PMAs in set considered by Merkle tree + m_merkle_pmas.push_back(&m_uarch.get_state().shadow_state); + m_merkle_pmas.push_back(&m_uarch.get_state().ram); + // Last, add sentinel PMA + m_merkle_pmas.push_back(&m_s.empty_pma); // Initialize TLB device // this must be done after all PMA entries are already registered, so we can lookup page addresses @@ -532,9 +545,10 @@ machine::machine(const machine_config &c, const machine_runtime_config &r) : m_c } os_open_tty(); } + os_silence_putchar(m_r.htif.no_console_putchar); // Initialize memory range descriptions returned by get_memory_ranges method - for (auto *pma : m_pmas) { + for (const auto *pma : m_merkle_pmas) { if (pma->get_length() != 0) { m_mrds.push_back(machine_memory_range_descr{pma->get_start(), pma->get_length(), pma->get_description()}); } @@ -622,11 +636,9 @@ const machine_runtime_config &machine::get_runtime_config() const { /// \brief Changes the machine runtime config. void machine::set_runtime_config(const machine_runtime_config &r) { - if (r.htif.no_console_putchar != m_r.htif.no_console_putchar) { - throw std::runtime_error{"cannot change htif runtime configuration"}; - } m_r = r; m_s.soft_yield = m_r.soft_yield; + os_silence_putchar(r.htif.no_console_putchar); } machine_config machine::get_serialization_config() const { @@ -1733,7 +1745,7 @@ bool machine::update_merkle_tree() const { mark_write_tlb_dirty_pages(); // Now go over all PMAs and updating the Merkle tree m_t.begin_update(); - for (const auto &pma : m_pmas) { + for (const auto &pma : m_merkle_pmas) { auto peek = pma->get_peek(); // Each PMA has a number of pages auto pages_in_range = (pma->get_length() + PMA_PAGE_SIZE - 1) / PMA_PAGE_SIZE; @@ -1805,7 +1817,7 @@ bool machine::update_merkle_tree_page(uint64_t address) { "PMA and machine_merkle_tree page sizes must match"); // Align address to beginning of page address &= ~(PMA_PAGE_SIZE - 1); - pma_entry &pma = find_pma_entry(m_pmas, address, sizeof(uint64_t)); + pma_entry &pma = find_pma_entry(m_merkle_pmas, address, sizeof(uint64_t)); const uint64_t page_start_in_range = address - pma.get_start(); machine_merkle_tree::hasher_type h; auto scratch = unique_calloc(PMA_PAGE_SIZE, std::nothrow_t{}); @@ -1885,7 +1897,7 @@ machine_merkle_tree::proof_type machine::get_proof(uint64_t address, int log2_si // or entirely outside it. if (log2_size < machine_merkle_tree::get_log2_page_size()) { const uint64_t length = UINT64_C(1) << log2_size; - const pma_entry &pma = find_pma_entry(m_pmas, address, length); + const pma_entry &pma = find_pma_entry(m_merkle_pmas, address, length); auto scratch = unique_calloc(PMA_PAGE_SIZE); const unsigned char *page_data = nullptr; // If the PMA range is empty, we know the desired range is @@ -1920,7 +1932,7 @@ void machine::read_memory(uint64_t address, unsigned char *data, uint64_t length if (data == nullptr) { throw std::invalid_argument{"invalid data buffer"}; } - const pma_entry &pma = find_pma_entry(m_pmas, address, length); + const pma_entry &pma = find_pma_entry(m_merkle_pmas, address, length); if (pma.get_istart_M()) { memcpy(data, pma.get_memory().get_host_memory() + (address - pma.get_start()), length); return; @@ -1972,7 +1984,7 @@ void machine::write_memory(uint64_t address, const unsigned char *data, uint64_t if (data == nullptr) { throw std::invalid_argument{"invalid data buffer"}; } - pma_entry &pma = find_pma_entry(m_pmas, address, length); + pma_entry &pma = find_pma_entry(m_merkle_pmas, address, length); if (!pma.get_istart_M() || pma.get_istart_E()) { throw std::invalid_argument{"address range not entirely in memory PMA"}; } @@ -1983,7 +1995,7 @@ void machine::fill_memory(uint64_t address, uint8_t data, uint64_t length) { if (length == 0) { return; } - pma_entry &pma = find_pma_entry(m_pmas, address, length); + pma_entry &pma = find_pma_entry(m_merkle_pmas, address, length); if (!pma.get_istart_M() || pma.get_istart_E()) { throw std::invalid_argument{"address range not entirely in memory PMA"}; } @@ -2099,7 +2111,7 @@ access_log machine::log_send_cmio_response(uint16_t reason, const unsigned char hash_type root_hash_before; get_root_hash(root_hash_before); // Call send_cmio_response with the recording state accessor - record_state_access a(*this, log_type); + record_send_cmio_state_access a(*this, log_type); a.push_bracket(bracket_type::begin, "send cmio response"); cartesi::send_cmio_response(a, reason, data, length); a.push_bracket(bracket_type::end, "send cmio response"); @@ -2119,7 +2131,7 @@ void machine::verify_send_cmio_response(uint16_t reason, const unsigned char *da } // Verify all intermediate state transitions - replay_state_access a(log, root_hash_before); + replay_send_cmio_state_access a(log, root_hash_before); cartesi::send_cmio_response(a, reason, data, length); a.finish(); diff --git a/src/machine.h b/src/machine.h index 2c500d33c..c7bb5f705 100644 --- a/src/machine.h +++ b/src/machine.h @@ -64,13 +64,13 @@ class machine final { // not constantly going through the extra indirection. We // should test this. - mutable machine_state m_s; ///< Opaque machine state - mutable machine_merkle_tree m_t; ///< Merkle tree of state - std::vector m_pmas; ///< List of all pmas used to compute the machine hash: big machine and uarch - machine_config m_c; ///< Copy of initialization config - uarch_machine m_uarch; ///< Microarchitecture machine - machine_runtime_config m_r; ///< Copy of initialization runtime config - machine_memory_range_descrs m_mrds; ///< List of memory ranges returned by get_memory_ranges(). + mutable machine_state m_s; ///< Opaque machine state + mutable machine_merkle_tree m_t; ///< Merkle tree of state + std::vector m_merkle_pmas; ///< PMAs considered by the Merkle tree: from big machine and uarch + machine_config m_c; ///< Copy of initialization config + uarch_machine m_uarch; ///< Microarchitecture machine + machine_runtime_config m_r; ///< Copy of initialization runtime config + machine_memory_range_descrs m_mrds; ///< List of memory ranges returned by get_memory_ranges(). boost::container::static_vector, VIRTIO_MAX> m_vdevs; ///< Array of VirtIO devices diff --git a/src/os.cpp b/src/os.cpp index e05316995..44d1ac527 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -120,6 +120,7 @@ using namespace std::string_literals; struct tty_state { bool initialized{false}; bool resize_pending{false}; + bool silence_putchar{false}; std::array buf{}; // Characters in console input buffer intptr_t buf_pos{}; intptr_t buf_len{}; @@ -135,12 +136,23 @@ struct tty_state { }; /// Returns pointer to the global TTY state -static tty_state *get_state() { +static tty_state *get_tty_state() { static tty_state data; return &data; } #endif // HAVE_TTY +/// \brief putchar global state +struct putchar_state { + bool silence; +}; + +/// Returns pointer to the global TTY state +static putchar_state *get_putchar_state() { + static putchar_state data; + return &data; +} + #ifdef HAVE_TERMIOS static int new_ttyfd(const char *path) { int fd{}; @@ -176,7 +188,7 @@ static int get_ttyfd() { #ifdef HAVE_SIGACTION /// \brief Signal raised whenever TTY size changes static void os_SIGWINCH_handler(int /*sig*/) { - auto *s = get_state(); + auto *s = get_tty_state(); if (!s->initialized) { return; } @@ -225,7 +237,7 @@ bool os_update_tty_size([[maybe_unused]] tty_state *s) { void os_open_tty() { #ifdef HAVE_TTY - auto *s = get_state(); + auto *s = get_tty_state(); if (s->initialized) { // Already initialized return; @@ -332,7 +344,7 @@ void os_open_tty() { void os_close_tty() { #ifdef HAVE_TTY #ifdef HAVE_TERMIOS - auto *s = get_state(); + auto *s = get_tty_state(); if (s->ttyfd >= 0) { // Restore old mode tcsetattr(s->ttyfd, TCSANOW, &s->oldtty); close(s->ttyfd); @@ -340,7 +352,7 @@ void os_close_tty() { } #elif defined(_WIN32) - auto *s = get_state(); + auto *s = get_tty_state(); if (s->hStdin) { SetConsoleMode(s->hStdin, s->dwOldStdinMode); s->hStdin = NULL; @@ -351,7 +363,7 @@ void os_close_tty() { } void os_get_tty_size(uint16_t *pwidth, uint16_t *pheight) { - auto *s = get_state(); + auto *s = get_tty_state(); if (!s->initialized) { // fallback values *pwidth = TTY_DEFAULT_COLS; @@ -370,7 +382,7 @@ void os_get_tty_size(uint16_t *pwidth, uint16_t *pheight) { void os_prepare_tty_select([[maybe_unused]] select_fd_sets *fds) { #ifdef HAVE_TTY - auto *s = get_state(); + auto *s = get_tty_state(); // Ignore if TTY is not initialized or stdin was closed if (!s->initialized) { return; @@ -387,7 +399,7 @@ void os_prepare_tty_select([[maybe_unused]] select_fd_sets *fds) { } bool os_poll_selected_tty([[maybe_unused]] int select_ret, [[maybe_unused]] select_fd_sets *fds) { - auto *s = get_state(); + auto *s = get_tty_state(); if (!s->initialized) { // We can't poll when TTY is not initialized return false; } @@ -444,7 +456,7 @@ bool os_poll_selected_tty([[maybe_unused]] int select_ret, [[maybe_unused]] sele bool os_poll_tty(uint64_t timeout_us) { #ifdef _WIN32 - auto *s = get_state(); + auto *s = get_tty_state(); if (!s->initialized) { // We can't poll when TTY is not initialized return false; } @@ -468,7 +480,7 @@ bool os_poll_tty(uint64_t timeout_us) { int os_getchar() { #ifdef HAVE_TTY - auto *s = get_state(); + auto *s = get_tty_state(); if (!s->initialized) { return -1; } @@ -501,9 +513,18 @@ static void fputc_with_line_buffering(uint8_t ch) { } } +void os_silence_putchar(bool yes) { + auto *ps = get_putchar_state(); + ps->silence = yes; +} + void os_putchar(uint8_t ch) { + auto *ps = get_putchar_state(); + if (ps->silence) { + return; + } #ifdef HAVE_TTY - auto *s = get_state(); + auto *s = get_tty_state(); if (!s->initialized) { // Write through fputc(), so we can take advantage of buffering. fputc_with_line_buffering(ch); @@ -520,6 +541,10 @@ void os_putchar(uint8_t ch) { } void os_putchars(const uint8_t *data, size_t len) { + auto *ps = get_putchar_state(); + if (ps->silence) { + return; + } for (size_t i = 0; i < len; ++i) { os_putchar(data[i]); } diff --git a/src/os.h b/src/os.h index ffb41ea21..dbca15543 100644 --- a/src/os.h +++ b/src/os.h @@ -86,6 +86,10 @@ void os_putchar(uint8_t ch); /// \param len Length of buffer. void os_putchars(const uint8_t *data, size_t len); +/// \brief Silences putchar (and putchars) output +/// \param yes If true, putchar is silenced +void os_silence_putchar(bool yes); + /// \brief Creates a new directory int os_mkdir(const char *path, int mode); diff --git a/src/record-state-access.h b/src/record-send-cmio-state-access.h similarity index 93% rename from src/record-state-access.h rename to src/record-send-cmio-state-access.h index 7df3b90b2..aee38f5ad 100644 --- a/src/record-state-access.h +++ b/src/record-send-cmio-state-access.h @@ -14,8 +14,8 @@ // with this program (see COPYING). If not, see . // -#ifndef RECORD_STATE_ACCESS_H -#define RECORD_STATE_ACCESS_H +#ifndef RECORD_SEND_CMIO_STATE_ACCESS_H +#define RECORD_SEND_CMIO_STATE_ACCESS_H /// \file /// \brief State access implementation that records state accesses to an access log. @@ -38,10 +38,10 @@ namespace cartesi { -/// \class record_state_access -/// \details This .... -/// access to the machine state. No logs are kept. -class record_state_access : public i_state_access { +/// \class record_send_cmio_state_access +/// \details This records all state accesses that happen during the execution of +/// a machine::send_cmio_response() function call +class record_send_cmio_state_access : public i_state_access { using hasher_type = machine_merkle_tree::hasher_type; using hash_type = machine_merkle_tree::hash_type; // NOLINTBEGIN(cppcoreguidelines-avoid-const-or-ref-data-members) @@ -58,7 +58,7 @@ class record_state_access : public i_state_access(log_type)) { @@ -66,15 +66,15 @@ class record_state_access : public i_state_access get_log() const { @@ -193,7 +193,7 @@ class record_state_access : public i_state_access; + friend i_state_access; void do_push_bracket(bracket_type &type, const char *text) { m_log->push_bracket(type, text); diff --git a/src/record-step-state-access.h b/src/record-step-state-access.h index cdfe85864..eb70b926f 100644 --- a/src/record-step-state-access.h +++ b/src/record-step-state-access.h @@ -588,28 +588,6 @@ class record_step_state_access : public i_state_access= 0 && i < (int) PMA_MAX); - touch_page(shadow_pmas_get_pma_abs_addr(i)); - const auto &pmas = m_m.get_pmas(); - uint64_t istart = 0; - if (i >= 0 && i < static_cast(pmas.size())) { - istart = pmas[i].get_istart(); - } - return istart; - } - - uint64_t do_read_pma_ilength(int i) const { - assert(i >= 0 && i < (int) PMA_MAX); - touch_page(shadow_pmas_get_pma_abs_addr(i)); - const auto &pmas = m_m.get_pmas(); - uint64_t ilength = 0; - if (i >= 0 && i < static_cast(pmas.size())) { - ilength = pmas[i].get_ilength(); - } - return ilength; - } - template void do_read_memory_word(uint64_t paddr, const unsigned char *hpage, uint64_t hoffset, T *pval) const { (void) paddr; @@ -641,56 +619,15 @@ class record_step_state_access : public i_state_access - pma_entry &do_find_pma_entry(uint64_t paddr) { - int i = 0; - while (true) { - touch_page(shadow_pmas_get_pma_abs_addr(i)); - auto &pma = m_m.get_state().pmas[i]; - // The pmas array always contain a sentinel. It is an entry with - // zero length. If we hit it, return it - if (pma.get_length() == 0) { - return pma; - } - // Otherwise, if we found an entry where the access fits, return it - // Note the "strange" order of arithmetic operations. - // This is to ensure there is no overflow. - // Since we know paddr >= start, there is no chance of overflow - // in the first subtraction. - // Since length is at least 4096 (an entire page), there is no - // chance of overflow in the second subtraction. - if (paddr >= pma.get_start() && paddr - pma.get_start() <= pma.get_length() - sizeof(T)) { - return pma; - } - i++; - } - } - static unsigned char *do_get_host_memory(pma_entry &pma) { return pma.get_memory_noexcept().get_host_memory(); } - pma_entry &do_get_pma_entry(int index) { - auto &pmas = m_m.get_state().pmas; - const auto last_pma_index = static_cast(pmas.size()) - 1; - if (index >= last_pma_index) { - touch_page(shadow_pmas_get_pma_abs_addr(last_pma_index)); - return pmas[last_pma_index]; - } + pma_entry &do_read_pma_entry(uint64_t index) { + assert(index < PMA_MAX); touch_page(shadow_pmas_get_pma_abs_addr(index)); - return pmas[index]; - } - - bool do_read_device(pma_entry &pma, uint64_t mcycle, uint64_t offset, uint64_t *pval, int log2_size) { - device_state_access da(*this, mcycle); - return pma.get_device_noexcept().get_driver()->read(pma.get_device_noexcept().get_context(), &da, offset, pval, - log2_size); - } - - execute_status do_write_device(pma_entry &pma, uint64_t mcycle, uint64_t offset, uint64_t val, int log2_size) { - device_state_access da(*this, mcycle); - return pma.get_device_noexcept().get_driver()->write(pma.get_device_noexcept().get_context(), &da, offset, val, - log2_size); + // NOLINTNEXTLINE(bugprone-narrowing-conversions) + return m_m.get_state().pmas[static_cast(index)]; } template @@ -756,7 +693,7 @@ class record_step_state_access : public i_state_access(tlbce.pma_index)); + pma_entry &pma = do_read_pma_entry(tlbce.pma_index); pma.mark_dirty_page(tlbce.paddr_page - pma.get_start()); } } @@ -782,7 +719,7 @@ class record_step_state_access : public i_state_access(tlbce.pma_index)); + pma_entry &pma = do_read_pma_entry(tlbce.pma_index); pma.mark_dirty_page(tlbce.paddr_page - pma.get_start()); } else { tlbhe.vaddr_page = TLB_INVALID_PAGE; diff --git a/src/replay-state-access.h b/src/replay-send-cmio-state-access.h similarity index 95% rename from src/replay-state-access.h rename to src/replay-send-cmio-state-access.h index 43c28cb35..56cc135be 100644 --- a/src/replay-state-access.h +++ b/src/replay-send-cmio-state-access.h @@ -14,8 +14,8 @@ // with this program (see COPYING). If not, see . // -#ifndef REPLAY_STATE_ACCESS_H -#define REPLAY_STATE_ACCESS_H +#ifndef REPLAY_SEND_CMIO_STATE_ACCESS_H +#define REPLAY_SEND_CMIO_STATE_ACCESS_H /// \file /// \brief State access implementation that replays recorded state accesses @@ -41,8 +41,8 @@ namespace cartesi { -/// \brief Allows replaying a uarch reset operation from an access log. -class replay_state_access : public i_state_access { +/// \brief Allows replaying a machine::send_cmio_response() from an access log. +class replay_send_cmio_state_access : public i_state_access { using tree_type = machine_merkle_tree; using hash_type = tree_type::hash_type; using hasher_type = tree_type::hasher_type; @@ -61,7 +61,7 @@ class replay_state_access : public i_state_access; + friend i_state_access; std::string access_to_report() const { auto index = m_next_access + 1; diff --git a/src/replay-step-state-access.h b/src/replay-step-state-access.h index 786414951..39637095a 100644 --- a/src/replay-step-state-access.h +++ b/src/replay-step-state-access.h @@ -42,6 +42,8 @@ namespace cartesi { // \file this code is designed to be compiled for a free-standing environment. // Environment-specific functions have the prefix "interop_" and are declared in "replay-step-state-access-interop.h" +//??D can we merge this calss with uarch_pma_entry in uarch/uarch-machine-state-access.h ? +//??D maybe implement base class in pma.h and subclass here if there is some minor difference? class mock_pma_entry final { public: struct flags { @@ -57,29 +59,57 @@ class mock_pma_entry final { }; private: - int m_pma_index; + uint64_t m_pma_index; uint64_t m_start; uint64_t m_length; flags m_flags; - const pma_driver *m_device_driver; - void *m_device_context; + const pma_driver *m_driver{nullptr}; + + static constexpr flags split_flags(uint64_t istart) { + flags f{}; + f.M = ((istart & PMA_ISTART_M_MASK) >> PMA_ISTART_M_SHIFT) != 0; + f.IO = ((istart & PMA_ISTART_IO_MASK) >> PMA_ISTART_IO_SHIFT) != 0; + f.E = ((istart & PMA_ISTART_E_MASK) >> PMA_ISTART_E_SHIFT) != 0; + f.R = ((istart & PMA_ISTART_R_MASK) >> PMA_ISTART_R_SHIFT) != 0; + f.W = ((istart & PMA_ISTART_W_MASK) >> PMA_ISTART_W_SHIFT) != 0; + f.X = ((istart & PMA_ISTART_X_MASK) >> PMA_ISTART_X_SHIFT) != 0; + f.IR = ((istart & PMA_ISTART_IR_MASK) >> PMA_ISTART_IR_SHIFT) != 0; + f.IW = ((istart & PMA_ISTART_IW_MASK) >> PMA_ISTART_IW_SHIFT) != 0; + f.DID = static_cast((istart & PMA_ISTART_DID_MASK) >> PMA_ISTART_DID_SHIFT); + return f; + } public: - mock_pma_entry(int pma_index, uint64_t start, uint64_t length, flags flags, const pma_driver *pma_driver = nullptr, - void *device_context = nullptr) : + mock_pma_entry(uint64_t pma_index, uint64_t istart, uint64_t ilength) : m_pma_index{pma_index}, - m_start{start}, - m_length{length}, - m_flags{flags}, - m_device_driver{pma_driver}, - m_device_context{device_context} {} - - mock_pma_entry() : - mock_pma_entry(-1, 0, 0, {false, false, true, false, false, false, false, false, PMA_ISTART_DID{}}) { - ; + m_start{istart & PMA_ISTART_START_MASK}, + m_length{ilength}, + m_flags{split_flags(istart)} { + if (m_flags.IO) { + switch (m_flags.DID) { + case PMA_ISTART_DID::shadow_state: + m_driver = &shadow_state_driver; + break; + case PMA_ISTART_DID::shadow_TLB: + m_driver = &shadow_tlb_driver; + break; + case PMA_ISTART_DID::CLINT: + m_driver = &clint_driver; + break; + case PMA_ISTART_DID::PLIC: + m_driver = &plic_driver; + break; + case PMA_ISTART_DID::HTIF: + m_driver = &htif_driver; + break; + default: + interop_throw_runtime_error("unsupported device in build_mock_pma_entry"); + break; + } + } } - int get_index() const { + uint64_t get_index() const { return m_pma_index; } @@ -123,12 +153,16 @@ class mock_pma_entry final { return m_flags.IR; } - const pma_driver *get_device_driver() { - return m_device_driver; + const auto *get_driver() const { + return m_driver; } - void *get_device_context() { - return m_device_context; + const auto &get_device_noexcept() const { + return *this; + } + + static void *get_context() { + return nullptr; } // NOLINTNEXTLINE(readability-convert-member-functions-to-static) @@ -820,11 +854,11 @@ class replay_step_state_access : public i_state_access(shadow_pmas_get_pma_abs_addr(i)); } - uint64_t do_read_pma_ilength(int i) { + uint64_t read_pma_ilength(uint64_t i) { return raw_read_memory(shadow_pmas_get_pma_abs_addr(i) + sizeof(uint64_t)); } @@ -858,28 +892,17 @@ class replay_step_state_access : public i_state_access - mock_pma_entry &do_find_pma_entry(uint64_t paddr) { - for (size_t i = 0; i < m_pmas.size(); i++) { - auto &pma = get_pma_entry(static_cast(i)); - if (pma.get_istart_E()) { - return pma; - } - if (paddr >= pma.get_start() && paddr - pma.get_start() <= pma.get_length() - sizeof(T)) { - return pma; - } - } - interop_throw_runtime_error("do_find_pma_entry failed to find address"); - } - - mock_pma_entry &do_get_pma_entry(int index) { + mock_pma_entry &do_read_pma_entry(uint64_t index) { + assert(index < PMA_MAX); const uint64_t istart = read_pma_istart(index); const uint64_t ilength = read_pma_ilength(index); - if (!m_pmas[index]) { - m_pmas[index] = build_mock_pma_entry(index, istart, ilength); + // NOLINTNEXTLINE(bugprone-narrowing-conversions) + const int i = static_cast(index); + if (!m_pmas[i]) { + m_pmas[i] = mock_pma_entry(index, istart, ilength); } // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - return m_pmas[index].value(); + return m_pmas[i].value(); } unsigned char *do_get_host_memory(mock_pma_entry &pma) { // NOLINT(readability-convert-member-functions-to-static) @@ -887,64 +910,6 @@ class replay_step_state_access : public i_state_accessread(pma.get_device_context(), &da, offset, pval, log2_size); - } - - execute_status do_write_device(mock_pma_entry &pma, uint64_t mcycle, uint64_t offset, uint64_t val, int log2_size) { - device_state_access da(*this, mcycle); - return pma.get_device_driver()->write(pma.get_device_context(), &da, offset, val, log2_size); - } - - // NOLINTNEXTLINE(readability-convert-member-functions-to-static) - mock_pma_entry build_mock_pma_entry(int index, uint64_t istart, uint64_t ilength) { - uint64_t start{}; - mock_pma_entry::flags flags{}; - split_istart(istart, start, flags); - const pma_driver *driver = nullptr; - void *device_ctx = nullptr; - if (flags.IO) { - switch (flags.DID) { - case PMA_ISTART_DID::shadow_state: - driver = &shadow_state_driver; - break; - case PMA_ISTART_DID::shadow_pmas: - driver = &shadow_pmas_driver; - break; - case PMA_ISTART_DID::shadow_TLB: - driver = &shadow_tlb_driver; - break; - case PMA_ISTART_DID::CLINT: - driver = &clint_driver; - break; - case PMA_ISTART_DID::PLIC: - driver = &plic_driver; - break; - case PMA_ISTART_DID::HTIF: - driver = &htif_driver; - break; - default: - interop_throw_runtime_error("Unsupported device in build_mock_pma_entry"); - break; - } - } - return mock_pma_entry{index, start, ilength, flags, driver, device_ctx}; - } - - static constexpr void split_istart(uint64_t istart, uint64_t &start, mock_pma_entry::flags &f) { - f.M = ((istart & PMA_ISTART_M_MASK) >> PMA_ISTART_M_SHIFT) != 0; - f.IO = ((istart & PMA_ISTART_IO_MASK) >> PMA_ISTART_IO_SHIFT) != 0; - f.E = ((istart & PMA_ISTART_E_MASK) >> PMA_ISTART_E_SHIFT) != 0; - f.R = ((istart & PMA_ISTART_R_MASK) >> PMA_ISTART_R_SHIFT) != 0; - f.W = ((istart & PMA_ISTART_W_MASK) >> PMA_ISTART_W_SHIFT) != 0; - f.X = ((istart & PMA_ISTART_X_MASK) >> PMA_ISTART_X_SHIFT) != 0; - f.IR = ((istart & PMA_ISTART_IR_MASK) >> PMA_ISTART_IR_SHIFT) != 0; - f.IW = ((istart & PMA_ISTART_IW_MASK) >> PMA_ISTART_IW_SHIFT) != 0; - f.DID = static_cast((istart & PMA_ISTART_DID_MASK) >> PMA_ISTART_DID_SHIFT); - start = istart & PMA_ISTART_START_MASK; - } - template volatile tlb_hot_entry &do_get_tlb_hot_entry(uint64_t eidx) { auto addr = tlb_get_entry_hot_abs_addr(eidx); @@ -1007,7 +972,7 @@ class replay_step_state_access : public i_state_access(eidx); if constexpr (ETYPE == TLB_WRITE) { if (tlbhe.vaddr_page != TLB_INVALID_PAGE) { - mock_pma_entry &pma = do_get_pma_entry(static_cast(tlbce.pma_index)); + mock_pma_entry &pma = do_read_pma_entry(tlbce.pma_index); pma.mark_dirty_page(tlbce.paddr_page - pma.get_start()); } } @@ -1020,7 +985,7 @@ class replay_step_state_access : public i_state_access(hpage) - vaddr_page; tlbce.paddr_page = paddr_page; - tlbce.pma_index = static_cast(pma.get_index()); + tlbce.pma_index = pma.get_index(); return hpage; } @@ -1032,7 +997,7 @@ class replay_step_state_access : public i_state_access(eidx); - mock_pma_entry &pma = do_get_pma_entry(static_cast(tlbce.pma_index)); + mock_pma_entry &pma = do_read_pma_entry(tlbce.pma_index); pma.mark_dirty_page(tlbce.paddr_page - pma.get_start()); } else { tlbhe.vaddr_page = TLB_INVALID_PAGE; diff --git a/src/send-cmio-response.cpp b/src/send-cmio-response.cpp index 48567e92d..121c48e08 100644 --- a/src/send-cmio-response.cpp +++ b/src/send-cmio-response.cpp @@ -19,8 +19,8 @@ // NOLINTBEGIN(google-readability-casting,misc-const-correctness,modernize-use-auto,hicpp-use-auto) -#include "record-state-access.h" -#include "replay-state-access.h" +#include "record-send-cmio-state-access.h" +#include "replay-send-cmio-state-access.h" #include "state-access.h" #include "send-cmio-response.h" @@ -64,10 +64,12 @@ void send_cmio_response(STATE_ACCESS &a, uint16 reason, bytes data, uint32 dataL template void send_cmio_response(state_access &a, uint16_t reason, const unsigned char *data, uint32 length); // Explicit instantiation for record_state_access -template void send_cmio_response(record_state_access &a, uint16_t reason, const unsigned char *data, uint32 length); +template void send_cmio_response(record_send_cmio_state_access &a, uint16_t reason, const unsigned char *data, + uint32 length); // Explicit instantiation for replay_state_access -template void send_cmio_response(replay_state_access &a, uint16_t reason, const unsigned char *data, uint32 length); +template void send_cmio_response(replay_send_cmio_state_access &a, uint16_t reason, const unsigned char *data, + uint32 length); } // namespace cartesi // NOLINTEND(google-readability-casting,misc-const-correctness,modernize-use-auto,hicpp-use-auto) diff --git a/src/shadow-pmas-factory.cpp b/src/shadow-pmas-factory.cpp index 116b5be86..51b146afb 100644 --- a/src/shadow-pmas-factory.cpp +++ b/src/shadow-pmas-factory.cpp @@ -26,32 +26,6 @@ namespace cartesi { -static bool shadow_pmas_peek(const pma_entry & /*pma*/, const machine &m, uint64_t page_offset, - const unsigned char **page_data, unsigned char *scratch) { - static_assert(sizeof(shadow_pmas) <= PMA_PAGE_SIZE); - - // There is only one page: 0 - if (page_offset != 0) { - *page_data = nullptr; - return false; - } - - // Clear page - memset(scratch, 0, PMA_PAGE_SIZE); - - // The page will reflect the pma_board structure - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - auto *b = reinterpret_cast(scratch); - int i = 0; - for (const auto &pma : m.get_pmas()) { - b->pmas[i].start = pma.get_istart(); - b->pmas[i].length = pma.get_ilength(); - ++i; - } - *page_data = scratch; - return true; -} - pma_entry make_shadow_pmas_pma_entry(uint64_t start, uint64_t length) { const pma_entry::flags f{ true, // R @@ -61,8 +35,7 @@ pma_entry make_shadow_pmas_pma_entry(uint64_t start, uint64_t length) { false, // IW PMA_ISTART_DID::shadow_pmas // DID }; - return make_device_pma_entry("shadow PMAs device", start, length, shadow_pmas_peek, &shadow_pmas_driver) - .set_flags(f); + return make_callocd_memory_pma_entry("shadow PMAs", start, length).set_flags(f); } } // namespace cartesi diff --git a/src/shadow-pmas-factory.h b/src/shadow-pmas-factory.h index 8ec2b0d9d..35514872f 100644 --- a/src/shadow-pmas-factory.h +++ b/src/shadow-pmas-factory.h @@ -23,11 +23,23 @@ /// \brief Shadow device. #include "pma.h" +#include "shadow-pmas.h" namespace cartesi { pma_entry make_shadow_pmas_pma_entry(uint64_t start, uint64_t length); +template +void populate_shadow_pmas_state(const PMAS &pmas, shadow_pmas_state *shadow) { + static_assert(PMA_SHADOW_PMAS_LENGTH >= sizeof(shadow_pmas_state), "shadow PMAs length is too small"); + unsigned index = 0; + for (const auto &pma : pmas) { + shadow->pmas[index].istart = pma.get_istart(); + shadow->pmas[index].ilength = pma.get_ilength(); + ++index; + } +} + } // namespace cartesi #endif diff --git a/src/shadow-pmas.cpp b/src/shadow-pmas.cpp deleted file mode 100644 index f35fcab3f..000000000 --- a/src/shadow-pmas.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright Cartesi and individual authors (see AUTHORS) -// SPDX-License-Identifier: LGPL-3.0-or-later -// -// This program is free software: you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License as published by the Free -// Software Foundation, either version 3 of the License, or (at your option) any -// later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT ANY -// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A -// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License along -// with this program (see COPYING). If not, see . -// - -#include "shadow-pmas.h" -#include "i-device-state-access.h" -#include "pma-constants.h" -#include "pma-driver.h" - -#include -#include - -namespace cartesi { - -/// \brief Shadow device read callback. See ::pma_read. -static bool shadow_pmas_read(void * /*context*/, i_device_state_access *a, uint64_t offset, uint64_t *pval, - int log2_size) { - static_assert(offsetof(shadow_pmas, pmas) == 0, "Unexpected offswet of PMA board"); - static_assert(sizeof(shadow_pma_entry) == 2 * sizeof(uint64_t), "Unexpected size of shadow PMA entry"); - static_assert(sizeof(shadow_pmas) == PMA_MAX * sizeof(shadow_pma_entry), "Unexpected size of Shadow PMA array "); - - // Our shadow only supports aligned 64-bit reads - if (((offset & 7) != 0) || log2_size != 3) { - return false; - } - if (offset < sizeof(shadow_pmas)) { - offset >>= 3; - const int p = static_cast(offset >> 1); - if ((offset & 1) != 0) { - *pval = a->read_pma_ilength(p); - } else { - *pval = a->read_pma_istart(p); - } - return true; - } - return false; -} - -const pma_driver shadow_pmas_driver = {"SHADOW PMAS", shadow_pmas_read, device_write_error}; - -} // namespace cartesi diff --git a/src/shadow-pmas.h b/src/shadow-pmas.h index a0c79e4a0..1aa7656da 100644 --- a/src/shadow-pmas.h +++ b/src/shadow-pmas.h @@ -32,27 +32,24 @@ namespace cartesi { /// \brief Shadow memory layout struct PACKED shadow_pma_entry { - uint64_t start; - uint64_t length; + uint64_t istart; + uint64_t ilength; }; -struct PACKED shadow_pmas { +struct PACKED shadow_pmas_state { shadow_pma_entry pmas[PMA_MAX]; }; -/// \brief Global instance of the pma board shadow device driver. -extern const pma_driver shadow_pmas_driver; - /// \brief Obtains the relative address of a PMA entry in shadow memory. /// \param p Index of desired shadow PMA entry, in 0..31. /// \returns The address. -static inline uint64_t shadow_pmas_get_pma_rel_addr(int p) { - assert(p >= 0 && p < (int) PMA_MAX); +static inline uint64_t shadow_pmas_get_pma_rel_addr(uint64_t p) { + assert(p < (int) PMA_MAX); return p * sizeof(shadow_pma_entry); } /// \brief Obtains the absolute address of a PMA entry in shadow memory. -static inline uint64_t shadow_pmas_get_pma_abs_addr(int p) { +static inline uint64_t shadow_pmas_get_pma_abs_addr(uint64_t p) { return PMA_SHADOW_PMAS_START + shadow_pmas_get_pma_rel_addr(p); } diff --git a/src/state-access.h b/src/state-access.h index c7fc808ce..fd2a5cd60 100644 --- a/src/state-access.h +++ b/src/state-access.h @@ -458,26 +458,6 @@ class state_access : public i_state_access { return {mcycle, interrupt_raised}; } - uint64_t do_read_pma_istart(int i) const { - assert(i >= 0 && i < (int) PMA_MAX); - const auto &pmas = m_m.get_pmas(); - uint64_t istart = 0; - if (i >= 0 && i < static_cast(pmas.size())) { - istart = pmas[i].get_istart(); - } - return istart; - } - - uint64_t do_read_pma_ilength(int i) const { - assert(i >= 0 && i < (int) PMA_MAX); - const auto &pmas = m_m.get_pmas(); - uint64_t ilength = 0; - if (i >= 0 && i < static_cast(pmas.size())) { - ilength = pmas[i].get_ilength(); - } - return ilength; - } - template void do_read_memory_word(uint64_t /*paddr*/, const unsigned char *hpage, uint64_t hoffset, T *pval) const { *pval = aliased_aligned_read(hpage + hoffset); @@ -510,52 +490,14 @@ class state_access : public i_state_access { } } - template - pma_entry &do_find_pma_entry(uint64_t paddr) { - int i = 0; - while (true) { - auto &pma = m_m.get_state().pmas[i]; - // The pmas array always contain a sentinel. It is an entry with - // zero length. If we hit it, return it - if (pma.get_length() == 0) { - return pma; - } - // Otherwise, if we found an entry where the access fits, return it - // Note the "strange" order of arithmetic operations. - // This is to ensure there is no overflow. - // Since we know paddr >= start, there is no chance of overflow - // in the first subtraction. - // Since length is at least 4096 (an entire page), there is no - // chance of overflow in the second subtraction. - if (paddr >= pma.get_start() && paddr - pma.get_start() <= pma.get_length() - sizeof(T)) { - return pma; - } - i++; - } - } - static unsigned char *do_get_host_memory(pma_entry &pma) { return pma.get_memory_noexcept().get_host_memory(); } - pma_entry &do_get_pma_entry(int index) { - auto &pmas = m_m.get_state().pmas; - if (index >= static_cast(pmas.size())) { - return pmas[pmas.size() - 1]; - } - return pmas[index]; - } - - bool do_read_device(pma_entry &pma, uint64_t mcycle, uint64_t offset, uint64_t *pval, int log2_size) { - device_state_access da(*this, mcycle); - return pma.get_device_noexcept().get_driver()->read(pma.get_device_noexcept().get_context(), &da, offset, pval, - log2_size); - } - - execute_status do_write_device(pma_entry &pma, uint64_t mcycle, uint64_t offset, uint64_t val, int log2_size) { - device_state_access da(*this, mcycle); - return pma.get_device_noexcept().get_driver()->write(pma.get_device_noexcept().get_context(), &da, offset, val, - log2_size); + pma_entry &do_read_pma_entry(uint64_t index) { + assert(index < PMA_MAX); + // NOLINTNEXTLINE(bugprone-narrowing-conversions) + return m_m.get_state().pmas[static_cast(index)]; } template @@ -601,7 +543,7 @@ class state_access : public i_state_access { // Mark page that was on TLB as dirty so we know to update the Merkle tree if constexpr (ETYPE == TLB_WRITE) { if (tlbhe.vaddr_page != TLB_INVALID_PAGE) { - pma_entry &pma = do_get_pma_entry(static_cast(tlbce.pma_index)); + pma_entry &pma = do_read_pma_entry(tlbce.pma_index); pma.mark_dirty_page(tlbce.paddr_page - pma.get_start()); } } @@ -623,7 +565,7 @@ class state_access : public i_state_access { if (tlbhe.vaddr_page != TLB_INVALID_PAGE) { tlbhe.vaddr_page = TLB_INVALID_PAGE; const tlb_cold_entry &tlbce = m_m.get_state().tlb.cold[ETYPE][eidx]; - pma_entry &pma = do_get_pma_entry(static_cast(tlbce.pma_index)); + pma_entry &pma = do_read_pma_entry(tlbce.pma_index); pma.mark_dirty_page(tlbce.paddr_page - pma.get_start()); } else { tlbhe.vaddr_page = TLB_INVALID_PAGE; diff --git a/src/translate-virtual-address.h b/src/translate-virtual-address.h index fe0811dc4..ecbecf867 100644 --- a/src/translate-virtual-address.h +++ b/src/translate-virtual-address.h @@ -46,6 +46,7 @@ #include #include "compiler-defines.h" +#include "find-pma-entry.h" #include "riscv-constants.h" namespace cartesi { @@ -58,7 +59,7 @@ namespace cartesi { /// \returns True if succeeded, false otherwise. template static inline bool write_ram_uint64(STATE_ACCESS &a, uint64_t paddr, uint64_t val) { - auto &pma = a.template find_pma_entry(paddr); + auto &pma = find_pma_entry(a, paddr); if (unlikely(!pma.get_istart_M() || !pma.get_istart_W())) { return false; } @@ -80,7 +81,7 @@ static inline bool write_ram_uint64(STATE_ACCESS &a, uint64_t paddr, uint64_t va /// \returns True if succeeded, false otherwise. template static inline bool read_ram_uint64(STATE_ACCESS &a, uint64_t paddr, uint64_t *pval) { - auto &pma = a.template find_pma_entry(paddr); + auto &pma = find_pma_entry(a, paddr); if (unlikely(!pma.get_istart_M() || !pma.get_istart_R())) { return false; } diff --git a/tests/lua/cartesi-machine-tests.lua b/tests/lua/cartesi-machine-tests.lua index 7478d3bfa..dbb7c6fe9 100755 --- a/tests/lua/cartesi-machine-tests.lua +++ b/tests/lua/cartesi-machine-tests.lua @@ -291,7 +291,7 @@ local riscv_tests = { { "translate_vaddr.bin", 343 }, { "htif_invalid_ops.bin", 109 }, { "clint_ops.bin", 133 }, - { "shadow_ops.bin", 114 }, + { "shadow_ops.bin", 80 }, { "compressed.bin", 410 }, } diff --git a/tests/machine/src/shadow_ops.S b/tests/machine/src/shadow_ops.S index e3c0666af..530f36547 100644 --- a/tests/machine/src/shadow_ops.S +++ b/tests/machine/src/shadow_ops.S @@ -32,9 +32,11 @@ #define MCAUSE_STORE_AMO_ACCESS_FAULT 0x7 #define MCAUSE_LOAD_ACCESS_FAULT 0x5 -#define SHADOWS_PMA_LAST_ENTRY_START (31*16) -#define SHADOWS_PMA_LAST_ENTRY_LENGTH (31*16+8) +#define SHADOWS_PMA_LAST_ENTRY_ISTART (31*16) +#define SHADOWS_PMA_LAST_ENTRY_ILENGTH (31*16+8) #define SHADOWS_PMA_TOTAL_SIZE (32*16) +#define EMPTY_PMA_ISTART (4) +#define EMPTY_PMA_ILENGTH (0) // Section with code .section .text.init @@ -47,33 +49,17 @@ _start: // We are allowed to read the shadow PMAs li t0, PMA_SHADOW_PMAS_START_DEF; - ld t1, SHADOWS_PMA_LAST_ENTRY_START(t0); - bnez t1, fail; - ld t1, SHADOWS_PMA_LAST_ENTRY_LENGTH(t0); - bnez t1, fail; + ld t1, SHADOWS_PMA_LAST_ENTRY_ISTART(t0); + li t2, EMPTY_PMA_ISTART + bne t1, t2, fail; + ld t1, SHADOWS_PMA_LAST_ENTRY_ILENGTH(t0); + li t2, EMPTY_PMA_ILENGTH + bne t1, t2, fail; // Set the exception handler to skip instructions la t0, skip_insn_trap; csrw mtvec, t0; - // Attempt to load a non 8-bytes value from the shadow PMAs - expect_trap(MCAUSE_LOAD_ACCESS_FAULT, - li t0, PMA_SHADOW_PMAS_START_DEF; - lw t1, 0(t0); - ) - - // Attempt to load from a misaligned shadow PMAs offset - expect_trap(MCAUSE_LOAD_ACCESS_FAULT, - li t0, PMA_SHADOW_PMAS_START_DEF; - lb t1, 1(t0); - ) - - // Attempt to load a PMA entry out of bounds from shadow PMAs - expect_trap(MCAUSE_LOAD_ACCESS_FAULT, - li t0, PMA_SHADOW_PMAS_START_DEF; - ld t1, SHADOWS_PMA_TOTAL_SIZE(t0); - ) - // Attempt to load a value from the shadow state expect_trap(MCAUSE_LOAD_ACCESS_FAULT, li t0, PMA_SHADOW_STATE_START_DEF; diff --git a/uarch/Makefile b/uarch/Makefile index cd80e391c..67cfeadcb 100644 --- a/uarch/Makefile +++ b/uarch/Makefile @@ -69,7 +69,6 @@ EMULATOR_SOURCES=\ shadow-tlb.cpp \ shadow-state.cpp \ shadow-uarch-state.cpp \ - shadow-pmas.cpp \ plic.cpp \ clint.cpp diff --git a/uarch/uarch-machine-state-access.h b/uarch/uarch-machine-state-access.h index 1b9455099..da9d20e23 100644 --- a/uarch/uarch-machine-state-access.h +++ b/uarch/uarch-machine-state-access.h @@ -14,8 +14,8 @@ // with this program (see COPYING). If not, see . // -#ifndef uarch_machine_state_access_H -#define uarch_machine_state_access_H +#ifndef UARCH_MACHINE_STATE_ACCESS_H +#define UARCH_MACHINE_STATE_ACCESS_H #include "uarch-runtime.h" // must be included first, because of assert @@ -50,6 +50,8 @@ static void raw_write_memory(uint64_t paddr, T val) { *p = val; } +//??D can we merge this class with the mock_pma_entry in src/replay-step-state-access.h ? +//??D maybe implement base class in pma.h and subclass here if there is some minor difference? class uarch_pma_entry final { public: struct flags { @@ -65,28 +67,61 @@ class uarch_pma_entry final { }; private: - int m_pma_index{-1}; - uint64_t m_start{}; - uint64_t m_length{}; + + uint64_t m_pma_index; + uint64_t m_start; + uint64_t m_length; flags m_flags; - const pma_driver *m_device_driver{}; - void *m_device_context{}; + const pma_driver *m_driver{nullptr}; + + static constexpr flags split_flags(uint64_t istart) { + flags f{}; + f.M = (((istart & PMA_ISTART_M_MASK) >> PMA_ISTART_M_SHIFT) != 0); + f.IO = (((istart & PMA_ISTART_IO_MASK) >> PMA_ISTART_IO_SHIFT) != 0); + f.E = (((istart & PMA_ISTART_E_MASK) >> PMA_ISTART_E_SHIFT) != 0); + f.R = (((istart & PMA_ISTART_R_MASK) >> PMA_ISTART_R_SHIFT) != 0); + f.W = (((istart & PMA_ISTART_W_MASK) >> PMA_ISTART_W_SHIFT) != 0); + f.X = (((istart & PMA_ISTART_X_MASK) >> PMA_ISTART_X_SHIFT) != 0); + f.IR = (((istart & PMA_ISTART_IR_MASK) >> PMA_ISTART_IR_SHIFT) != 0); + f.IW = (((istart & PMA_ISTART_IW_MASK) >> PMA_ISTART_IW_SHIFT) != 0); + f.DID = static_cast((istart & PMA_ISTART_DID_MASK) >> PMA_ISTART_DID_SHIFT); + return f; + } public: - uarch_pma_entry(int pma_index, uint64_t start, uint64_t length, flags flags, - const pma_driver *pma_driver = nullptr, void *device_context = nullptr) : - m_pma_index{pma_index}, - m_start{start}, - m_length{length}, - m_flags{flags}, - m_device_driver{pma_driver}, - m_device_context{device_context} {} - uarch_pma_entry() : uarch_pma_entry(-1, 0, 0, {false, false, true /* empty */}) { - ; + uarch_pma_entry(uint64_t pma_index, uint64_t istart, uint64_t ilength) : + m_pma_index{pma_index}, + m_start{istart & PMA_ISTART_START_MASK}, + m_length{ilength}, + m_flags{split_flags(istart)}, + m_driver{nullptr} { + if (m_flags.IO) { + switch (m_flags.DID) { + case PMA_ISTART_DID::shadow_state: + m_driver = &shadow_state_driver; + break; + case PMA_ISTART_DID::shadow_TLB: + m_driver = &shadow_tlb_driver; + break; + case PMA_ISTART_DID::CLINT: + m_driver = &clint_driver; + break; + case PMA_ISTART_DID::PLIC: + m_driver = &plic_driver; + break; + case PMA_ISTART_DID::HTIF: + m_driver = &htif_driver; + break; + default: + // Other unsupported device in uarch (eg. VirtIO) + abort(); + break; + } + } } - int get_index() const { + uint64_t get_index() const { return m_pma_index; } @@ -130,12 +165,16 @@ class uarch_pma_entry final { return m_flags.IR; } - const pma_driver *get_device_driver() { - return m_device_driver; + const pma_driver *get_driver() const { + return m_driver; } - void *get_device_context() { - return m_device_context; + const auto &get_device_noexcept() const { + return *this; + } + + static void *get_context() { + return nullptr; } void mark_dirty_page(uint64_t /*address_in_range*/) { @@ -501,11 +540,11 @@ class uarch_machine_state_access : public i_state_access(shadow_pmas_get_pma_abs_addr(i)); } - uint64_t do_read_pma_ilength(int i) { + uint64_t read_pma_ilength(int i) { return raw_read_memory(shadow_pmas_get_pma_abs_addr(i) + sizeof(uint64_t)); } @@ -531,92 +570,22 @@ class uarch_machine_state_access : public i_state_access - uarch_pma_entry &do_find_pma_entry(uint64_t paddr) { - for (unsigned int i = 0; i < m_pmas.size(); i++) { - auto &pma = get_pma_entry(static_cast(i)); - if (pma.get_istart_E()) { - return pma; - } - if (paddr >= pma.get_start() && paddr - pma.get_start() <= pma.get_length() - sizeof(T)) { - return pma; - } - } - abort(); - } - - uarch_pma_entry &do_get_pma_entry(int index) { + uarch_pma_entry &do_read_pma_entry(uint64_t index) { const uint64_t istart = read_pma_istart(index); const uint64_t ilength = read_pma_ilength(index); - if (!m_pmas[index]) { - m_pmas[index] = build_uarch_pma_entry(index, istart, ilength); + // NOLINTNEXTLINE(bugprone-narrowing-conversions) + int i = static_cast(index); + if (!m_pmas[i]) { + m_pmas[i] = uarch_pma_entry{index, istart, ilength}; } // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - return m_pmas[index].value(); + return m_pmas[i].value(); } unsigned char *do_get_host_memory(uarch_pma_entry &/*pma*/) { return nullptr; } - bool do_read_device(uarch_pma_entry &pma, uint64_t mcycle, uint64_t offset, uint64_t *pval, int log2_size) { - device_state_access da(*this, mcycle); - return pma.get_device_driver()->read(pma.get_device_context(), &da, offset, pval, log2_size); - } - - execute_status do_write_device(uarch_pma_entry &pma, uint64_t mcycle, uint64_t offset, uint64_t val, int log2_size) { - device_state_access da(*this, mcycle); - return pma.get_device_driver()->write(pma.get_device_context(), &da, offset, val, log2_size); - } - - uarch_pma_entry build_uarch_pma_entry(int index, uint64_t istart, uint64_t ilength) { - uint64_t start = 0; - uarch_pma_entry::flags flags; - split_istart(istart, start, flags); - const pma_driver *driver = nullptr; - void *device_ctx = nullptr; - if (flags.IO) { - switch (flags.DID) { - case PMA_ISTART_DID::shadow_state: - driver = &shadow_state_driver; - break; - case PMA_ISTART_DID::shadow_pmas: - driver = &shadow_pmas_driver; - break; - case PMA_ISTART_DID::shadow_TLB: - driver = &shadow_tlb_driver; - break; - case PMA_ISTART_DID::CLINT: - driver = &clint_driver; - break; - case PMA_ISTART_DID::PLIC: - driver = &plic_driver; - break; - case PMA_ISTART_DID::HTIF: - driver = &htif_driver; - break; - default: - // Other unsupported device in uarch (eg. VirtIO) - abort(); - break; - } - } - return uarch_pma_entry{index, start, ilength, flags, driver, device_ctx}; - } - - static constexpr void split_istart(uint64_t istart, uint64_t &start, uarch_pma_entry::flags &f) { - f.M = (((istart & PMA_ISTART_M_MASK) >> PMA_ISTART_M_SHIFT) != 0); - f.IO = (((istart & PMA_ISTART_IO_MASK) >> PMA_ISTART_IO_SHIFT) != 0); - f.E = (((istart & PMA_ISTART_E_MASK) >> PMA_ISTART_E_SHIFT) != 0); - f.R = (((istart & PMA_ISTART_R_MASK) >> PMA_ISTART_R_SHIFT) != 0); - f.W = (((istart & PMA_ISTART_W_MASK) >> PMA_ISTART_W_SHIFT) != 0); - f.X = (((istart & PMA_ISTART_X_MASK) >> PMA_ISTART_X_SHIFT) != 0); - f.IR = (((istart & PMA_ISTART_IR_MASK) >> PMA_ISTART_IR_SHIFT) != 0); - f.IW = (((istart & PMA_ISTART_IW_MASK) >> PMA_ISTART_IW_SHIFT) != 0); - f.DID = static_cast((istart & PMA_ISTART_DID_MASK) >> PMA_ISTART_DID_SHIFT); - start = istart & PMA_ISTART_START_MASK; - } - template volatile tlb_hot_entry& do_get_tlb_hot_entry(uint64_t eidx) { // Volatile is used, so the compiler does not optimize out, or do of order writes. @@ -680,7 +649,7 @@ class uarch_machine_state_access : public i_state_access(tlbce.pma_index)); + uarch_pma_entry &pma = do_read_pma_entry(tlbce.pma_index); pma.mark_dirty_page(tlbce.paddr_page - pma.get_start()); } } @@ -709,7 +678,7 @@ class uarch_machine_state_access : public i_state_access(eidx); - uarch_pma_entry &pma = do_get_pma_entry(static_cast(tlbce.pma_index)); + uarch_pma_entry &pma = do_read_pma_entry(tlbce.pma_index); pma.mark_dirty_page(tlbce.paddr_page - pma.get_start()); } else { tlbhe.vaddr_page = TLB_INVALID_PAGE;