diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 000000000..d58d5c6c6
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+third-party/** linguist-vendored
diff --git a/src/Makefile b/src/Makefile
index f69dcb85a..e183c8b51 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -84,8 +84,8 @@ SLIRP_INC=-I$(BREW_PREFIX)/libslirp/include
# Macports installation
else ifneq (,$(PORT_PREFIX))
INSTALL_PREFIX=/opt/local
-BOOST_LIB_DIR=-L$(INSTALL_PREFIX)/libexec/boost/1.81/lib
-BOOST_INC=-I$(INSTALL_PREFIX)/libexec/boost/1.81/include
+BOOST_LIB_DIR=-L$(INSTALL_PREFIX)/libexec/boost/1.87/lib
+BOOST_INC=-I$(INSTALL_PREFIX)/libexec/boost/1.87/include
SLIRP_LIB=-L$(INSTALL_PREFIX)/lib -lslirp
SLIRP_INC=-I$(INSTALL_PREFIX)/include
@@ -133,8 +133,8 @@ ifeq ($(slirp),yes)
# Workaround for building with macports lua-luarocks installation
machine.o: INCS+=$(SLIRP_INC)
machine.clang-tidy: INCS+=$(SLIRP_INC)
-virtio-net-carrier-slirp.o: INCS+=$(SLIRP_INC)
-virtio-net-carrier-slirp.clang-tidy: INCS+=$(SLIRP_INC)
+virtio-net-user-address-range.o: INCS+=$(SLIRP_INC)
+virtio-net-user-address-range.clang-tidy: INCS+=$(SLIRP_INC)
#INCS+=$(SLIRP_INC)
LIBCARTESI_COMMON_LIBS+=$(SLIRP_LIB)
else
@@ -345,52 +345,43 @@ c-api: $(LIBCARTESI) $(LIBCARTESI_MERKLE_TREE) $(LIBCARTESI_JSONRPC)
.PHONY: all generate use clean lint format format-lua check-format check-format-lua luacartesi hash c-api compile_flags.txt
LIBCARTESI_OBJS:= \
- pma-driver.o \
- clint.o \
- clint-factory.o \
- plic.o \
- plic-factory.o \
- virtio-factory.o \
- virtio-device.o \
- virtio-console.o \
- virtio-p9fs.o \
- virtio-net.o \
- virtio-net-carrier-tuntap.o \
- virtio-net-carrier-slirp.o \
- dtb.o \
- os.o \
- htif.o \
- htif-factory.o \
- shadow-state.o \
- shadow-state-factory.o \
- shadow-pmas-factory.o \
- shadow-tlb.o \
- shadow-tlb-factory.o \
- shadow-uarch-state.o \
- shadow-uarch-state-factory.o \
- pma.o \
- machine.o \
- machine-config.o \
- json-util.o \
base64.o \
+ clint-address-range.o \
+ dtb.o \
+ htif-address-range.o \
interpret.o \
- virtual-machine.o \
- sha3.o \
+ json-util.o \
+ machine-c-api.o \
+ machine-config.o \
machine-merkle-tree.o \
+ machine.o \
+ memory-address-range.o \
+ os.o \
+ plic-address-range.o \
pristine-merkle-tree.o \
- machine-c-api.o \
+ replay-step-state-access-interop.o \
+ send-cmio-response.o \
+ sha3.o \
+ shadow-state-address-range.o \
+ shadow-tlb-address-range.o \
+ shadow-uarch-state-address-range.o \
+ uarch-pristine-hash.o \
uarch-pristine-ram.o \
uarch-pristine-state-hash.o \
- uarch-pristine-hash.o \
- uarch-interpret.o \
- uarch-step.o \
uarch-reset-state.o \
- send-cmio-response.o \
- replay-step-state-access-interop.o
+ uarch-step.o \
+ local-machine.o \
+ uarch-interpret.o \
+ virtio-address-range.o \
+ virtio-console-address-range.o \
+ virtio-p9fs-address-range.o \
+ virtio-net-address-range.o \
+ virtio-net-tuntap-address-range.o \
+ virtio-net-user-address-range.o
CARTESI_CLUA_OBJS:= \
clua.o \
- clua-i-virtual-machine.o \
+ clua-i-machine.o \
uarch-pristine-ram.o \
uarch-pristine-state-hash.o \
uarch-pristine-hash.o
@@ -411,7 +402,7 @@ MERKLE_TREE_HASH_OBJS:= \
merkle-tree-hash.o
LIBCARTESI_JSONRPC_OBJS:= \
- jsonrpc-virtual-machine.o \
+ jsonrpc-machine.o \
os.o \
jsonrpc-machine-c-api.o \
uarch-pristine-ram.o \
diff --git a/src/address-range-constants.h b/src/address-range-constants.h
new file mode 100644
index 000000000..1c4fdb65f
--- /dev/null
+++ b/src/address-range-constants.h
@@ -0,0 +1,77 @@
+// 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 ADDRESS_RANGE_CONSTANTS_H
+#define ADDRESS_RANGE_CONSTANTS_H
+
+#include
+
+#include "address-range-defines.h"
+
+namespace cartesi {
+
+/// \brief Fixed address ranges.
+enum AR_ranges : uint64_t {
+ AR_SHADOW_STATE_START = EXPAND_UINT64_C(AR_SHADOW_STATE_START_DEF), ///< Start of shadow state range
+ AR_SHADOW_STATE_LENGTH = EXPAND_UINT64_C(AR_SHADOW_STATE_LENGTH_DEF), ///< Length of shadow state range
+ AR_PMAS_START = EXPAND_UINT64_C(AR_PMAS_START_DEF), ///< Start of PMAS list range
+ AR_PMAS_LENGTH = EXPAND_UINT64_C(AR_PMAS_LENGTH_DEF), ///< Length of PMAS list range
+ AR_DTB_START = EXPAND_UINT64_C(AR_DTB_START_DEF), ///< Start of DTB range
+ AR_DTB_LENGTH = EXPAND_UINT64_C(AR_DTB_LENGTH_DEF), ///< Length of DTB range
+ AR_SHADOW_TLB_START = EXPAND_UINT64_C(AR_SHADOW_TLB_START_DEF), ///< Start of shadow TLB range
+ AR_SHADOW_TLB_LENGTH = EXPAND_UINT64_C(AR_SHADOW_TLB_LENGTH_DEF), ///< Length of shadow TLB range
+ AR_SHADOW_UARCH_STATE_START =
+ EXPAND_UINT64_C(AR_SHADOW_UARCH_STATE_START_DEF), ///< Start of uarch shadow state range
+ AR_SHADOW_UARCH_STATE_LENGTH =
+ EXPAND_UINT64_C(AR_SHADOW_UARCH_STATE_LENGTH_DEF), ///< Length of uarch shadow state range
+ AR_CLINT_START = EXPAND_UINT64_C(AR_CLINT_START_DEF), ///< Start of CLINT range
+ AR_CLINT_LENGTH = EXPAND_UINT64_C(AR_CLINT_LENGTH_DEF), ///< Length of CLINT range
+ AR_PLIC_START = EXPAND_UINT64_C(AR_PLIC_START_DEF), ///< Start of PLIC range
+ AR_PLIC_LENGTH = EXPAND_UINT64_C(AR_PLIC_LENGTH_DEF), ///< Length of PLIC range
+ AR_HTIF_START = EXPAND_UINT64_C(AR_HTIF_START_DEF), ///< Start of HTIF range
+ AR_HTIF_LENGTH = EXPAND_UINT64_C(AR_HTIF_LENGTH_DEF), ///< Length of HTIF range
+ AR_UARCH_RAM_START = EXPAND_UINT64_C(AR_UARCH_RAM_START_DEF), ///< Start of uarch RAM range
+ AR_UARCH_RAM_LENGTH = EXPAND_UINT64_C(AR_UARCH_RAM_LENGTH_DEF), ///< Length of uarch RAM range
+ AR_CMIO_RX_BUFFER_START = EXPAND_UINT64_C(AR_CMIO_RX_BUFFER_START_DEF), ///< Start of CMIO RX buffer range
+ AR_CMIO_RX_BUFFER_LOG2_SIZE = EXPAND_UINT64_C(AR_CMIO_RX_BUFFER_LOG2_SIZE_DEF), ///< Log2 of CMIO RX buffer range
+ AR_CMIO_RX_BUFFER_LENGTH = (UINT64_C(1) << AR_CMIO_RX_BUFFER_LOG2_SIZE_DEF), ///< Length of CMIO RX buffer range
+ AR_CMIO_TX_BUFFER_START = EXPAND_UINT64_C(AR_CMIO_TX_BUFFER_START_DEF), ///< Start of CMIO TX buffer range
+ AR_CMIO_TX_BUFFER_LOG2_SIZE = EXPAND_UINT64_C(AR_CMIO_TX_BUFFER_LOG2_SIZE_DEF), ///< Log2 of CMIO TX buffer range
+ AR_CMIO_TX_BUFFER_LENGTH = (UINT64_C(1) << AR_CMIO_TX_BUFFER_LOG2_SIZE_DEF), ///< Length of CMIO TX buffer range
+ AR_DRIVE_START = EXPAND_UINT64_C(AR_DRIVE_START_DEF), ///< Start address for flash drive ranges
+ AR_DRIVE_OFFSET = EXPAND_UINT64_C(AR_DRIVE_OFFSET_DEF), ///< Offset for extra flash drive ranges
+
+ AR_FIRST_VIRTIO_START = EXPAND_UINT64_C(AR_FIRST_VIRTIO_START_DEF), ///< Start of first VIRTIO range
+ AR_VIRTIO_LENGTH = EXPAND_UINT64_C(AR_VIRTIO_LENGTH_DEF), ///< Length of each VIRTIO range
+ AR_LAST_VIRTIO_END = EXPAND_UINT64_C(AR_LAST_VIRTIO_END_DEF), ///< End of last VIRTIO range
+
+ AR_RAM_START = EXPAND_UINT64_C(AR_RAM_START_DEF), ///< Start of RAM range
+};
+
+/// \brief PMA constants.
+enum AR_constants : uint64_t {
+ AR_PAGE_SIZE_LOG2 = EXPAND_UINT64_C(AR_PAGE_SIZE_LOG2_DEF), ///< Log2 of physical memory page size.
+ AR_PAGE_SIZE = (UINT64_C(1) << AR_PAGE_SIZE_LOG2_DEF), ///< Physical memory page size.
+};
+
+/// \brief PMA masks.
+enum AR_masks : uint64_t {
+ AR_ADDRESSABLE_MASK = ((UINT64_C(1) << 56) - 1) ///< Mask for addressable ranges.
+};
+
+} // namespace cartesi
+
+#endif
diff --git a/src/address-range-defines.h b/src/address-range-defines.h
new file mode 100644
index 000000000..4f49e9141
--- /dev/null
+++ b/src/address-range-defines.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 AR_DEFINES_H
+#define AR_DEFINES_H
+
+// NOLINTBEGIN(cppcoreguidelines-macro-usage,cppcoreguidelines-macro-to-enum,modernize-macro-to-enum)
+#define AR_SHADOW_STATE_START_DEF 0x0 ///< Shadow start address
+#define AR_SHADOW_STATE_LENGTH_DEF 0x1000 ///< Shadow length in bytes
+#define AR_PMAS_START_DEF 0x10000 ///< PMA Array start address
+#define AR_PMAS_LENGTH_DEF 0x1000 ///< PMA Array length in bytes
+#define AR_SHADOW_TLB_START_DEF 0x20000 ///< TLB start address
+#define AR_SHADOW_TLB_LENGTH_DEF 0x6000 ///< TLB length in bytes
+#define AR_SHADOW_UARCH_STATE_START_DEF 0x400000 ///< microarchitecture shadow state start address
+#define AR_SHADOW_UARCH_STATE_LENGTH_DEF 0x1000 ///< microarchitecture shadow state length
+#define AR_UARCH_RAM_START_DEF 0x600000 ///< microarchitecture RAM start address
+#define AR_UARCH_RAM_LENGTH_DEF 0x200000 ///< microarchitecture RAM length
+#define AR_CLINT_START_DEF 0x2000000 ///< CLINT start address
+#define AR_CLINT_LENGTH_DEF 0xC0000 ///< CLINT length in bytes
+#define AR_PLIC_START_DEF 0x40100000 ///< Start of PLIC range
+#define AR_PLIC_LENGTH_DEF 0x00400000 ///< Length of PLIC range
+#define AR_HTIF_START_DEF 0x40008000 ///< HTIF base address (to_host)
+#define AR_HTIF_LENGTH_DEF 0x1000 ///< HTIF length in bytes
+#define AR_FIRST_VIRTIO_START_DEF 0x40010000 ///< Start of first VIRTIO range
+#define AR_VIRTIO_LENGTH_DEF 0x1000 ///< Length of each VIRTIO range
+#define AR_LAST_VIRTIO_END_DEF 0x40020000 ///< End of last VIRTIO range
+#define AR_DTB_START_DEF 0x7ff00000 ///< DTB start address
+#define AR_DTB_LENGTH_DEF 0x100000 ///< DTB length in bytes
+#define AR_CMIO_RX_BUFFER_START_DEF 0x60000000 ///< CMIO RX buffer start address
+#define AR_CMIO_RX_BUFFER_LOG2_SIZE_DEF 21 ///< log2 of CMIO RX buffer length in bytes
+#define AR_CMIO_TX_BUFFER_START_DEF 0x60800000 ///< CMIO TX buffer start address
+#define AR_CMIO_TX_BUFFER_LOG2_SIZE_DEF 21 ///< log2 of CMIO TX buffer length in bytes
+#define AR_DRIVE_START_DEF 0x80000000000000 ///< Start PMA address for flash drives
+#define AR_DRIVE_OFFSET_DEF 0x10000000000000 ///< PMA offset for extra flash drives
+
+#define AR_RAM_START_DEF 0x80000000 ///< RAM start address
+
+#define AR_PAGE_SIZE_LOG2_DEF 12 ///< log2 of physical memory page size.
+
+// helper for using UINT64_C with defines
+#ifndef EXPAND_UINT64_C
+#define EXPAND_UINT64_C(a) UINT64_C(a)
+#endif
+// NOLINTEND(cppcoreguidelines-macro-usage,cppcoreguidelines-macro-to-enum,modernize-macro-to-enum)
+#endif /* end of include guard: AR_DEFINES_H */
diff --git a/src/machine-memory-range-descr.h b/src/address-range-description.h
similarity index 74%
rename from src/machine-memory-range-descr.h
rename to src/address-range-description.h
index 787275b9d..325425b98 100644
--- a/src/machine-memory-range-descr.h
+++ b/src/address-range-description.h
@@ -14,8 +14,8 @@
// with this program (see COPYING). If not, see .
//
-#ifndef MACHINE_MEMORY_RANGE_DESCR_H
-#define MACHINE_MEMORY_RANGE_DESCR_H
+#ifndef ADDRESS_RANGE_DESCRIPTION_H
+#define ADDRESS_RANGE_DESCRIPTION_H
#include
#include
@@ -23,15 +23,15 @@
namespace cartesi {
-/// \brief Description of memory range used for introspection (i.e., get_memory_ranges())
-struct machine_memory_range_descr {
+/// \brief Description of an address range used for introspection (i.e., get_address_ranges())
+struct address_range_description {
uint64_t start = 0; ///< Start of memory range
uint64_t length = 0; ///< Length of memory range
std::string description; ///< User-friendly description for memory range
};
-/// \brief List of memory range descriptions used for introspection (i.e., get_memory_ranges())
-using machine_memory_range_descrs = std::vector;
+/// \brief List of address range descriptions used for introspection (i.e., get_address_ranges())
+using address_range_descriptions = std::vector;
} // namespace cartesi
diff --git a/src/address-range.h b/src/address-range.h
new file mode 100644
index 000000000..be681ccfb
--- /dev/null
+++ b/src/address-range.h
@@ -0,0 +1,389 @@
+// 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 ADDRESS_RANGE_H
+#define ADDRESS_RANGE_H
+
+#include
+#include
+#include
+#include
+#include
+
+#include "assert-printf.h"
+#include "i-device-state-access.h"
+#include "interpret.h"
+#include "pmas.h"
+
+namespace cartesi {
+
+// Forward declarations
+class machine;
+
+/// \file
+/// \brief Physical address range
+
+/// \brief Physical Address Range.
+/// \details The target's physical address layout is described by an array of specializations of such ranges.
+class address_range {
+
+ std::array m_description; ///< Description of address range for use in error messages.
+ uint64_t m_start; ///< Target physical address where range starts.
+ uint64_t m_length; ///< Length of range, in bytes.
+ uint64_t m_length_bit_ceil; ///< Smallest power of 2 that is not smaller than length, in bytes.
+ pmas_flags m_flags; ///< Physical memory attribute flags for range.
+
+public:
+ /// \brief Noexcept constexpr constructor for empty ranges with description
+ /// \detail Can be used to initialize a constexpr empty range
+ template
+ explicit constexpr address_range(const char (&description)[N]) noexcept :
+ m_description{},
+ m_start{0},
+ m_length{0},
+ m_length_bit_ceil{0},
+ m_flags{} {
+ for (unsigned i = 0; i < std::min(N, m_description.size() - 1); ++i) {
+ m_description[i] = description[i];
+ }
+ }
+
+ address_range(const address_range &other) = default;
+ address_range &operator=(const address_range &other) = default;
+ address_range(address_range &&other) = default;
+ address_range &operator=(address_range &&other) = default;
+ // NOLINTNEXTLINE(hicpp-use-equals-default,modernize-use-equals-default)
+ constexpr virtual ~address_range() {}; // = default; // doesn't work due to bug in gcc
+
+ template
+ [[noreturn]]
+ static void ABRTF(ABRT abrt, const char (&fmt)[N], ARGS... args) {
+ char buf[256]{};
+ std::ignore = snprintf(buf, std::size(buf), fmt, args...);
+ abrt(buf);
+ __builtin_trap();
+ }
+
+ /// \brief Constructor
+ /// \tparam ABRT type of function used to abort and report errors
+ /// \param description Description of address range for use in error messages (will be copied)
+ /// \param start Target physical address where range starts
+ /// \param length Length of range, in bytes
+ /// \param f Phyical memory attribute flags for range
+ template
+ address_range(const char *description, uint64_t start, uint64_t length, const pmas_flags &flags, ABRT abrt) :
+ m_description{},
+ m_start{start},
+ m_length{length},
+ m_length_bit_ceil{(length >> 63) == 0 ? std::bit_ceil(length) : 0},
+ m_flags{flags} {
+ // Non-empty description is mandatory
+ if (description == nullptr || *description == '\0') {
+ ABRTF(abrt, "address range 0x%" PRIx64 ":0x%" PRIx64 " has empty description", m_start, m_length);
+ }
+ for (unsigned i = 0; i < m_description.size() - 1 && description[i] != '\0'; ++i) {
+ m_description[i] = description[i];
+ }
+ // All address ranges must be page-aligned
+ if ((m_length & ~PMA_ISTART_START_MASK) != 0) {
+ ABRTF(abrt, "length must be multiple of page size when initializing %s", description);
+ }
+ if ((m_start & ~PMA_ISTART_START_MASK) != 0) {
+ ABRTF(abrt, "start of %s (0x%" PRIx64 ") must be aligned to page boundary of %" PRId64 " bytes",
+ description, start, AR_PAGE_SIZE);
+ }
+ // It must be possible to round length up to the next power of two
+ if (m_length_bit_ceil == 0) {
+ ABRTF(abrt, "address range too long when initializing %s", description);
+ }
+ // Empty range must really be empty
+ if (m_length == 0) {
+ if (m_start != 0) {
+ ABRTF(abrt, "range with length 0 must start at 0 when initializing %s", description);
+ }
+ if (m_flags.M) {
+ ABRTF(abrt, "memory address range cannot have length 0 when initializing %s", description);
+ }
+ if (m_flags.IO) {
+ ABRTF(abrt, "device address range cannot have length 0 when initializing %s", description);
+ }
+ }
+ }
+
+ /// \brief Checks if a range of addresses is entirely contained within this range
+ /// \param offset Start of range of interest, relative to start of this range
+ /// \param length Length of range of interest, in bytes
+ /// \returns True if and only if range of interest is entirely contained within this range
+ bool contains_relative(uint64_t offset, uint64_t length) const noexcept {
+ return get_length() >= length && offset <= get_length() - length;
+ }
+
+ /// \brief Checks if a range of addresses is entirely contained within this range
+ /// \param start Target phyisical address of start of range of interest
+ /// \param length Length of range of interest, in bytes
+ /// \returns True if and only if range of interest is entirely contained within this range
+ bool contains_absolute(uint64_t start, uint64_t length) const noexcept {
+ return start >= get_start() && contains_relative(start - get_start(), length);
+ }
+
+ /// \brief Returns PMA flags used during construction
+ /// \returns Flags
+ const pmas_flags &get_flags() const noexcept {
+ return m_flags;
+ }
+
+ /// \brief Returns description of address range for use in error messages.
+ /// \returns Description
+ const char *get_description() const noexcept {
+ return m_description.data();
+ }
+
+ /// \brief Returns target physical address where range starts.
+ /// \returns Start of range
+ uint64_t get_start() const noexcept {
+ return m_start;
+ }
+
+ /// \brief Returns length of range, in bytes.
+ /// \returns Length of range
+ uint64_t get_length() const noexcept {
+ return m_length;
+ }
+
+ /// \brief Returns smallest power of 2 that is not smaller than range length, in bytes
+ /// \returns Bit-ceil of length of range
+ uint64_t get_length_bit_ceil() const noexcept {
+ return m_length_bit_ceil;
+ }
+
+ /// \brief Test if address range is occupied by memory
+ /// \returns True if and only if range is occupied by memory
+ /// \details In this case, get_host_memory() is guaranteed not to return nullptr.
+ bool is_memory() const noexcept {
+ return m_flags.M;
+ }
+
+ /// \brief Test if address range is occupied by a device
+ /// \returns True if and only if range is occupied by a device
+ /// \details In this case, read_device() and write_device() are operational.
+ bool is_device() const noexcept {
+ return m_flags.IO;
+ }
+
+ /// \brief Test if address range is empty
+ /// \returns True if and only if range is empty
+ /// \details Empty ranges should be used only for sentinels.
+ bool is_empty() const noexcept {
+ return m_length == 0;
+ }
+
+ /// \brief Tests if range is readable
+ /// \returns True if and only if range is readable from within the machine.
+ bool is_readable() const noexcept {
+ return m_flags.R;
+ }
+
+ /// \brief Tests if range is writeable
+ /// \returns True if and only if range is writeable from within the machine.
+ bool is_writeable() const noexcept {
+ return m_flags.W;
+ }
+
+ /// \brief Tests if range is executable
+ /// \returns True if and only if range is executable from within the machine.
+ bool is_executable() const noexcept {
+ return m_flags.X;
+ }
+
+ /// \brief Tests if range is read-idempotent
+ /// \returns True if and only if what is read from range remains there until written to
+ bool is_read_idempotent() const noexcept {
+ return m_flags.IR;
+ }
+
+ /// \brief Tests if range is write-idempotent
+ /// \returns True if and only if what is written to range remains there and can be read until written to again
+ bool is_write_idempotent() const noexcept {
+ return m_flags.IW;
+ }
+
+ /// \brief Returns driver ID associated to range
+ /// \returns Teh driver ID
+ PMA_ISTART_DID get_driver_id() const noexcept {
+ return m_flags.DID;
+ }
+
+ /// \brief Returns packed address range istart field as per whitepaper
+ /// \returns Packed address range istart
+ uint64_t get_istart() const noexcept {
+ return pmas_pack_istart(m_flags, m_start);
+ }
+
+ /// \brief Returns encoded addres range ilength field as per whitepaper
+ /// \returns Packed address range ilength
+ /// \details This currently contains only the length itself
+ uint64_t get_ilength() const noexcept {
+ return get_length();
+ }
+
+ /// \brief Read contents from address range with, no side-effects.
+ /// \param m Reference to machine.
+ /// \param offset Offset within range to start reading.
+ /// \param length Number of bytes to read.
+ /// \param data Receives pointer to start of data, or nullptr if data is constant *and* pristine (filled with
+ /// zeros).
+ /// \param scratch Pointer to memory buffer that must be able to hold \p length bytes.
+ /// \returns True if operation succeeded, false otherwise.
+ bool peek(const machine &m, uint64_t offset, uint64_t length, const unsigned char **data,
+ unsigned char *scratch) const noexcept {
+ return do_peek(m, offset, length, data, scratch);
+ };
+
+ // -----
+ // These are only for device ranges
+ // -----
+
+ /// \brief Reads a word from a device
+ /// \param da State access object through which the machine state can be accessed.
+ /// \param offset Where to start reading, relative to start of this range.
+ /// \param log2_size Log2 of size of value to read (0=uint8_t, 1=uint16_t, 2=uint32_t, 3=uint64_t).
+ /// \param pval Pointer to word where value will be stored.
+ /// \returns True if operation succeeded, false otherwise.
+ bool read_device(i_device_state_access *da, uint64_t offset, int log2_size, uint64_t *pval) const noexcept {
+ return do_read_device(da, offset, log2_size, pval);
+ }
+
+ /// \brief Writes a word to a device
+ /// \param da State access object through which the machine state can be accessed.
+ /// \param offset Where to start reading, relative to start of this range.
+ /// \param log2_size Log2 of size of value to write (0=uint8_t, 1=uint16_t, 2=uint32_t, 3=uint64_t).
+ /// \param val Value to write.
+ /// \returns execute::failure if operation failed, otherwise a success code if operation succeeded.
+ execute_status write_device(i_device_state_access *da, uint64_t offset, int log2_size, uint64_t val) noexcept {
+ return do_write_device(da, offset, log2_size, val);
+ }
+
+ // -----
+ // These are only for memory ranges
+ // -----
+
+ /// \brief Returns start of associated memory region in host
+ /// \returns Pointer to memory
+ const unsigned char *get_host_memory() const noexcept {
+ return do_get_host_memory();
+ }
+
+ /// \brief Returns start of associated memory region in host
+ /// \returns Pointer to memory
+ unsigned char *get_host_memory() noexcept {
+ return do_get_host_memory();
+ }
+
+ /// \brief Mark a given page as dirty
+ /// \param offset Any offset in range within desired page
+ void mark_dirty_page(uint64_t offset) noexcept {
+ do_mark_dirty_page(offset);
+ }
+
+ /// \brief Mark all pages in a range of interest as dirty
+ /// \param offset Start of range of interest, relative to start of this range
+ /// \param length Length of range of interest, in bytes
+ void mark_dirty_pages(uint64_t offset, uint64_t length) noexcept {
+ auto offset_aligned = offset &= ~(AR_PAGE_SIZE - 1);
+ const auto length_aligned = length + (offset - offset_aligned);
+ for (; offset_aligned < length_aligned; offset_aligned += AR_PAGE_SIZE) {
+ mark_dirty_page(offset_aligned);
+ }
+ }
+
+ /// \brief Mark a given page as clean
+ /// \param offset Any offset in range within desired page
+ void mark_clean_page(uint64_t offset) noexcept {
+ do_mark_clean_page(offset);
+ }
+
+ /// \brief Marks all pages in range as clean
+ void mark_pages_clean() noexcept {
+ do_mark_pages_clean();
+ }
+
+ /// \brief Tests if a given page is dirty
+ /// \param offset Any offset in range within desired page
+ /// \returns True if and only if page is marked dirty
+ bool is_page_marked_dirty(uint64_t offset) const noexcept {
+ return do_is_page_marked_dirty(offset);
+ }
+
+private:
+ // Default implementation of peek() always fails
+ virtual bool do_peek(const machine & /*m*/, uint64_t /*offset*/, uint64_t /*length*/,
+ const unsigned char ** /*data*/, unsigned char * /*scratch*/) const noexcept {
+ return false;
+ }
+
+ // Default implementation of read_device() for non-device ranges always fails
+ virtual bool do_read_device(i_device_state_access * /*a*/, uint64_t /*offset*/, int /*log2_size*/,
+ uint64_t * /*val*/) const noexcept {
+ return false;
+ }
+
+ // Default implementation of write_device() for non-device ranges always fails
+ virtual execute_status do_write_device(i_device_state_access * /*a*/, uint64_t /*offset*/, int /* log2_size */,
+ uint64_t /*val*/) noexcept {
+ return execute_status::failure;
+ }
+
+ // Default implementation of get_host_memory() for non-memory ranges returns nullptr
+ virtual const unsigned char *do_get_host_memory() const noexcept {
+ return nullptr;
+ }
+
+ virtual unsigned char *do_get_host_memory() noexcept {
+ return nullptr;
+ }
+
+ // Defaul implemenation always assumes every page is always dirty
+ virtual void do_mark_dirty_page(uint64_t /*offset*/) noexcept {
+ ;
+ }
+
+ virtual void do_mark_clean_page(uint64_t /*offset*/) noexcept {
+ ;
+ }
+
+ virtual void do_mark_pages_clean() noexcept {
+ ;
+ }
+
+ virtual bool do_is_page_marked_dirty(uint64_t /*offset*/) const noexcept {
+ return true;
+ }
+};
+
+template
+constexpr static auto make_empty_address_range(const char (&description)[N]) {
+ return address_range{description};
+}
+
+template
+static inline auto make_address_range(const char *description, uint64_t start, uint64_t length, pmas_flags f,
+ ABRT abrt) {
+ return address_range{description, start, length, f, abrt};
+}
+
+} // namespace cartesi
+
+#endif // OCCUPIED_ADDRESS_RANGE_H
diff --git a/src/dump.h b/src/assert-printf.h
similarity index 59%
rename from src/dump.h
rename to src/assert-printf.h
index f7c00c538..0f0f5b41b 100644
--- a/src/dump.h
+++ b/src/assert-printf.h
@@ -13,22 +13,34 @@
// You should have received a copy of the GNU Lesser General Public License along
// with this program (see COPYING). If not, see .
//
-#ifndef DUMP_H
-#define DUMP_H
-#include
+#ifndef ASSERT_PRINTF_H
+#define ASSERT_PRINTF_H
+
+/// \file
+/// \brief Microarchitecture-dependent includes for printf and assert
#ifdef MICROARCHITECTURE
-template
-static inline void D_PRINTF(const char (&fmt)[N], ARGS... args) {
- std::ignore = printf(fmt, args...);
-}
+#include "uarch-runtime.h"
#else
+#include
#include
-template
-static inline auto D_PRINTF(const char (&fmt)[N], ARGS... args) {
- std::ignore = fprintf(stderr, fmt, args...);
-}
#endif
-#endif // DUMP_H
+#include
+#include
+
+static inline void d_vprintf(const char *fmt, va_list ap) {
+ std::ignore = vfprintf(stderr, fmt, ap);
+}
+
+// Better to use C-style variadic function that checks for format!
+// NOLINTNEXTLINE(cert-dcl50-cpp)
+__attribute__((__format__(__printf__, 1, 2))) static inline void d_printf(const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ d_vprintf(fmt, ap);
+ va_end(ap);
+}
+
+#endif
diff --git a/src/cartesi-machine.lua b/src/cartesi-machine.lua
index a6982ea0e..a339a0985 100755
--- a/src/cartesi-machine.lua
+++ b/src/cartesi-machine.lua
@@ -87,10 +87,6 @@ where options are:
--ram-length=
set RAM length.
- --dtb-image=
- name of file containing DTB image
- (default: auto generated flattened device tree).
-
--no-bootargs
clear default bootargs.
@@ -106,9 +102,10 @@ where options are:
: is one of
label:
- filename:
start:
length:
+ data_filename:
+ dht_filename:
shared
mount:
user:
@@ -116,10 +113,6 @@ where options are:
label (mandatory)
identifies the flash drive. init attempts to mount it as /mnt/.
- filename (optional)
- gives the name of the file containing the image for the flash drive.
- when omitted or set to the empty, the drive starts filled with 0.
-
start (optional)
sets the starting physical memory offset for flash drive in bytes.
when omitted, drives start at 1 << 55 and are spaced by 1 << 52.
@@ -130,8 +123,18 @@ where options are:
if omitted, the length is computed from the image in filename.
if length and filename are set, the image file size must match length.
+ data_filename (optional)
+ gives the name of the file containing the data for the flash drive.
+ when omitted or set to the empty, the drive starts filled with 0.
+
+ dht_filename (optional)
+ gives the name of the file containing the dense hash tree for the flash drive.
+ (this is the part of the hash tree that subintends the entire address
+ range for the drive, down to one hash per page.)
+ when omitted or set to the empty, the hash tree will be built from scratch.
+
shared (optional)
- target modifications to flash drive modify image file as well.
+ target modifications to flash drive modify the memory and hash tree files.
by default, image files are not modified and changes are lost.
mount (optional)
@@ -148,33 +151,72 @@ where options are:
filesystem being mounted.
in case mount is false, the default ownership is set to the root user.
- (an option "--flash-drive=label:root,filename:rootfs.ext2" is implicit)
+ (an option "--flash-drive=label:root,data_filename:rootfs.ext2" is implicit)
--replace-flash-drive=:[,:[,...]...]
--replace-memory-range=:[,:[,...]...]
- replaces an existing flash drive or cmio memory range right after
- machine instantiation.
+ replaces an existing memory range right after machine instantiation.
(typically used in conjunction with the --load= option.)
: is one of
- filename:
start:
length:
+ data_filename:
+ dht_filename:
shared
semantics are the same as for the --flash-drive option with the following
difference: start and length are mandatory, and must match those of a
- previously existing flash drive or cmio memory memory range.
+ previously existing memory range (e.g., flash drive, cmio buffer, etc).
+ --ram=:[,:[,...]...]
+ --dtb=:[,:[,...]...]
+ --tlb=:[,:[,...]...]
--cmio-rx-buffer=:[,:[,...]...]
--cmio-tx-buffer=:[,:[,...]...]
+ --pmas=:[,:[,...]...]
+ --uarch-ram=:[,:[,...]...]
+ configures file storage for other memory ranges in the machine
: is one of
- filename:
+ data_filename:
+ dht_filename:
shared
semantics are the same as for the --flash-drive option.
+ --hash-tree=:[,:[,...]...]
+ configures the global hash tree the the machine
+
+ : is one of
+ hasher:
+ sht_filename:
+ phtc_filename:
+ phtc_size:
+ shared
+
+ hasher (default: "keccak")
+ hashing algorithm used for the tree
+
+ sht_filename (optional)
+ gives the name of the file containing the sparse hash-tree for the machine.
+ (this is the part of the hash tree from the root down to leaves that subindend
+ entire memory ranges, such as flash-drives or the ram.)
+ when omitted or set to the empty, the hash tree will be built from scratch.
+
+ phtc_filename (optional)
+ gives the name of the file containing the page hash-tree cache for the machine.
+ (this is a cache with the dense hash-trees for a subset of the pages in the
+ machine, all the way down to 256-bit words.)
+ when omitted or set to the empty, the page hash-tree cache will start empty.
+
+ phtc_size (default: 2048)
+ give the maximum number of pages in the cache.
+
+ shared (optional)
+ target modifications to machine state modify the sparse hash tree file.
+ by default, the files is not modified and changes are lost.
+
--cmio-advance-state=:[,:[,...]...]
advances the state of the machine through a number of inputs.
@@ -568,7 +610,8 @@ local remote_destroy = true
local perform_rollbacks = true
local default_config = cartesi.machine:get_default_config()
local images_path = adjust_images_path(os.getenv("CARTESI_IMAGES_PATH"))
-local flash_image_filename = { root = images_path .. "rootfs.ext2" }
+local flash_data_filename = { root = images_path .. "rootfs.ext2" }
+local flash_dht_filename = {}
local flash_label_order = { "root" }
local flash_shared = {}
local flash_mount = {}
@@ -583,18 +626,37 @@ local has_virtio_console = false
local has_network = false
local has_sync_init_date = false
local memory_range_replace = {}
-local ram_image_filename = images_path .. "linux.bin"
-local ram_length = 128 << 20 -- 128MB
-local dtb_image_filename = nil
-local bootargs = default_config.dtb.bootargs
+local ram = {
+ length = 128 << 20, -- 128MB
+ backing_store = { data_filename = images_path .. "linux.bin" },
+}
local init_splash = true
local append_bootargs = ""
local append_init = ""
local append_entrypoint = ""
-local uarch
-local cmio
+local dtb = {
+ init = "",
+ entrypoint = "",
+ bootargs = default_config.dtb.bootargs,
+}
+local tlb = {}
+local cmio = {
+ rx_buffer = {},
+ tx_buffer = {},
+}
local cmio_advance
local cmio_inspect
+local uarch = {
+ ram = {
+ backing_store = {
+ data_filename = "",
+ },
+ },
+}
+local pmas = {}
+local hash_tree = {
+ hasher = default_config.hash_tree.hasher,
+}
local concurrency_update_merkle_tree = 0
local skip_root_hash_check = false
local skip_root_hash_store = false
@@ -630,51 +692,66 @@ local log_step_filename
local function parse_memory_range(opts, what, all)
local f = util.parse_options(opts, {
- filename = true,
+ data_filename = true,
+ dht_filename = true,
shared = true,
length = true,
start = true,
})
- f.image_filename = f.filename
- f.filename = nil
- if f.image_filename == true then f.image_filename = "" end
- assert(not f.shared or f.shared == true, "invalid " .. what .. " shared value in " .. all)
+ if f.data_filename == true then f.backing_filename = "" end
+ if f.dense_hashdense_tree_filename == true then f.dht_filename = "" end
+ if f.shared == nil or f.shared == "false" then f.shared = false end
+ if f.shared == "true" then f.shared = true end
+ assert(type(f.shared) == "boolean", "invalid " .. what .. " shared value in " .. all)
+ f.backing_store = {
+ data_filename = f.data_filename,
+ dht_filename = f.dht_filename,
+ shared = f.shared,
+ }
+ f.data_filename = nil
+ f.dht_filename = nil
+ f.shared = nil
f.start = assert(util.parse_number(f.start), "invalid " .. what .. " start in " .. all)
f.length = assert(util.parse_number(f.length), "invalid " .. what .. " length in " .. all)
return f
end
-local function parse_cmio_buffer(opts, what, all)
+local function parse_backing_store(opts, what, all, def)
local f = util.parse_options(opts, {
- filename = true,
+ data_filename = true,
+ dht_filename = true,
shared = true,
})
- f.image_filename = f.filename
- f.filename = nil
- if f.image_filename == true then f.image_filename = "" end
- assert(not f.shared or f.shared == true, "invalid " .. what .. " shared value in " .. all)
+ if f.data_filename == true then f.data_filename = "" end
+ if f.dht_filename == true then f.dht_filename = "" end
+ if f.shared == nil or f.shared == "false" then f.shared = false end
+ if f.shared == "true" then f.shared = true end
+ assert(type(f.shared) == "boolean", "invalid " .. what .. " shared value in " .. all)
+ if def then
+ for i, v in pairs(def) do
+ if f[i] == nil then f[i] = v end
+ end
+ end
return f
end
-local function handle_sync_init_date(all)
- if not all then return false end
+local function handle_sync_init_date()
if has_sync_init_date then return end
unreproducible = true
has_sync_init_date = true
-- round up time by 1, to decrease chance of guest time being in the past
local seconds = os.time() + 1
append_init = append_init .. "busybox date -s @" .. seconds .. " >> /dev/null\n"
+ return true
end
local function handle_virtio_9p(tag, host_directory)
- if not tag or not host_directory then return false end
unreproducible = true
table.insert(virtio, { type = "p9fs", tag = tag, host_directory = host_directory })
return true
end
local function handle_volume_option(host_directory, guest_directory)
- if not host_directory or not guest_directory then return false end
unreproducible = true
local tag = "vfs" .. virtio_volume_count
virtio_volume_count = virtio_volume_count + 1
@@ -686,33 +763,28 @@ local function handle_volume_option(host_directory, guest_directory)
return true
end
-local function handle_htif_console_getchar(all)
- if not all then return false end
+local function handle_htif_console_getchar()
htif_console_getchar = true
unreproducible = true
return true
end
local function handle_user(user)
- if not user then return false end
append_init = append_init .. "USER=" .. user .. "\n"
return true
end
local function handle_env(name, value)
- if not name or not value then return false end
append_init = append_init .. "export " .. name .. "=" .. value .. "\n"
return true
end
local function handle_workdir(value)
- if not value then return false end
append_init = append_init .. "WORKDIR=" .. value .. "\n"
return true
end
local function handle_hostname(name)
- if not name then return false end
append_init = append_init .. "busybox hostname " .. name .. "\n"
return true
end
@@ -795,18 +867,16 @@ busybox ip route add default via 10.0.2.2 dev eth0
return true
end
-local function handle_virtio_console(all)
- if not all then return false end
+local function handle_virtio_console()
if has_virtio_console then return end
unreproducible = true
has_virtio_console = true
-- Switch from HTIF Console (hvc0) to VirtIO console (hvc1)
- bootargs = bootargs:gsub("console=hvc0", "console=hvc1")
+ dtb.bootargs = dtb.bootargs:gsub("console=hvc0", "console=hvc1")
table.insert(virtio, 1, { type = "console" })
end
-local function handle_interactive(all)
- if not all then return false end
+local function handle_interactive()
handle_virtio_console(true)
handle_sync_init_date(true)
-- Expose current terminal features to the virtual terminal
@@ -826,24 +896,21 @@ end
local options = {
{
"^%-h$",
- function(all)
- if not all then return false end
+ function()
help()
return true
end,
},
{
"^%-%-help$",
- function(all)
- if not all then return false end
+ function()
help()
return true
end,
},
{
"^%-%-version$",
- function(all)
- if not all then return false end
+ function()
print(string.format("cartesi-machine %s", cartesi.VERSION))
if cartesi.GIT_COMMIT then print(string.format("git commit: %s", cartesi.GIT_COMMIT)) end
if cartesi.BUILD_TIME then print(string.format("build time: %s", cartesi.BUILD_TIME)) end
@@ -856,8 +923,7 @@ local options = {
},
{
"^%-%-version%-json$",
- function(all)
- if not all then return false end
+ function()
print("{")
print(string.format(' "version": "%s",', cartesi.VERSION))
print(string.format(' "version_major": %d,', cartesi.VERSION_MAJOR))
@@ -886,72 +952,120 @@ local options = {
end,
},
{
- "^%-%-dtb%-image%=(.*)$",
- function(o)
- if not o or #o < 1 then return false end
- dtb_image_filename = o
+ "^%-%-dtb%-image%=(.+)$",
+ function(opts)
+ dtb.backing_store = dtb.backing_store or {}
+ dtb.backing_store.data_filename = opts
return true
end,
},
{
"^%-%-no%-bootargs$",
- function(all)
- if not all then return false end
- bootargs = ""
+ function()
+ dtb.bootargs = ""
return true
end,
},
{
- "^%-%-append%-bootargs%=(.*)$",
- function(o)
- if not o then return false end
- if #o == 0 then return true end
+ "^%-%-append%-bootargs%=(.+)$",
+ function(opts)
if #append_bootargs == 0 then
- append_bootargs = o
+ append_bootargs = opts
else
- append_bootargs = append_bootargs .. " " .. o
+ append_bootargs = append_bootargs .. " " .. opts
end
return true
end,
},
+ {
+ "^(%-%-dtb%=(.+))$",
+ function(all, opts)
+ dtb.backing_store = parse_backing_store(opts, "dtb", all, dtb.backing_store)
+ return true
+ end,
+ },
{
"^%-%-ram%-length%=(.+)$",
function(n)
- if not n then return false end
- ram_length = assert(util.parse_number(n), "invalid RAM length " .. n)
+ ram.length = assert(util.parse_number(n), "invalid RAM length " .. n)
return true
end,
},
{
- "^%-%-ram%-image%=(.*)$",
- function(o)
- if not o or #o < 1 then return false end
- ram_image_filename = o
+ "^%-%-ram%-image%=(.+)$",
+ function(opts)
+ ram.backing_store.data_filename = opts
return true
end,
},
{
"^%-%-no%-ram%-image$",
- function(all)
- if not all then return false end
- ram_image_filename = ""
+ function()
+ ram.backing_store.data_filename = ""
+ return true
+ end,
+ },
+ {
+ "^(%-%-ram%=(.+))$",
+ function(all, opts)
+ ram.backing_store = parse_backing_store(opts, "ram", all, ram.backing_store)
+ return true
+ end,
+ },
+ {
+ "^(%-%-tlb%=(.+))$",
+ function(all, opts)
+ tlb.backing_store = parse_backing_store(opts, "tlb", all, tlb.backing_store)
+ return true
+ end,
+ },
+ {
+ "^(%-%-pmas%=(.+))$",
+ function(all, opts)
+ pmas.backing_store = parse_backing_store(opts, "pmas", all, pmas.backing_store)
+ return true
+ end,
+ },
+ {
+ "^%-%-uarch%-ram%-image%=(.+)$",
+ function(opts)
+ uarch.ram.backing_store.data_filename = opts
return true
end,
},
{
- "^%-%-uarch%-ram%-image%=(.*)$",
- function(o)
- if not o or #o < 1 then return false end
- uarch = uarch or {}
- uarch.ram = uarch.ram or {}
- uarch.ram.image_filename = o
+ "^(%-%-uarch%-ram%=(.+))$",
+ function(all, opts)
+ uarch.ram.backing_store = parse_backing_store(opts, "uarch-ram", all, uarch.ram.backing_store)
+ return true
+ end,
+ },
+ {
+ "^(%-%-hash%-tree%=(.+))$",
+ function(all, opts)
+ local h = util.parse_options(opts, {
+ hasher = true,
+ sht_filename = true,
+ phtc_filename = true,
+ phtc_size = true,
+ shared = true,
+ })
+ if h.sht_filename == true then h.sht_filename = "" end
+ if h.phtc_filename == true then h.phtc_filename = "" end
+ if h.hasher == true then h.hasher = "keccak" end
+ if h.shared == nil or h.shared == "false" then h.shared = false end
+ if h.shared == "true" then h.shared = true end
+ h.phtc_size = assert(util.parse_number(h.phtc_size), "invalid page hash cache size in " .. all)
+ assert(type(h.shared) == "boolean", "invalid hash tree shared value in " .. all)
+ for i, v in pairs(h) do
+ hash_tree[i] = v
+ end
return true
end,
},
{
"^%-%-unreproducible$",
- function(all)
- if not all then return false end
+ function()
unreproducible = true
return true
end,
@@ -1027,10 +1141,10 @@ local options = {
{
"^(%-%-flash%-drive%=(.+))$",
function(all, opts)
- if not opts then return false end
local f = util.parse_options(opts, {
label = true,
- filename = true,
+ data_filename = true,
+ dht_filename = true,
shared = true,
mount = true,
user = true,
@@ -1038,13 +1152,12 @@ local options = {
start = true,
})
assert(f.label, "missing flash drive label in " .. all)
- f.image_filename = f.filename
- f.filename = nil
- if f.image_filename == true then f.image_filename = "" end
+ if f.data_filename == true then f.data_filename = "" end
+ if f.dht_filename == true then f.dht_filename = "" end
assert(not f.shared or f.shared == true, "invalid flash drive shared value in " .. all)
if f.mount == nil then
-- mount only if there is a file backing
- if f.image_filename and f.image_filename ~= "" then
+ if f.data_filename and f.data_filename ~= "" then
f.mount = "/mnt/" .. f.label
else
f.mount = false
@@ -1057,11 +1170,12 @@ local options = {
if f.start then f.start = assert(util.parse_number(f.start), "invalid flash drive start in " .. all) end
if f.length then f.length = assert(util.parse_number(f.length), "invalid flash drive length in " .. all) end
local d = f.label
- if not flash_image_filename[d] then
+ if not flash_data_filename[d] then
flash_label_order[#flash_label_order + 1] = d
- flash_image_filename[d] = ""
+ flash_data_filename[d] = ""
end
- flash_image_filename[d] = f.image_filename or flash_image_filename[d]
+ flash_data_filename[d] = f.data_filename or flash_data_filename[d]
+ flash_dht_filename[d] = f.dht_filename or flash_dht_filename[d]
flash_start[d] = f.start or flash_start[d]
flash_length[d] = f.length or flash_length[d]
flash_shared[d] = f.shared or flash_shared[d]
@@ -1073,7 +1187,6 @@ local options = {
{
"^(%-%-replace%-flash%-drive%=(.+))$",
function(all, opts)
- if not opts then return false end
memory_range_replace[#memory_range_replace + 1] = parse_memory_range(opts, "flash drive", all)
return true
end,
@@ -1081,15 +1194,13 @@ local options = {
{
"^(%-%-replace%-memory%-range%=(.+))$",
function(all, opts)
- if not opts then return false end
- memory_range_replace[#memory_range_replace + 1] = parse_memory_range(opts, "flash drive", all)
+ memory_range_replace[#memory_range_replace + 1] = parse_memory_range(opts, "memory range", all)
return true
end,
},
{
"^(%-%-cmio%-advance%-state%=(.+))$",
function(all, opts)
- if not opts then return false end
local r = util.parse_options(opts, {
input = true,
input_index_begin = true,
@@ -1114,9 +1225,8 @@ local options = {
end,
},
{
- "^(%-%-cmio%-inspect%-state%=(.+))$",
- function(_, opts)
- if not opts then return false end
+ "^%-%-cmio%-inspect%-state%=(.+)$",
+ function(opts)
local r = util.parse_options(opts, {
query = true,
report = true,
@@ -1142,7 +1252,6 @@ local options = {
{
"^(%-%-concurrency%=(.+))$",
function(all, opts)
- if not opts then return false end
local c = util.parse_options(opts, {
update_merkle_tree = true,
})
@@ -1187,7 +1296,6 @@ local options = {
{
"^(%-%-initial%-proof%=(.+))$",
function(all, opts)
- if not opts then return false end
local p = util.parse_options(opts, {
address = true,
log2_size = true,
@@ -1222,13 +1330,13 @@ local options = {
"^%-%-no%-root%-flash%-drive$",
function(all)
if not all then return false end
- assert(flash_image_filename.root and flash_label_order[1] == "root", "no root flash drive to remove")
- flash_image_filename.root = nil
+ assert(flash_data_filename.root and flash_label_order[1] == "root", "no root flash drive to remove")
+ flash_data_filename.root = nil
flash_start.root = nil
flash_length.root = nil
flash_shared.root = nil
table.remove(flash_label_order, 1)
- bootargs = bootargs:gsub(" root=$", "")
+ dtb.bootargs = dtb.bootargs:gsub(" root=$", "")
return true
end,
},
@@ -1307,41 +1415,41 @@ local options = {
},
{
"^%-%-load%=(.*)$",
- function(o)
- if not o or #o < 1 then return false end
- load_dir = o
+ function(opts)
+ if not opts or #opts < 1 then return false end
+ load_dir = opts
return true
end,
},
{
"^%-%-store%=(.*)$",
- function(o)
- if not o or #o < 1 then return false end
- store_dir = o
+ function(opts)
+ if not opts or #opts < 1 then return false end
+ store_dir = opts
return true
end,
},
{
"^%-%-remote%-spawn$",
- function(o)
- if not o then return false end
+ function(opts)
+ if not opts then return false end
remote_spawn = true
return true
end,
},
{
"^%-%-remote%-address%=(.*)$",
- function(o)
- if not o or #o < 1 then return false end
- remote_address = o
+ function(opts)
+ if not opts or #opts < 1 then return false end
+ remote_address = opts
return true
end,
},
{
"^%-%-remote%-fork(%=?)(.*)$",
- function(o, v)
- if not o then return false end
- if o == "=" then
+ function(opts, v)
+ if not opts then return false end
+ if opts == "=" then
if not v or #v < 1 then return false end
remote_fork = v
elseif #v ~= 0 then
@@ -1354,40 +1462,40 @@ local options = {
},
{
"^%-%-remote%-health%-check$",
- function(o)
- if not o then return false end
+ function(opts)
+ if not opts then return false end
remote_health_check = true
return true
end,
},
{
"^%-%-remote%-shutdown$",
- function(o)
- if not o then return false end
+ function(opts)
+ if not opts then return false end
remote_shutdown = true
return true
end,
},
{
"^%-%-no%-remote%-create$",
- function(o)
- if not o then return false end
+ function(opts)
+ if not opts then return false end
remote_create = false
return true
end,
},
{
"^%-%-no%-remote%-destroy$",
- function(o)
- if not o then return false end
+ function(opts)
+ if not opts then return false end
remote_destroy = false
return true
end,
},
{
"^%-%-no%-rollback$",
- function(o)
- if not o then return false end
+ function(opts)
+ if not opts then return false end
perform_rollbacks = false
return true
end,
@@ -1427,9 +1535,9 @@ local options = {
},
{
"^%-%-store%-config(%=?)(%g*)$",
- function(o, v)
- if not o then return false end
- if o == "=" then
+ function(opts, v)
+ if not opts then return false end
+ if opts == "=" then
if not v or #v < 1 then return false end
store_config = v
elseif #v ~= 0 then
@@ -1442,9 +1550,9 @@ local options = {
},
{
"^%-%-store%-json%-config(%=?)(%g*)$",
- function(o, v)
- if not o then return false end
- if o == "=" then
+ function(opts, v)
+ if not opts then return false end
+ if opts == "=" then
if not v or #v < 1 then return false end
store_json_config = v
elseif #v ~= 0 then
@@ -1457,17 +1565,17 @@ local options = {
},
{
"^%-%-load%-config%=(%g*)$",
- function(o)
- if not o or #o < 1 then return false end
- load_config = o
+ function(opts)
+ if not opts or #opts < 1 then return false end
+ load_config = opts
return true
end,
},
{
"^%-%-load%-json%-config%=(%g*)$",
- function(o)
- if not o or #o < 1 then return false end
- load_json_config = o
+ function(opts)
+ if not opts or #opts < 1 then return false end
+ load_json_config = opts
return true
end,
},
@@ -1475,8 +1583,8 @@ local options = {
"^(%-%-cmio%-rx%-buffer%=(.+))$",
function(all, opts)
if not opts then return false end
- cmio = cmio or {}
- cmio.rx_buffer = parse_cmio_buffer(opts, "cmio rx buffer", all)
+ cmio.rx_buffer.backing_store =
+ parse_backing_store(opts, "cmio rx buffer", all, cmio.rx_buffer.backing_store)
return true
end,
},
@@ -1484,8 +1592,8 @@ local options = {
"^(%-%-cmio%-tx%-buffer%=(.+))$",
function(all, opts)
if not opts then return false end
- cmio = cmio or {}
- cmio.tx_buffer = parse_cmio_buffer(opts, "tx buffer", all)
+ cmio.tx_buffer.backing_store =
+ parse_backing_store(opts, "cmio tx buffer", all, cmio.tx_buffer.backing_store)
return true
end,
},
@@ -1498,51 +1606,48 @@ local options = {
end,
},
{
- "^%-u%=(.*)$",
+ "^%-u%=(.+)$",
handle_user,
},
{
- "^%-%-user%=(.*)$",
+ "^%-%-user%=(.+)$",
handle_user,
},
{
- "^%-e%=([%w_]+)%=(.*)$",
+ "^%-e%=([%w_]+)%=(.+)$",
handle_env,
},
{
- "^%-%-env%=([%w_]+)%=(.*)$",
+ "^%-%-env%=([%w_]+)%=(.+)$",
handle_env,
},
{
- "^%-w%=(.*)$",
+ "^%-w%=(.+)$",
handle_workdir,
},
{
- "^%-%-workdir%=(.*)$",
+ "^%-%-workdir%=(.+)$",
handle_workdir,
},
{
- "^%-h%=(.*)$",
+ "^%-h%=(.+)$",
handle_hostname,
},
{
- "^%-%-hostname%=(.*)$",
+ "^%-%-hostname%=(.+)$",
handle_hostname,
},
{
- "^%-%-append%-init%=(.*)$",
- function(o)
- if not o then return false end
- if #o == 0 then return true end
- append_init = append_init .. o .. "\n"
+ "^%-%-append%-init%=(.+)$",
+ function(opts)
+ append_init = append_init .. opts .. "\n"
return true
end,
},
{
- "^%-%-append%-init%-file%=(.*)$",
- function(o)
- if not o or #o < 1 then return false end
- local f = assert(io.open(o, "rb"))
+ "^%-%-append%-init%-file%=(.+)$",
+ function(opts)
+ local f = assert(io.open(opts, "rb"))
local contents = assert(f:read("*a"))
if not contents:find("\n$") then contents = contents .. "\n" end
append_init = append_init .. contents
@@ -1550,19 +1655,16 @@ local options = {
end,
},
{
- "^%-%-append%-entrypoint%=(.*)$",
- function(o)
- if not o then return false end
- if #o == 0 then return true end
- append_entrypoint = append_entrypoint .. o .. "\n"
+ "^%-%-append%-entrypoint%=(.+)$",
+ function(opts)
+ append_entrypoint = append_entrypoint .. opts .. "\n"
return true
end,
},
{
- "^%-%-append%-entrypoint%-file%=(.*)$",
- function(o)
- if not o or #o < 1 then return false end
- local f = assert(io.open(o, "rb"))
+ "^%-%-append%-entrypoint%-file%=(.+)$",
+ function(opts)
+ local f = assert(io.open(opts, "rb"))
local contents = assert(f:read("*a"))
if not contents:find("\n$") then contents = contents .. "\n" end
append_entrypoint = append_entrypoint .. contents
@@ -1571,11 +1673,11 @@ local options = {
},
{
"^%-%-gdb(%=?)(.*)$",
- function(o, address)
- if o == "=" and #o > 0 then
+ function(eq, address)
+ if eq == "=" and address ~= "" then
gdb_address = address
return true
- elseif o == "" then
+ elseif eq == "" and address == "" then
gdb_address = "127.0.0.1:1234"
return true
end
@@ -1585,7 +1687,6 @@ local options = {
{
".*",
function(all)
- if not all then return false end
local not_option = all:sub(1, 1) ~= "-"
if not_option or all == "--" then
cmdline_opts_finished = true
@@ -1597,11 +1698,15 @@ local options = {
},
}
+local function tryoption(handler, ...)
+ if select(1, ...) ~= nil then return handler(...) end
+end
+
-- Process command line options
for _, a in ipairs(arg) do
if not cmdline_opts_finished then
for _, option in ipairs(options) do
- if option[2](a:match(option[1])) then break end
+ if tryoption(option[2], a:match(option[1])) then break end
end
else
exec_arguments[#exec_arguments + 1] = a
@@ -1697,25 +1802,20 @@ else
processor = {
iunrep = unreproducible and 1 or 0,
},
- dtb = {
- image_filename = dtb_image_filename,
- bootargs = bootargs,
- init = "",
- entrypoint = "",
- },
- ram = {
- image_filename = ram_image_filename,
- length = ram_length,
- },
+ ram = ram,
+ dtb = dtb,
+ flash_drive = {},
+ tlb = tlb,
htif = {
console_getchar = htif_console_getchar,
yield_automatic = htif_yield_automatic,
yield_manual = htif_yield_manual,
},
+ virtio = virtio,
cmio = cmio,
+ pmas = pmas,
uarch = uarch,
- flash_drive = {},
- virtio = virtio,
+ hash_tree = hash_tree,
}
-- show splash on init
@@ -1739,8 +1839,11 @@ echo "
for _, label in ipairs(flash_label_order) do
local devname = "pmem" .. #config.flash_drive
config.flash_drive[#config.flash_drive + 1] = {
- image_filename = flash_image_filename[label],
- shared = flash_shared[label],
+ backing_store = {
+ data_filename = flash_data_filename[label],
+ dht_filename = flash_dht_filename[label],
+ shared = flash_shared[label],
+ },
start = flash_start[label],
length = flash_length[label] or -1,
}
diff --git a/src/clint.cpp b/src/clint-address-range.cpp
similarity index 76%
rename from src/clint.cpp
rename to src/clint-address-range.cpp
index 55dbfa200..9ffeb0416 100644
--- a/src/clint.cpp
+++ b/src/clint-address-range.cpp
@@ -14,21 +14,36 @@
// with this program (see COPYING). If not, see .
//
-#include "clint.h"
+#include "clint-address-range.h"
#include
#include "i-device-state-access.h"
#include "interpret.h"
-#include "pma-constants.h"
-#include "pma-driver.h"
+#include "pmas-constants.h"
#include "riscv-constants.h"
#include "rtc.h"
namespace cartesi {
+/// \brief Mapping between CSRs and their relative addresses in CLINT memory
+enum class clint_csr {
+ msip0 = UINT64_C(0), // Machine software interrupt for hart 0
+ mtimecmp = UINT64_C(0x4000),
+ mtime = UINT64_C(0xbff8)
+};
+
+/// \brief Obtains the relative address of the msip0 CSR in HTIF memory.
+static constexpr auto clint_msip0_rel_addr = static_cast(clint_csr::msip0);
+
+/// \brief Obtains the relative address of the mtime CSR in HTIF memory.
+static constexpr auto clint_mtime_rel_addr = static_cast(clint_csr::mtime);
+
+/// \brief Obtains the relative address of the mtimecmp CSR in HTIF memory.
+constexpr auto clint_mtimecmp_rel_addr = static_cast(clint_csr::mtimecmp);
+
static constexpr uint64_t base(uint64_t v) {
- return v - (v % PMA_PAGE_SIZE);
+ return v - (v % AR_PAGE_SIZE);
}
static bool clint_read_msip(i_device_state_access *a, uint64_t *val, int log2_size) {
@@ -61,8 +76,8 @@ static bool clint_read_mtimecmp(i_device_state_access *a, uint64_t *val, int log
return false;
}
-/// \brief CLINT device read callback. See ::pma_read.
-static bool clint_read(void * /*context*/, i_device_state_access *a, uint64_t offset, uint64_t *val, int log2_size) {
+bool clint_address_range::do_read_device(i_device_state_access *a, uint64_t offset, int log2_size,
+ uint64_t *val) const noexcept {
switch (offset) {
case clint_msip0_rel_addr:
return clint_read_msip(a, val, log2_size);
@@ -76,9 +91,8 @@ static bool clint_read(void * /*context*/, i_device_state_access *a, uint64_t of
}
}
-/// \brief CLINT device read callback. See ::pma_write.
-static execute_status clint_write(void * /*context*/, i_device_state_access *a, uint64_t offset, uint64_t val,
- int log2_size) {
+execute_status clint_address_range::do_write_device(i_device_state_access *a, uint64_t offset, int log2_size,
+ uint64_t val) noexcept {
switch (offset) {
case clint_msip0_rel_addr:
if (log2_size == 2) {
@@ -108,6 +122,4 @@ static execute_status clint_write(void * /*context*/, i_device_state_access *a,
}
}
-const pma_driver clint_driver = {.name = "CLINT", .read = clint_read, .write = clint_write};
-
} // namespace cartesi
diff --git a/src/clint-address-range.h b/src/clint-address-range.h
new file mode 100644
index 000000000..3a785a749
--- /dev/null
+++ b/src/clint-address-range.h
@@ -0,0 +1,70 @@
+// 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 CLINT_ADDRESS_RANGE_H
+#define CLINT_ADDRESS_RANGE_H
+
+#include
+
+#include "address-range-constants.h"
+#include "pristine-address-range.h"
+
+/// \file
+/// \brief Core-Local Interruptor device.
+
+namespace cartesi {
+
+class clint_address_range final : public pristine_address_range {
+
+ static constexpr pmas_flags m_clint_flags{
+ .M = false,
+ .IO = true,
+ .R = true,
+ .W = true,
+ .X = false,
+ .IR = false,
+ .IW = false,
+ .DID = PMA_ISTART_DID::CLINT,
+ };
+
+public:
+ template
+ explicit clint_address_range(ABRT abrt) :
+ pristine_address_range("CLINT device", AR_CLINT_START, AR_CLINT_LENGTH, m_clint_flags, abrt) {
+ ;
+ }
+
+ clint_address_range(const clint_address_range &other) = default;
+ clint_address_range &operator=(const clint_address_range &other) = default;
+ clint_address_range(clint_address_range &&other) = default;
+ clint_address_range &operator=(clint_address_range &&other) = default;
+ ~clint_address_range() override = default;
+
+private:
+ bool do_read_device(i_device_state_access *a, uint64_t offset, int log2_size,
+ uint64_t *pval) const noexcept override;
+ execute_status do_write_device(i_device_state_access *a, uint64_t offset, int log2_size,
+ uint64_t val) noexcept override;
+};
+
+template
+static inline clint_address_range make_clint_address_range(ABRT abrt) {
+ return clint_address_range{abrt};
+}
+
+} // namespace cartesi
+
+#endif
diff --git a/src/clint-factory.cpp b/src/clint-factory.cpp
deleted file mode 100644
index 0dd9ba3e7..000000000
--- a/src/clint-factory.cpp
+++ /dev/null
@@ -1,32 +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 "clint-factory.h"
-
-#include
-
-#include "clint.h"
-#include "pma-constants.h"
-#include "pma.h"
-
-namespace cartesi {
-
-pma_entry make_clint_pma_entry(uint64_t start, uint64_t length) {
- const pma_entry::flags f{.R = true, .W = true, .X = false, .IR = false, .IW = false, .DID = PMA_ISTART_DID::CLINT};
- return make_device_pma_entry("CLINT device", start, length, pma_peek_pristine, &clint_driver).set_flags(f);
-}
-
-} // namespace cartesi
diff --git a/src/clint-factory.h b/src/clint-factory.h
deleted file mode 100644
index 1910bfd0e..000000000
--- a/src/clint-factory.h
+++ /dev/null
@@ -1,34 +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 .
-//
-
-#ifndef CLINT_FACTORY_H
-#define CLINT_FACTORY_H
-
-#include
-
-#include "pma.h"
-
-namespace cartesi {
-
-/// \brief Creates a PMA entry for the CLINT device
-/// \param start Start address for memory range.
-/// \param length Length of memory range.
-/// \returns Corresponding PMA entry
-pma_entry make_clint_pma_entry(uint64_t start, uint64_t length);
-
-} // namespace cartesi
-
-#endif
diff --git a/src/clint.h b/src/clint.h
deleted file mode 100644
index 04d228885..000000000
--- a/src/clint.h
+++ /dev/null
@@ -1,50 +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 .
-//
-
-#ifndef CLINT_H
-#define CLINT_H
-
-#include
-
-#include "pma-driver.h"
-
-/// \file
-/// \brief Clock interruptor device.
-
-namespace cartesi {
-
-/// \brief Global CLINT device driver instance
-extern const pma_driver clint_driver;
-
-/// \brief Mapping between CSRs and their relative addresses in CLINT memory
-enum class clint_csr {
- msip0 = UINT64_C(0), // Machine software interrupt for hart 0
- mtimecmp = UINT64_C(0x4000),
- mtime = UINT64_C(0xbff8)
-};
-
-/// \brief Obtains the relative address of the msip0 CSR in HTIF memory.
-static constexpr auto clint_msip0_rel_addr = static_cast(clint_csr::msip0);
-
-/// \brief Obtains the relative address of the mtime CSR in HTIF memory.
-static constexpr auto clint_mtime_rel_addr = static_cast(clint_csr::mtime);
-
-/// \brief Obtains the relative address of the mtimecmp CSR in HTIF memory.
-constexpr auto clint_mtimecmp_rel_addr = static_cast(clint_csr::mtimecmp);
-
-} // namespace cartesi
-
-#endif
diff --git a/src/clua-cartesi-jsonrpc.cpp b/src/clua-cartesi-jsonrpc.cpp
index 7034e533c..1116fed96 100644
--- a/src/clua-cartesi-jsonrpc.cpp
+++ b/src/clua-cartesi-jsonrpc.cpp
@@ -21,7 +21,7 @@ extern "C" {
#include
}
-#include "clua-i-virtual-machine.h"
+#include "clua-i-machine.h"
#include "clua.h"
#include "jsonrpc-machine-c-api.h"
#include "machine-c-api.h"
@@ -211,7 +211,7 @@ CM_API int luaopen_cartesi_jsonrpc(lua_State *L) {
clua_init(L); // cluactx
lua_newtable(L); // cluactx jsonrpc
// Initialize and export jsonrpc machine bind
- clua_i_virtual_machine_export(L, -2); // cluactx jsonrpc
+ clua_i_machine_export(L, -2); // cluactx jsonrpc
clua_setmethods>(L, jsonrpc_machine_obj_index.data(), 0, -2); // cluactx jsonrpc
// Set module functions
lua_pushvalue(L, -2); // cluactx jsonrpc cluactx
diff --git a/src/clua-cartesi.cpp b/src/clua-cartesi.cpp
index 232638c87..1326f0806 100644
--- a/src/clua-cartesi.cpp
+++ b/src/clua-cartesi.cpp
@@ -28,7 +28,7 @@ extern "C" {
}
#include "base64.h"
-#include "clua-i-virtual-machine.h"
+#include "clua-i-machine.h"
#include "clua.h"
#include "keccak-256-hasher.h"
#include "machine-c-api.h"
@@ -181,7 +181,7 @@ CM_API int luaopen_cartesi(lua_State *L) {
clua_init(L); // cluactx
lua_newtable(L); // cluactx cartesi
// Initialize and export machine bind
- clua_i_virtual_machine_export(L, -2); // cluactx cartesi
+ clua_i_machine_export(L, -2); // cluactx cartesi
// Set module functions
lua_pushvalue(L, -2); // cluactx cartesi cluactx
luaL_setfuncs(L, cartesi_mod.data(), 1); // cluactx cartesi
@@ -220,11 +220,11 @@ CM_API int luaopen_cartesi(lua_State *L) {
clua_setintegerfield(L, CM_CMIO_YIELD_MANUAL_REASON_TX_EXCEPTION, "CMIO_YIELD_MANUAL_REASON_TX_EXCEPTION", -1);
clua_setintegerfield(L, CM_CMIO_YIELD_REASON_ADVANCE_STATE, "CMIO_YIELD_REASON_ADVANCE_STATE", -1);
clua_setintegerfield(L, CM_CMIO_YIELD_REASON_INSPECT_STATE, "CMIO_YIELD_REASON_INSPECT_STATE", -1);
- clua_setintegerfield(L, CM_PMA_CMIO_RX_BUFFER_START, "PMA_CMIO_RX_BUFFER_START", -1);
- clua_setintegerfield(L, CM_PMA_CMIO_RX_BUFFER_LOG2_SIZE, "PMA_CMIO_RX_BUFFER_LOG2_SIZE", -1);
- clua_setintegerfield(L, CM_PMA_CMIO_TX_BUFFER_START, "PMA_CMIO_TX_BUFFER_START", -1);
- clua_setintegerfield(L, CM_PMA_CMIO_TX_BUFFER_LOG2_SIZE, "PMA_CMIO_TX_BUFFER_LOG2_SIZE", -1);
- clua_setintegerfield(L, CM_PMA_RAM_START, "PMA_RAM_START", -1);
+ clua_setintegerfield(L, CM_AR_CMIO_RX_BUFFER_START, "AR_CMIO_RX_BUFFER_START", -1);
+ clua_setintegerfield(L, CM_AR_CMIO_RX_BUFFER_LOG2_SIZE, "AR_CMIO_RX_BUFFER_LOG2_SIZE", -1);
+ clua_setintegerfield(L, CM_AR_CMIO_TX_BUFFER_START, "AR_CMIO_TX_BUFFER_START", -1);
+ clua_setintegerfield(L, CM_AR_CMIO_TX_BUFFER_LOG2_SIZE, "AR_CMIO_TX_BUFFER_LOG2_SIZE", -1);
+ clua_setintegerfield(L, CM_AR_RAM_START, "AR_RAM_START", -1);
// Set other constants used by internal tests
clua_setintegerfield(L, UARCH_STATE_START_ADDRESS, "UARCH_STATE_START_ADDRESS", -1);
clua_setintegerfield(L, UARCH_STATE_LOG2_SIZE, "UARCH_STATE_LOG2_SIZE", -1);
diff --git a/src/clua-i-virtual-machine.cpp b/src/clua-i-machine.cpp
similarity index 98%
rename from src/clua-i-virtual-machine.cpp
rename to src/clua-i-machine.cpp
index 121a4a944..ca89546f8 100644
--- a/src/clua-i-virtual-machine.cpp
+++ b/src/clua-i-machine.cpp
@@ -14,7 +14,7 @@
// with this program (see COPYING). If not, see .
//
-#include "clua-i-virtual-machine.h"
+#include "clua-i-machine.h"
#include
#include
@@ -687,12 +687,12 @@ static int machine_obj_index_reset_uarch(lua_State *L) {
return 0;
}
-/// \brief This is the machine:get_memory_ranges() method implementation.
+/// \brief This is the machine:get_address_ranges() method implementation.
/// \param L Lua state.
-static int machine_obj_index_get_memory_ranges(lua_State *L) {
+static int machine_obj_index_get_address_ranges(lua_State *L) {
auto &m = clua_check>(L, 1);
const char *ranges = nullptr;
- if (cm_get_memory_ranges(m.get(), &ranges) != 0) {
+ if (cm_get_address_ranges(m.get(), &ranges) != 0) {
return luaL_error(L, "%s", cm_get_last_error_message());
}
clua_push_json_table(L, ranges);
@@ -825,13 +825,10 @@ static int machine_obj_index_translate_virtual_address(lua_State *L) {
/// \brief Replaces a memory range.
/// \param L Lua state.
static int machine_obj_index_replace_memory_range(lua_State *L) {
- lua_settop(L, 5);
+ lua_settop(L, 2);
auto &m = clua_check>(L, 1);
- const uint64_t start = luaL_checkinteger(L, 2);
- const uint64_t length = luaL_checkinteger(L, 3);
- const bool shared = lua_toboolean(L, 4) != 0;
- const char *image_filename = luaL_optstring(L, 5, nullptr);
- if (cm_replace_memory_range(m.get(), start, length, shared, image_filename) != 0) {
+ const char *range_config = clua_check_json_string(L, 2);
+ if (cm_replace_memory_range(m.get(), range_config) != 0) {
return luaL_error(L, "%s", cm_get_last_error_message());
}
return 0;
@@ -1054,7 +1051,7 @@ static const auto machine_obj_index = cartesi::clua_make_luaL_Reg_array({
{"destroy", machine_obj_index_destroy},
{"get_default_config", machine_obj_index_get_default_config},
{"get_initial_config", machine_obj_index_get_initial_config},
- {"get_memory_ranges", machine_obj_index_get_memory_ranges},
+ {"get_address_ranges", machine_obj_index_get_address_ranges},
{"get_proof", machine_obj_index_get_proof},
{"get_reg_address", machine_obj_index_get_reg_address},
{"get_root_hash", machine_obj_index_get_root_hash},
@@ -1125,7 +1122,7 @@ static const auto machine_meta = cartesi::clua_make_luaL_Reg_array({
{"__call", machine_meta_call},
});
-int clua_i_virtual_machine_init(lua_State *L, int ctxidx) {
+int clua_i_machine_init(lua_State *L, int ctxidx) {
clua_createnewtype>(L, ctxidx);
clua_createnewtype>(L, ctxidx);
clua_createnewtype>(L, ctxidx);
@@ -1137,8 +1134,8 @@ int clua_i_virtual_machine_init(lua_State *L, int ctxidx) {
return 0;
}
-int clua_i_virtual_machine_export(lua_State *L, int ctxidx) {
- clua_i_virtual_machine_init(L, ctxidx);
+int clua_i_machine_export(lua_State *L, int ctxidx) {
+ clua_i_machine_init(L, ctxidx);
return 0;
}
diff --git a/src/clua-i-virtual-machine.h b/src/clua-i-machine.h
similarity index 97%
rename from src/clua-i-virtual-machine.h
rename to src/clua-i-machine.h
index 1361c5b12..4d9951cd6 100644
--- a/src/clua-i-virtual-machine.h
+++ b/src/clua-i-machine.h
@@ -14,15 +14,15 @@
// with this program (see COPYING). If not, see .
//
-#ifndef CLUA_I_VIRTUAL_MACHINE_H
-#define CLUA_I_VIRTUAL_MACHINE_H
+#ifndef CLUA_I_MACHINE_H
+#define CLUA_I_MACHINE_H
#include
#include
#include
#include "clua.h"
-#include "i-virtual-machine.h"
+#include "i-machine.h"
#include "json-util.h"
#include "machine-c-api.h"
@@ -34,12 +34,12 @@ namespace cartesi {
/// \brief Initialize Cartesi machine Lua interface
/// \param L Lua state
/// \param ctxidx Index of Clua context
-int clua_i_virtual_machine_init(lua_State *L, int ctxidx);
+int clua_i_machine_init(lua_State *L, int ctxidx);
/// \brief Exports symbols to table on top of Lua stack
/// \param L Lua state
/// \param ctxidx Index of Clua context
-int clua_i_virtual_machine_export(lua_State *L, int ctxidx);
+int clua_i_machine_export(lua_State *L, int ctxidx);
/// \brief Create overloaded deleters for C API objects
template
diff --git a/src/dtb.cpp b/src/dtb.cpp
index 5d0914896..7024fd3ad 100644
--- a/src/dtb.cpp
+++ b/src/dtb.cpp
@@ -24,10 +24,12 @@
#include
#include
+#include "address-range-constants.h"
#include "fdt-builder.h"
#include "machine-c-version.h"
#include "machine-config.h"
-#include "pma-constants.h"
+#include "plic-constants.h"
+#include "pmas-constants.h"
#include "riscv-constants.h"
#include "rng-seed.h"
#include "rtc.h"
@@ -119,38 +121,38 @@ void dtb_init(const machine_config &c, unsigned char *dtb_start, uint64_t dtb_le
fdt.prop_string("compatible", "ucbbar,riscvemu-bar-soc\0simple-bus"s);
fdt.prop_empty("ranges");
{ // clint
- fdt.begin_node_num("clint", PMA_CLINT_START);
+ fdt.begin_node_num("clint", AR_CLINT_START);
fdt.prop_string("compatible", "riscv,clint0");
- fdt.prop_u64_list<2>("reg", {PMA_CLINT_START, PMA_CLINT_LENGTH});
+ fdt.prop_u64_list<2>("reg", {AR_CLINT_START, AR_CLINT_LENGTH});
fdt.prop_u32_list<4>("interrupts-extended",
{INTC_PHANDLE, MIP_MSIP_SHIFT, INTC_PHANDLE, MIP_MTIP_SHIFT});
fdt.end_node();
}
{ // plic
- fdt.begin_node_num("plic", PMA_PLIC_START);
+ fdt.begin_node_num("plic", AR_PLIC_START);
fdt.prop_u32("#interrupt-cells", 1);
fdt.prop_empty("interrupt-controller");
fdt.prop_string("compatible", "riscv,plic0");
- fdt.prop_u32("riscv,ndev", PMA_PLIC_MAX_IRQ);
- fdt.prop_u64_list<2>("reg", {PMA_PLIC_START, PMA_PLIC_LENGTH});
+ fdt.prop_u32("riscv,ndev", PLIC_MAX_IRQ);
+ fdt.prop_u64_list<2>("reg", {AR_PLIC_START, AR_PLIC_LENGTH});
fdt.prop_u32_list<4>("interrupts-extended",
{INTC_PHANDLE, MIP_SEIP_SHIFT, INTC_PHANDLE, MIP_MEIP_SHIFT});
fdt.prop_u32("phandle", PLIC_PHANDLE);
fdt.end_node();
}
{ // htif
- fdt.begin_node_num("htif", PMA_HTIF_START);
+ fdt.begin_node_num("htif", AR_HTIF_START);
fdt.prop_string("compatible", "ucb,htif0");
- fdt.prop_u64_list<2>("reg", {PMA_HTIF_START, PMA_HTIF_LENGTH});
+ fdt.prop_u64_list<2>("reg", {AR_HTIF_START, AR_HTIF_LENGTH});
fdt.prop_u32_list<2>("interrupts-extended", {INTC_PHANDLE, X_HOST});
fdt.end_node();
}
for (uint32_t virtio_idx = 0; virtio_idx < c.virtio.size(); ++virtio_idx) { // virtio
- const uint64_t virtio_paddr = PMA_FIRST_VIRTIO_START + (virtio_idx * PMA_VIRTIO_LENGTH);
+ const uint64_t virtio_paddr = AR_FIRST_VIRTIO_START + (virtio_idx * AR_VIRTIO_LENGTH);
const uint32_t plic_irq_id = virtio_idx + 1;
fdt.begin_node_num("virtio", virtio_paddr);
fdt.prop_string("compatible", "virtio,mmio");
- fdt.prop_u64_list<2>("reg", {virtio_paddr, PMA_VIRTIO_LENGTH});
+ fdt.prop_u64_list<2>("reg", {virtio_paddr, AR_VIRTIO_LENGTH});
fdt.prop_u32_list<2>("interrupts-extended", {PLIC_PHANDLE, plic_irq_id});
fdt.end_node();
}
@@ -158,9 +160,9 @@ void dtb_init(const machine_config &c, unsigned char *dtb_start, uint64_t dtb_le
}
{ // memory
- fdt.begin_node_num("memory", PMA_RAM_START);
+ fdt.begin_node_num("memory", AR_RAM_START);
fdt.prop_string("device_type", "memory");
- fdt.prop_u64_list<2>("reg", {PMA_RAM_START, c.ram.length});
+ fdt.prop_u64_list<2>("reg", {AR_RAM_START, c.ram.length});
fdt.end_node();
}
@@ -170,8 +172,8 @@ void dtb_init(const machine_config &c, unsigned char *dtb_start, uint64_t dtb_le
fdt.prop_u32("#size-cells", 2);
fdt.prop_empty("ranges");
{ // reserve 256KB for firmware M-mode code (such as OpenSBI)
- fdt.begin_node_num("fw_resv", PMA_RAM_START);
- fdt.prop_u64_list<2>("reg", {PMA_RAM_START, 0x40000});
+ fdt.begin_node_num("fw_resv", AR_RAM_START);
+ fdt.prop_u64_list<2>("reg", {AR_RAM_START, 0x40000});
fdt.prop_empty("no-map");
fdt.end_node();
}
@@ -193,13 +195,13 @@ void dtb_init(const machine_config &c, unsigned char *dtb_start, uint64_t dtb_le
fdt.prop_u32("#size-cells", 2);
fdt.prop_string("compatible", "ctsi-cmio");
{ // rx_buffer
- fdt.begin_node_num("rx_buffer", PMA_CMIO_RX_BUFFER_START);
- fdt.prop_u64_list<2>("reg", {PMA_CMIO_RX_BUFFER_START, PMA_CMIO_RX_BUFFER_LENGTH});
+ fdt.begin_node_num("rx_buffer", AR_CMIO_RX_BUFFER_START);
+ fdt.prop_u64_list<2>("reg", {AR_CMIO_RX_BUFFER_START, AR_CMIO_RX_BUFFER_LENGTH});
fdt.end_node();
}
{ // tx_buffer
- fdt.begin_node_num("tx_buffer", PMA_CMIO_TX_BUFFER_START);
- fdt.prop_u64_list<2>("reg", {PMA_CMIO_TX_BUFFER_START, PMA_CMIO_TX_BUFFER_LENGTH});
+ fdt.begin_node_num("tx_buffer", AR_CMIO_TX_BUFFER_START);
+ fdt.prop_u64_list<2>("reg", {AR_CMIO_TX_BUFFER_START, AR_CMIO_TX_BUFFER_LENGTH});
fdt.end_node();
}
fdt.end_node();
diff --git a/src/find-pma-entry.h b/src/find-pma.h
similarity index 62%
rename from src/find-pma-entry.h
rename to src/find-pma.h
index 1cb857ca4..9a3135d3f 100644
--- a/src/find-pma-entry.h
+++ b/src/find-pma.h
@@ -14,15 +14,17 @@
// with this program (see COPYING). If not, see .
//
-#ifndef FIND_PMA_ENTRY_H
-#define FIND_PMA_ENTRY_H
+#ifndef FIND_PMA_H
+#define FIND_PMA_H
-#include "compiler-defines.h"
#include
+#include "address-range.h"
+#include "compiler-defines.h"
+
namespace cartesi {
-/// \brief Returns PMAs entry where a word falls.
+/// \brief Returns address range associated to the PMA 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.
@@ -30,41 +32,34 @@ namespace cartesi {
/// \param index Receives index where PMA entry was found.
/// \returns PMA entry where word falls, or empty sentinel.
template
-auto &find_pma_entry(STATE_ACCESS &a, uint64_t paddr, uint64_t &index) {
- [[maybe_unused]] auto note = a.make_scoped_note("find_pma_entry");
+address_range &find_pma(const STATE_ACCESS a, uint64_t paddr, uint64_t &index) {
+ [[maybe_unused]] auto note = a.make_scoped_note("find_pma");
index = 0;
while (true) {
- auto &pma = a.read_pma_entry(index);
- const auto length = pma.get_length();
+ auto &ar = a.read_pma(index);
// 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;
+ if (unlikely(ar.is_empty())) {
+ return ar;
}
- // 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;
+ if (ar.contains_absolute(paddr, sizeof(T))) {
+ return ar;
}
++index;
}
}
-/// \brief Returns PMAs entry where a word falls.
+/// \brief Returns address range associated to the PMA 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
-FORCE_INLINE auto &find_pma_entry(STATE_ACCESS &a, uint64_t paddr) {
+FORCE_INLINE auto &find_pma(const STATE_ACCESS a, uint64_t paddr) {
uint64_t index = 0;
- return find_pma_entry(a, paddr, index);
+ return find_pma(a, paddr, index);
}
} // namespace cartesi
diff --git a/src/htif.cpp b/src/htif-address-range.cpp
similarity index 91%
rename from src/htif.cpp
rename to src/htif-address-range.cpp
index 1a17d1e02..4af6019ce 100644
--- a/src/htif.cpp
+++ b/src/htif-address-range.cpp
@@ -14,13 +14,12 @@
// with this program (see COPYING). If not, see .
//
-#include "htif.h"
+#include "htif-address-range.h"
#include
+#include "htif-constants.h"
#include "i-device-state-access.h"
-#include "interpret.h"
-#include "pma-driver.h"
namespace cartesi {
@@ -30,8 +29,8 @@ static constexpr auto htif_ihalt_rel_addr = static_cast(htif_csr::ihal
static constexpr auto htif_iconsole_rel_addr = static_cast(htif_csr::iconsole);
static constexpr auto htif_iyield_rel_addr = static_cast(htif_csr::iyield);
-/// \brief HTIF device read callback. See ::pma_read.
-static bool htif_read(void * /*context*/, i_device_state_access *a, uint64_t offset, uint64_t *pval, int log2_size) {
+bool htif_address_range::do_read_device(i_device_state_access *a, uint64_t offset, int log2_size,
+ uint64_t *pval) const noexcept {
// Our HTIF only supports 64-bit reads
if (log2_size != 3) {
return false;
@@ -129,9 +128,9 @@ static execute_status htif_write_tohost(i_device_state_access *a, uint64_t tohos
}
}
-/// \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,
- int log2_size) {
+execute_status htif_address_range::do_write_device(i_device_state_access *a, uint64_t offset, int log2_size,
+ uint64_t val) noexcept {
+
// Our HTIF only supports 64-bit writes
if (log2_size != 3) {
return execute_status::failure;
@@ -150,6 +149,4 @@ static execute_status htif_write(void * /*context*/, i_device_state_access *a, u
}
}
-const pma_driver htif_driver{.name = "HTIF", .read = htif_read, .write = htif_write};
-
} // namespace cartesi
diff --git a/src/htif-address-range.h b/src/htif-address-range.h
new file mode 100644
index 000000000..c48b27c3e
--- /dev/null
+++ b/src/htif-address-range.h
@@ -0,0 +1,72 @@
+// 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 HTIF_ADDRESS_RANGE_H
+#define HTIF_ADDRESS_RANGE_H
+
+#include
+
+#include "address-range-constants.h"
+#include "i-device-state-access.h"
+#include "pmas-constants.h"
+#include "pristine-address-range.h"
+
+/// \file
+/// \brief Host-Target InterFace device.
+
+namespace cartesi {
+
+class htif_address_range final : public pristine_address_range {
+
+ static constexpr pmas_flags m_htif_flags{
+ .M = false,
+ .IO = true,
+ .R = true,
+ .W = true,
+ .X = false,
+ .IR = false,
+ .IW = false,
+ .DID = PMA_ISTART_DID::HTIF,
+ };
+
+public:
+ template
+ explicit htif_address_range(ABRT abrt) :
+ pristine_address_range("HTIF device", AR_HTIF_START, AR_HTIF_LENGTH, m_htif_flags, abrt) {
+ ;
+ }
+
+ htif_address_range(const htif_address_range &other) = default;
+ htif_address_range &operator=(const htif_address_range &other) = default;
+ htif_address_range(htif_address_range &&other) = default;
+ htif_address_range &operator=(htif_address_range &&other) = default;
+ ~htif_address_range() override = default;
+
+private:
+ bool do_read_device(i_device_state_access *a, uint64_t offset, int log2_size,
+ uint64_t *pval) const noexcept override;
+ execute_status do_write_device(i_device_state_access *a, uint64_t offset, int log2_size,
+ uint64_t val) noexcept override;
+};
+
+template
+static inline htif_address_range make_htif_address_range(ABRT abrt) {
+ return htif_address_range{abrt};
+}
+
+} // namespace cartesi
+
+#endif
diff --git a/src/htif.h b/src/htif-constants.h
similarity index 81%
rename from src/htif.h
rename to src/htif-constants.h
index b4cb6afc5..1a57a13f6 100644
--- a/src/htif.h
+++ b/src/htif-constants.h
@@ -14,23 +14,23 @@
// with this program (see COPYING). If not, see .
//
-#ifndef HTIF_H
-#define HTIF_H
+#ifndef HTIF_CONSTANTS_H
+#define HTIF_CONSTANTS_H
#include
#include "htif-defines.h"
-#include "machine-c-api.h"
-#include "pma-defines.h"
-#include "pma-driver.h"
/// \file
-/// \brief Host-Target interface device.
+/// \brief Host-Target InterFace device.
namespace cartesi {
-/// \brief Global HTIF device driver instance
-extern const pma_driver htif_driver;
+// helper for using UINT64_C with defines
+#ifndef EXPAND_UINT64_C
+// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
+#define EXPAND_UINT64_C(a) UINT64_C(a)
+#endif
// Forward declarations
/// \brief HTIF shifts
@@ -122,15 +122,6 @@ enum class htif_csr {
iyield = UINT64_C(0x20)
};
-static_assert(HTIF_YIELD_AUTOMATIC_REASON_PROGRESS_DEF == CM_CMIO_YIELD_AUTOMATIC_REASON_PROGRESS);
-static_assert(HTIF_YIELD_AUTOMATIC_REASON_TX_OUTPUT_DEF == CM_CMIO_YIELD_AUTOMATIC_REASON_TX_OUTPUT);
-static_assert(HTIF_YIELD_AUTOMATIC_REASON_TX_REPORT_DEF == CM_CMIO_YIELD_AUTOMATIC_REASON_TX_REPORT);
-static_assert(HTIF_YIELD_MANUAL_REASON_RX_ACCEPTED_DEF == CM_CMIO_YIELD_MANUAL_REASON_RX_ACCEPTED);
-static_assert(HTIF_YIELD_MANUAL_REASON_RX_REJECTED_DEF == CM_CMIO_YIELD_MANUAL_REASON_RX_REJECTED);
-static_assert(HTIF_YIELD_MANUAL_REASON_TX_EXCEPTION_DEF == CM_CMIO_YIELD_MANUAL_REASON_TX_EXCEPTION);
-static_assert(HTIF_YIELD_REASON_ADVANCE_STATE_DEF == CM_CMIO_YIELD_REASON_ADVANCE_STATE);
-static_assert(HTIF_YIELD_REASON_INSPECT_STATE_DEF == CM_CMIO_YIELD_REASON_INSPECT_STATE);
-
} // namespace cartesi
#endif
diff --git a/src/htif-factory.cpp b/src/htif-factory.cpp
deleted file mode 100644
index f09b2fb39..000000000
--- a/src/htif-factory.cpp
+++ /dev/null
@@ -1,32 +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 "htif-factory.h"
-
-#include
-
-#include "htif.h"
-#include "pma-constants.h"
-#include "pma.h"
-
-namespace cartesi {
-
-pma_entry make_htif_pma_entry(uint64_t start, uint64_t length) {
- const pma_entry::flags f{.R = true, .W = true, .X = false, .IR = false, .IW = false, .DID = PMA_ISTART_DID::HTIF};
- return make_device_pma_entry("HTIF device", start, length, pma_peek_pristine, &htif_driver).set_flags(f);
-}
-
-} // namespace cartesi
diff --git a/src/htif-factory.h b/src/htif-factory.h
deleted file mode 100644
index 1bba5844c..000000000
--- a/src/htif-factory.h
+++ /dev/null
@@ -1,31 +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 .
-//
-
-#ifndef HTIF_FACTORY_H
-#define HTIF_FACTORY_H
-
-#include
-
-#include "pma.h"
-
-namespace cartesi {
-
-/// \brief Creates a PMA entry for the HTIF device
-pma_entry make_htif_pma_entry(uint64_t start, uint64_t length);
-
-} // namespace cartesi
-
-#endif
diff --git a/src/i-accept-scoped-notes.h b/src/i-accept-scoped-notes.h
index affc19c3e..1c1220269 100644
--- a/src/i-accept-scoped-notes.h
+++ b/src/i-accept-scoped-notes.h
@@ -20,10 +20,11 @@
/// \file
/// \brief Accept scoped notes interface
+#include
#include
#include
-#include "dump.h"
+#include "assert-printf.h"
#include "i-state-access.h"
#include "i-uarch-state-access.h"
#include "meta.h"
@@ -48,32 +49,43 @@ class i_accept_scoped_notes { // CRTP
}
public:
- /// \brief Works as printf if we are dumping scoped notes, otherwise does nothing
- template
- static void DSN_PRINTF([[maybe_unused]] const char (&fmt)[N], [[maybe_unused]] ARGS... args) {
+ /// \brief Works as vprintf if we are dumping scoped notes, otherwise does nothing
+ static void dsn_vprintf([[maybe_unused]] const char *fmt, [[maybe_unused]] va_list ap) {
#ifdef DUMP_SCOPED_NOTE
if constexpr (is_an_i_state_access_v) {
- DERIVED::DSA_PRINTF(fmt, args...);
+ DERIVED::dsa_vprintf(fmt, ap);
} else if (is_an_i_uarch_state_access_v) {
- DERIVED::DUSA_PRINTF(fmt, args...);
+ DERIVED::dusa_vprintf(fmt, ap);
} else {
- D_PRINTF(fmt, args...);
+ d_vprintf(fmt, ap);
}
#endif
}
+ /// \brief Works as printf if we are dumping scoped notes, otherwise does nothing
+ // Better to use C-style variadic function that checks for format!
+ // NOLINTNEXTLINE(cert-dcl50-cpp)
+ __attribute__((__format__(__printf__, 1, 2))) static void dsn_printf([[maybe_unused]] const char *fmt, ...) {
+#ifdef DUMP_SCOPED_NOTE
+ va_list ap;
+ va_start(ap, fmt);
+ dsn_vprintf(fmt, ap);
+ va_end(ap);
+#endif
+ }
+
/// \brief Adds a begin bracket annotation to the log
/// \param text String with the text for the annotation
void push_begin_bracket(const char *text) const {
derived().do_push_begin_bracket(text);
- DSN_PRINTF("----> begin %s (%s)\n", text, derived().get_name());
+ dsn_printf("----> begin %s (%s)\n", text, derived().get_name());
}
/// \brief Adds an end bracket annotation to the log
/// \param text String with the text for the annotation
void push_end_bracket(const char *text) const {
derived().do_push_end_bracket(text);
- DSN_PRINTF("<---- end %s (%s)\n", text, derived().get_name());
+ dsn_printf("<---- end %s (%s)\n", text, derived().get_name());
}
/// \brief Adds annotations to the state, bracketing a scope
diff --git a/src/i-virtual-machine.h b/src/i-machine.h
similarity index 92%
rename from src/i-virtual-machine.h
rename to src/i-machine.h
index 2ea1dd3cd..ee82aa6be 100644
--- a/src/i-virtual-machine.h
+++ b/src/i-machine.h
@@ -14,23 +14,23 @@
// with this program (see COPYING). If not, see .
//
-#ifndef I_VIRTUAL_MACHINE_H
-#define I_VIRTUAL_MACHINE_H
+#ifndef I_MACHINE_H
+#define I_MACHINE_H
#include
#include
#include "access-log.h"
+#include "address-range-description.h"
#include "interpret.h"
#include "machine-config.h"
-#include "machine-memory-range-descr.h"
#include "machine-merkle-tree.h"
#include "machine.h"
#include "uarch-interpret.h"
namespace cartesi {
-/// \class i_virtual_machine
+/// \class i_machine
/// \brief Interface representing the public API of the Cartesi machine.
/// \details \{
/// Allows clients to reference this interface in order to transparently
@@ -42,24 +42,24 @@ namespace cartesi {
/// Classes implementing this interface are required to provide
/// implementations for the pure virtual methods.
/// \}
-class i_virtual_machine {
+class i_machine {
public:
using hash_type = machine_merkle_tree::hash_type;
using reg = machine_reg;
/// \brief Constructor
- i_virtual_machine() = default;
+ i_machine() = default;
/// \brief Destructor.
- virtual ~i_virtual_machine() = default;
+ virtual ~i_machine() = default;
- i_virtual_machine(const i_virtual_machine &other) = delete;
- i_virtual_machine(i_virtual_machine &&other) noexcept = delete;
- i_virtual_machine &operator=(const i_virtual_machine &other) = delete;
- i_virtual_machine &operator=(i_virtual_machine &&other) noexcept = delete;
+ i_machine(const i_machine &other) = delete;
+ i_machine(i_machine &&other) noexcept = delete;
+ i_machine &operator=(const i_machine &other) = delete;
+ i_machine &operator=(i_machine &&other) noexcept = delete;
/// \brief Clone an object of same underlying type but without a machine instance
- i_virtual_machine *clone_empty() const {
+ i_machine *clone_empty() const {
return do_clone_empty();
}
@@ -200,8 +200,8 @@ class i_virtual_machine {
}
/// \brief Returns a list of descriptions for all PMA entries registered in the machine, sorted by start
- virtual machine_memory_range_descrs get_memory_ranges() const {
- return do_get_memory_ranges();
+ virtual address_range_descriptions get_address_ranges() const {
+ return do_get_address_ranges();
}
/// \brief Sends cmio response.
@@ -249,13 +249,13 @@ class i_virtual_machine {
do_verify_send_cmio_response(reason, data, length, root_hash_before, log, root_hash_after);
}
- /// \brief Checks if implementation is jsorpc-virtual-machine
- bool is_jsonrpc_virtual_machine() const {
- return do_is_jsonrpc_virtual_machine();
+ /// \brief Checks if implementation is jsorpc-machine
+ bool is_jsonrpc_machine() const {
+ return do_is_jsonrpc_machine();
}
private:
- virtual i_virtual_machine *do_clone_empty() const = 0;
+ virtual i_machine *do_clone_empty() const = 0;
virtual bool do_is_empty() const = 0;
virtual void do_create(const machine_config &config, const machine_runtime_config &runtime) = 0;
virtual void do_load(const std::string &directory, const machine_runtime_config &runtime) = 0;
@@ -283,7 +283,7 @@ class i_virtual_machine {
virtual void do_reset_uarch() = 0;
virtual access_log do_log_reset_uarch(const access_log::type &log_type) = 0;
virtual uarch_interpreter_break_reason do_run_uarch(uint64_t uarch_cycle_end) = 0;
- virtual machine_memory_range_descrs do_get_memory_ranges() const = 0;
+ virtual address_range_descriptions do_get_address_ranges() const = 0;
virtual void do_send_cmio_response(uint16_t reason, const unsigned char *data, uint64_t length) = 0;
virtual access_log do_log_send_cmio_response(uint16_t reason, const unsigned char *data, uint64_t length,
const access_log::type &log_type) = 0;
@@ -297,11 +297,11 @@ class i_virtual_machine {
const hash_type &root_hash_after) const = 0;
virtual void do_verify_send_cmio_response(uint16_t reason, const unsigned char *data, uint64_t length,
const hash_type &root_hash_before, const access_log &log, const hash_type &root_hash_after) const = 0;
- virtual bool do_is_jsonrpc_virtual_machine() const {
+ virtual bool do_is_jsonrpc_machine() const {
return false;
}
};
} // namespace cartesi
-#endif // I_VIRTUAL_MACHINE_H
+#endif // I_MACHINE_H
diff --git a/src/i-state-access.h b/src/i-state-access.h
index 6e7be2874..c9cbb1a0d 100644
--- a/src/i-state-access.h
+++ b/src/i-state-access.h
@@ -21,14 +21,15 @@
/// \brief State access interface
#include
+#include
#include
#include
#include
-#include "dump.h"
+#include "assert-printf.h"
#include "i-prefer-shadow-state.h"
#include "meta.h"
-#include "pm-type-name.h"
+#include "poor-type-name.h"
#include "tlb.h"
namespace cartesi {
@@ -36,12 +37,6 @@ namespace cartesi {
// Forward declarations
enum class bracket_type;
-// Type trait that should return the pma_entry type for a state access class
-template
-struct i_state_access_pma_entry {};
-template
-using i_state_access_pma_entry_t = typename i_state_access_pma_entry::type;
-
// Type trait that should return the fast_addr type for a state access class
template
struct i_state_access_fast_addr {};
@@ -53,7 +48,7 @@ using i_state_access_fast_addr_t = typename i_state_access_fast_addr) { \
const auto val = derived().do_read_##REG(); \
- DSA_PRINTF("%s::read_" #REG "() = %" PRIu64 "(0x%" PRIx64 ")\n", get_name(), val, val); \
+ dsa_printf("%s::read_" #REG "() = %" PRIu64 "(0x%" PRIx64 ")\n", get_name(), val, val); \
return val; \
} else { \
return prefer_read_shadow_state(shadow_state_what::REG); \
@@ -64,7 +59,7 @@ using i_state_access_fast_addr_t = typename i_state_access_fast_addr) { \
derived().do_write_##REG(val); \
- DSA_PRINTF("%s::write_" #REG "(%" PRIu64 "(0x%" PRIx64 "))\n", get_name(), val, val); \
+ dsa_printf("%s::write_" #REG "(%" PRIu64 "(0x%" PRIx64 "))\n", get_name(), val, val); \
} else { \
prefer_write_shadow_state(shadow_state_what::REG, val); \
} \
@@ -110,25 +105,35 @@ class i_state_access { // CRTP
uint64_t prefer_read_shadow_state(shadow_state_what what) const {
const auto val = derived().read_shadow_state(what);
[[maybe_unused]] const auto *const what_name = shadow_state_get_what_name(what);
- DSA_PRINTF("%s::read_shadow_state(%s) = %" PRIu64 "(0x%" PRIx64 ")\n", get_name(), what_name, val, val);
+ dsa_printf("%s::read_shadow_state(%s) = %" PRIu64 "(0x%" PRIx64 ")\n", get_name(), what_name, val, val);
return val;
}
void prefer_write_shadow_state(shadow_state_what what, uint64_t val) const {
derived().write_shadow_state(what, val);
[[maybe_unused]] const auto *const what_name = shadow_state_get_what_name(what);
- DSA_PRINTF("%s::write_shadow_state(%s, %" PRIu64 "(0x%" PRIx64 "))\n", get_name(), what_name, val, val);
+ dsa_printf("%s::write_shadow_state(%s, %" PRIu64 "(0x%" PRIx64 "))\n", get_name(), what_name, val, val);
}
public:
- using pma_entry = i_state_access_pma_entry_t;
using fast_addr = i_state_access_fast_addr_t;
+ /// \brief Works as vprintf if we are dumping state accesses, otherwise does nothing
+ static void dsa_vprintf([[maybe_unused]] const char *fmt, [[maybe_unused]] va_list ap) {
+#ifdef DUMP_STATE_ACCESS
+ d_vprintf(fmt, ap);
+#endif
+ }
+
/// \brief Works as printf if we are dumping state accesses, otherwise does nothing
- template
- static void DSA_PRINTF([[maybe_unused]] const char (&fmt)[N], [[maybe_unused]] ARGS... args) {
+ // Better to use C-style variadic function that checks for format!
+ // NOLINTNEXTLINE(cert-dcl50-cpp)
+ __attribute__((__format__(__printf__, 1, 2))) static void dsa_printf([[maybe_unused]] const char *fmt, ...) {
#ifdef DUMP_STATE_ACCESS
- D_PRINTF(fmt, args...);
+ va_list ap;
+ va_start(ap, fmt);
+ dsa_vprintf(fmt, ap);
+ va_end(ap);
#endif
}
@@ -138,7 +143,7 @@ class i_state_access { // CRTP
uint64_t read_x(int i) const {
if constexpr (!is_an_i_prefer_shadow_state_v) {
const auto val = derived().do_read_x(i);
- DSA_PRINTF("%s::read_x(%d) = %" PRIu64 "(0x%" PRIx64 ")\n", get_name(), i, val, val);
+ dsa_printf("%s::read_x(%d) = %" PRIu64 "(0x%" PRIx64 ")\n", get_name(), i, val, val);
return val;
} else {
return prefer_read_shadow_state(shadow_state_get_what(shadow_state_what::x0, i));
@@ -153,7 +158,7 @@ class i_state_access { // CRTP
void write_x(int i, uint64_t val) const {
if constexpr (!is_an_i_prefer_shadow_state_v) {
derived().do_write_x(i, val);
- DSA_PRINTF("%s::write_x(%d, %" PRIu64 "(0x%" PRIx64 "))\n", get_name(), i, val, val);
+ dsa_printf("%s::write_x(%d, %" PRIu64 "(0x%" PRIx64 "))\n", get_name(), i, val, val);
} else {
prefer_write_shadow_state(shadow_state_get_what(shadow_state_what::x0, i), val);
}
@@ -165,7 +170,7 @@ class i_state_access { // CRTP
uint64_t read_f(int i) const {
if constexpr (!is_an_i_prefer_shadow_state_v) {
const auto val = derived().do_read_f(i);
- DSA_PRINTF("%s::read_f(%d) = %" PRIu64 "(0x%" PRIx64 ")\n", get_name(), i, val, val);
+ dsa_printf("%s::read_f(%d) = %" PRIu64 "(0x%" PRIx64 ")\n", get_name(), i, val, val);
return val;
} else {
return prefer_read_shadow_state(shadow_state_get_what(shadow_state_what::f0, i));
@@ -178,7 +183,7 @@ class i_state_access { // CRTP
void write_f(int i, uint64_t val) const {
if constexpr (!is_an_i_prefer_shadow_state_v) {
derived().do_write_f(i, val);
- DSA_PRINTF("%s::write_f(%d, %" PRIu64 "(%" PRIx64 "))\n", get_name(), i, val, val);
+ dsa_printf("%s::write_f(%d, %" PRIu64 "(%" PRIx64 "))\n", get_name(), i, val, val);
} else {
prefer_write_shadow_state(shadow_state_get_what(shadow_state_what::f0, i), val);
}
@@ -274,11 +279,11 @@ class i_state_access { // CRTP
/// \brief Reads PMA entry at a given index.
/// \param index Index of PMA
- pma_entry &read_pma_entry(uint64_t index) const {
- auto &pma = derived().do_read_pma_entry(index);
- DSA_PRINTF("%s::read_pma_entry(%" PRIu64 ") = {%s, 0x%" PRIx64 ", 0x%" PRIx64 "}\n", get_name(), index,
- pma_get_DID_name(pma.get_istart_DID()), pma.get_start(), pma.get_length());
- return pma;
+ address_range &read_pma(uint64_t index) const {
+ auto &ar = derived().do_read_pma(index);
+ dsa_printf("%s::read_address_range(%" PRIu64 ") = {%s, 0x%" PRIx64 ", 0x%" PRIx64 "}\n", get_name(), index,
+ pmas_get_DID_name(ar.get_driver_id()), ar.get_start(), ar.get_length());
+ return ar;
}
/// \brief Converts a target physical address to the implementation-defined fast address
@@ -288,8 +293,8 @@ class i_state_access { // CRTP
fast_addr get_faddr(uint64_t paddr, uint64_t pma_index) const {
const auto val = derived().do_get_faddr(paddr, pma_index);
[[maybe_unused]] const auto fast_addr_name = std::is_same_v ? "phys_addr" : "fast_addr";
- DSA_PRINTF("%s::get_faddr(%" PRIu64 "(0x%" PRIx64 ")) = %s{%" PRIu64 "(0x%" PRIx64 ")}\n", get_name(), paddr,
- paddr, fast_addr_name, val, val);
+ dsa_printf("%s::get_faddr(%" PRIu64 "(0x%" PRIx64 ")) = %s{%" PRIu64 "(0x%" PRIx64 ")}\n", get_name(), paddr,
+ paddr, fast_addr_name, static_cast(val), static_cast(val));
return val;
}
@@ -338,9 +343,9 @@ class i_state_access { // CRTP
static_assert(std::is_integral_v && sizeof(T) <= sizeof(uint64_t), "unsupported type");
derived().template do_read_memory_word(faddr, pma_index, pval);
[[maybe_unused]] const auto fast_addr_name = std::is_same_v ? "phys_addr" : "fast_addr";
- DSA_PRINTF("%s::read_memory_word<%s,%s>(%s{0x%" PRIx64 "}, %" PRIu64 ") = %" PRIu64 "(0x%" PRIx64 ")\n",
- get_name(), pm_type_name_v, pm_type_name_v, fast_addr_name, faddr, pma_index,
- static_cast(*pval), static_cast(*pval));
+ dsa_printf("%s::read_memory_word<%s,%s>(%s{0x%" PRIx64 "}, %" PRIu64 ") = %" PRIu64 "(0x%" PRIx64 ")\n",
+ get_name(), poor_type_name_v, poor_type_name_v, fast_addr_name, static_cast(faddr),
+ pma_index, static_cast(*pval), static_cast(*pval));
}
/// \brief Writes a word to memory.
@@ -355,9 +360,9 @@ class i_state_access { // CRTP
static_assert(std::is_integral_v && sizeof(T) <= sizeof(uint64_t), "unsupported type");
derived().template do_write_memory_word(faddr, pma_index, val);
[[maybe_unused]] const auto fast_addr_name = std::is_same_v ? "phys_addr" : "fast_addr";
- DSA_PRINTF("%s::write_memory_word<%s,%s>(%s{0x%" PRIx64 "}, %" PRIu64 ", %" PRIu64 "(0x%" PRIx64 "))\n",
- get_name(), pm_type_name_v, pm_type_name_v, fast_addr_name, faddr, pma_index,
- static_cast(val), static_cast(val));
+ dsa_printf("%s::write_memory_word<%s,%s>(%s{0x%" PRIx64 "}, %" PRIu64 ", %" PRIu64 "(0x%" PRIx64 "))\n",
+ get_name(), poor_type_name_v, poor_type_name_v, fast_addr_name, static_cast(faddr),
+ pma_index, static_cast(val), static_cast(val));
}
/// \brief Reads TLB's vaddr_page
@@ -367,7 +372,7 @@ class i_state_access { // CRTP
template
uint64_t read_tlb_vaddr_page(uint64_t slot_index) const {
const auto val = derived().template do_read_tlb_vaddr_page(slot_index);
- DSA_PRINTF("%s::read_tlb_vaddr_page<%" PRIu64 ">(%" PRIu64 ") = 0x%" PRIx64 "\n", get_name(), SET, slot_index,
+ dsa_printf("%s::read_tlb_vaddr_page<%" PRIu64 ">(%" PRIu64 ") = 0x%" PRIx64 "\n", get_name(), SET, slot_index,
val);
return val;
}
@@ -380,8 +385,8 @@ class i_state_access { // CRTP
fast_addr read_tlb_vp_offset(uint64_t slot_index) const {
[[maybe_unused]] const auto fast_addr_name = std::is_same_v ? "phys_addr" : "fast_addr";
const auto val = derived().template do_read_tlb_vp_offset(slot_index);
- DSA_PRINTF("%s::read_tlb_vp_offset<%" PRIu64 ">(%" PRIu64 ") = %s{0x%" PRIx64 "}\n", get_name(), SET,
- slot_index, fast_addr_name, val);
+ dsa_printf("%s::read_tlb_vp_offset<%" PRIu64 ">(%" PRIu64 ") = %s{0x%" PRIx64 "}\n", get_name(), SET,
+ slot_index, fast_addr_name, static_cast(val));
return val;
}
@@ -392,7 +397,7 @@ class i_state_access { // CRTP
template
uint64_t read_tlb_pma_index(uint64_t slot_index) const {
const auto val = derived().template do_read_tlb_pma_index(slot_index);
- DSA_PRINTF("%s::read_tlb_pma_index<%" PRIu64 ">(%" PRIu64 ") = %" PRIu64 "(0x%" PRIx64 ")\n", get_name(), SET,
+ dsa_printf("%s::read_tlb_pma_index<%" PRIu64 ">(%" PRIu64 ") = %" PRIu64 "(0x%" PRIx64 ")\n", get_name(), SET,
slot_index, val, val);
return val;
}
@@ -409,8 +414,8 @@ class i_state_access { // CRTP
void write_tlb(uint64_t slot_index, uint64_t vaddr_page, fast_addr vp_offset, uint64_t pma_index) const {
derived().template do_write_tlb(slot_index, vaddr_page, vp_offset, pma_index);
[[maybe_unused]] const auto fast_addr_name = std::is_same_v ? "phys_addr" : "fast_addr";
- DSA_PRINTF("%s::write_tlb<%" PRIu64 ">(%" PRIu64 ", 0x%" PRIx64 ", %s{0x%" PRIx64 "}, %" PRIu64 ")\n",
- get_name(), SET, slot_index, vaddr_page, fast_addr_name, vp_offset, pma_index);
+ dsa_printf("%s::write_tlb<%" PRIu64 ">(%" PRIu64 ", 0x%" PRIx64 ", %s{0x%" PRIx64 "}, %" PRIu64 ")\n",
+ get_name(), SET, slot_index, vaddr_page, fast_addr_name, static_cast(vp_offset), pma_index);
}
/// \brief Marks a page as dirty
diff --git a/src/i-uarch-state-access.h b/src/i-uarch-state-access.h
index fd58933b7..3e290c768 100644
--- a/src/i-uarch-state-access.h
+++ b/src/i-uarch-state-access.h
@@ -18,9 +18,10 @@
#define I_UARCH_STATE_ACCESS_H
#include
+#include
#include
-#include "dump.h"
+#include "assert-printf.h"
#include "i-prefer-shadow-uarch-state.h"
#include "meta.h"
#include "tlb.h"
@@ -30,7 +31,7 @@
uint64_t read_##REG() const { \
if constexpr (!is_an_i_prefer_shadow_uarch_state_v) { \
const auto val = derived().do_read_##REG(); \
- DUSA_PRINTF("%s::read_" #REG "() = %" PRIu64 "(0x%" PRIx64 ")\n", get_name(), val, val); \
+ dusa_printf("%s::read_" #REG "() = %" PRIu64 "(0x%" PRIx64 ")\n", get_name(), val, val); \
return val; \
} else { \
return prefer_read_shadow_uarch_state(shadow_uarch_state_what::REG); \
@@ -41,7 +42,7 @@
void write_##REG(uint64_t val) const { \
if constexpr (!is_an_i_prefer_shadow_uarch_state_v) { \
derived().do_write_##REG(val); \
- DUSA_PRINTF("%s::write_" #REG "(%" PRIu64 "(0x%" PRIx64 "))\n", get_name(), val, val); \
+ dusa_printf("%s::write_" #REG "(%" PRIu64 "(0x%" PRIx64 "))\n", get_name(), val, val); \
} else { \
prefer_write_shadow_uarch_state(shadow_uarch_state_what::REG, val); \
} \
@@ -67,29 +68,40 @@ class i_uarch_state_access { // CRTP
uint64_t prefer_read_shadow_uarch_state(shadow_uarch_state_what what) const {
const auto val = derived().read_shadow_uarch_state(what);
[[maybe_unused]] const auto *const what_name = shadow_uarch_state_get_what_name(what);
- DUSA_PRINTF("%s::read_shadow_uarch_state(%s) = %" PRIu64 "(0x%" PRIx64 ")\n", get_name(), what_name, val, val);
+ dusa_printf("%s::read_shadow_uarch_state(%s) = %" PRIu64 "(0x%" PRIx64 ")\n", get_name(), what_name, val, val);
return val;
}
void prefer_write_shadow_uarch_state(shadow_uarch_state_what what, uint64_t val) const {
derived().write_shadow_uarch_state(what, val);
[[maybe_unused]] const auto *const what_name = shadow_uarch_state_get_what_name(what);
- DUSA_PRINTF("%s::write_shadow_uarch_state(%s, %" PRIu64 "(0x%" PRIx64 "))\n", get_name(), what_name, val, val);
+ dusa_printf("%s::write_shadow_uarch_state(%s, %" PRIu64 "(0x%" PRIx64 "))\n", get_name(), what_name, val, val);
}
public:
+ /// \brief Works as vprintf if we are dumping uarch state accesses, otherwise does nothing
+ static void dusa_vprintf([[maybe_unused]] const char *fmt, [[maybe_unused]] va_list ap) {
+#ifdef DUMP_UARCH_STATE_ACCESS
+ d_vprintf(fmt, ap);
+#endif
+ }
+
/// \brief Works as printf if we are dumping uarch state accesses, otherwise does nothing
- template
- static void DUSA_PRINTF([[maybe_unused]] const char (&fmt)[N], [[maybe_unused]] ARGS... args) {
+ // Better to use C-style variadic function that checks for format!
+ // NOLINTNEXTLINE(cert-dcl50-cpp)
+ __attribute__((__format__(__printf__, 1, 2))) static void dusa_printf([[maybe_unused]] const char *fmt, ...) {
#ifdef DUMP_UARCH_STATE_ACCESS
- D_PRINTF(fmt, args...);
+ va_list ap;
+ va_start(ap, fmt);
+ dusa_vprintf(fmt, ap);
+ va_end(ap);
#endif
}
uint64_t read_uarch_x(int i) const {
if constexpr (!is_an_i_prefer_shadow_uarch_state_v) {
const auto val = derived().do_read_uarch_x(i);
- DUSA_PRINTF("%s::read_uarch_x(%d) = %" PRIu64 "(0x%" PRIx64 ")\n", get_name(), i, val, val);
+ dusa_printf("%s::read_uarch_x(%d) = %" PRIu64 "(0x%" PRIx64 ")\n", get_name(), i, val, val);
return val;
} else {
return prefer_read_shadow_uarch_state(shadow_uarch_state_get_what(shadow_uarch_state_what::uarch_x0, i));
@@ -99,7 +111,7 @@ class i_uarch_state_access { // CRTP
void write_uarch_x(int i, uint64_t val) const {
if constexpr (!is_an_i_prefer_shadow_uarch_state_v) {
derived().do_write_uarch_x(i, val);
- DUSA_PRINTF("%s::write_uarch_x(%d, %" PRIu64 ")\n", get_name(), i, val);
+ dusa_printf("%s::write_uarch_x(%d, %" PRIu64 ")\n", get_name(), i, val);
} else {
prefer_write_shadow_uarch_state(shadow_uarch_state_get_what(shadow_uarch_state_what::uarch_x0, i), val);
}
@@ -117,14 +129,14 @@ class i_uarch_state_access { // CRTP
uint64_t read_word(uint64_t paddr) const {
const auto val = derived().do_read_word(paddr);
- DUSA_PRINTF("%s::read_word(phys_addr{0x%" PRIx64 "}) = %" PRIu64 "(0x%" PRIx64 ")\n", get_name(), paddr, val,
+ dusa_printf("%s::read_word(phys_addr{0x%" PRIx64 "}) = %" PRIu64 "(0x%" PRIx64 ")\n", get_name(), paddr, val,
val);
return val;
}
void write_word(uint64_t paddr, uint64_t val) const {
derived().do_write_word(paddr, val);
- DUSA_PRINTF("%s::write_word(phys_addr{0x%" PRIx64 "}, %" PRIu64 "(0x%" PRIx64 "))\n", get_name(), paddr, val,
+ dusa_printf("%s::write_word(phys_addr{0x%" PRIx64 "}, %" PRIu64 "(0x%" PRIx64 "))\n", get_name(), paddr, val,
val);
}
diff --git a/src/interpret.cpp b/src/interpret.cpp
index 02a272b65..11c5a6f31 100644
--- a/src/interpret.cpp
+++ b/src/interpret.cpp
@@ -92,18 +92,16 @@
#ifdef MICROARCHITECTURE
#include "machine-uarch-bridge-state-access.h"
-#include "uarch-runtime.h"
#else
#include "record-step-state-access.h"
#include "replay-step-state-access.h"
#include "state-access.h"
-#include
#endif // MICROARCHITECTURE
+#include "assert-printf.h"
#include "compiler-defines.h"
#include "device-state-access.h"
-#include "dump.h"
-#include "find-pma-entry.h"
+#include "find-pma.h"
#include "i-accept-counters.h"
#include "i-interactive-state-access.h"
#include "i-state-access.h"
@@ -182,94 +180,94 @@ static void dump_exception_or_interrupt(uint64_t cause, uint64_t a7) {
if ((cause & MCAUSE_INTERRUPT_FLAG) != 0) {
switch (cause & ~MCAUSE_INTERRUPT_FLAG) {
case 0:
- D_PRINTF("reserved software interrupt", "");
+ d_printf("reserved software interrupt");
break;
case 1:
- D_PRINTF("supervisor software interrupt", "");
+ d_printf("supervisor software interrupt");
break;
case 2:
- D_PRINTF("reserved software interrupt", "");
+ d_printf("reserved software interrupt");
break;
case 3:
- D_PRINTF("machine software interrupt", "");
+ d_printf("machine software interrupt");
break;
case 4:
- D_PRINTF("reserved timer interrupt", "");
+ d_printf("reserved timer interrupt");
break;
case 5:
- D_PRINTF("supervisor timer interrupt", "");
+ d_printf("supervisor timer interrupt");
break;
case 6:
- D_PRINTF("reserved timer interrupt", "");
+ d_printf("reserved timer interrupt");
break;
case 7:
- D_PRINTF("machine timer interrupt", "");
+ d_printf("machine timer interrupt");
break;
case 8:
- D_PRINTF("reserved external interrupt", "");
+ d_printf("reserved external interrupt");
break;
case 9:
- D_PRINTF("supervisor external interrupt", "");
+ d_printf("supervisor external interrupt");
break;
case 10:
- D_PRINTF("reserved external interrupt", "");
+ d_printf("reserved external interrupt");
break;
case 11:
- D_PRINTF("machine external interrupt", "");
+ d_printf("machine external interrupt");
break;
default:
- D_PRINTF("unknown interrupt", "");
+ d_printf("unknown interrupt");
break;
}
} else {
switch (cause) {
case 0:
- D_PRINTF("instruction address misaligned", "");
+ d_printf("instruction address misaligned");
break;
case 1:
- D_PRINTF("instruction access fault", "");
+ d_printf("instruction access fault");
break;
case 2:
- D_PRINTF("illegal instruction", "");
+ d_printf("illegal instruction");
break;
case 3:
- D_PRINTF("breakpoint", "");
+ d_printf("breakpoint");
break;
case 4:
- D_PRINTF("load address misaligned", "");
+ d_printf("load address misaligned");
break;
case 5:
- D_PRINTF("load access fault", "");
+ d_printf("load access fault");
break;
case 6:
- D_PRINTF("store/amo address misaligned", "");
+ d_printf("store/amo address misaligned");
break;
case 7:
- D_PRINTF("store/amo access fault", "");
+ d_printf("store/amo access fault");
break;
case 8:
- D_PRINTF("ecall %d from u-mode", static_cast(a7));
+ d_printf("ecall %d from u-mode", static_cast(a7));
break;
case 9:
- D_PRINTF("ecall %s(%d) from s-mode", sbi_ecall_name(a7), static_cast(a7));
+ d_printf("ecall %s(%d) from s-mode", sbi_ecall_name(a7), static_cast(a7));
break;
case 10:
- D_PRINTF("ecall %d reserved", static_cast(a7));
+ d_printf("ecall %d reserved", static_cast(a7));
break;
case 11:
- D_PRINTF("ecall %s(%d) from m-mode", sbi_ecall_name(a7), static_cast(a7));
+ d_printf("ecall %s(%d) from m-mode", sbi_ecall_name(a7), static_cast(a7));
break;
case 12:
- D_PRINTF("instruction page fault", "");
+ d_printf("instruction page fault");
break;
case 13:
- D_PRINTF("load page fault", "");
+ d_printf("load page fault");
break;
case 15:
- D_PRINTF("store/amo page fault", "");
+ d_printf("store/amo page fault");
break;
default:
- D_PRINTF("reserved", "");
+ d_printf("reserved");
break;
}
}
@@ -319,22 +317,22 @@ static void dump_regs(STATE_ACCESS &a) {
f[i] = a.read_f(i);
}
// Now print them
- D_PRINTF("pc = " PRIxREG " ", pc);
+ d_printf("pc = " PRIxREG " ", pc);
for (int i = 1; i < X_REG_COUNT; i++) {
const char sep = ((i & (cols - 1)) == (cols - 1)) ? '\n' : ' ';
- D_PRINTF("%-4s= " PRIxREG "%c", reg_name[i], x[i], sep);
+ d_printf("%-4s= " PRIxREG "%c", reg_name[i], x[i], sep);
}
for (int i = 0; i < F_REG_COUNT; i++) {
const char sep = ((i & (cols - 1)) == (cols - 1)) ? '\n' : ' ';
- D_PRINTF("%-4s= " PRIxREG "%c", f_reg_name[i], f[i], sep);
- }
- D_PRINTF("prv=%s", prv_get_name(iprv));
- D_PRINTF(" mstatus=" PRIxREG "\n", mstatus);
- D_PRINTF(" cycles=" PRIuREG, mcycle);
- D_PRINTF(" insns=" PRIuREG "\n", mcycle - icycleinstret);
- D_PRINTF("mideleg=" PRIxREG, mideleg);
- D_PRINTF(" mie=" PRIxREG, mie);
- D_PRINTF(" mip=" PRIxREG "\n", mip);
+ d_printf("%-4s= " PRIxREG "%c", f_reg_name[i], f[i], sep);
+ }
+ d_printf("prv=%s", prv_get_name(iprv));
+ d_printf(" mstatus=" PRIxREG "\n", mstatus);
+ d_printf(" cycles=" PRIuREG, mcycle);
+ d_printf(" insns=" PRIuREG "\n", mcycle - icycleinstret);
+ d_printf("mideleg=" PRIxREG, mideleg);
+ d_printf(" mie=" PRIxREG, mie);
+ d_printf(" mip=" PRIxREG "\n", mip);
#undef PRIxREG
#undef PRIuREG
}
@@ -420,10 +418,10 @@ static NO_INLINE uint64_t raise_exception(const STATE_ACCESS a, uint64_t pc, uin
if (flag) {
[[maybe_unused]] auto dnote = a.make_scoped_note("dump_exception");
const auto a7 = a.read_x(17);
- D_PRINTF("raise_exception: cause=0x%016" PRIx64, cause);
- D_PRINTF(" tval=0x%016" PRIx64 " (", tval);
+ d_printf("raise_exception: cause=0x%016" PRIx64, cause);
+ d_printf(" tval=0x%016" PRIx64 " (", tval);
dump_exception_or_interrupt(cause, a7);
- D_PRINTF(")\n", "");
+ d_printf(")\n");
#ifdef DUMP_REGS
dump_regs(a);
#endif
@@ -995,22 +993,21 @@ static NO_INLINE std::pair read_virtual_memory_slow(const STATE_
return {false, pc};
}
uint64_t pma_index = 0;
- const auto &pma = find_pma_entry(a, paddr, pma_index);
- if (likely(pma.get_istart_R())) {
- if (likely(pma.get_istart_M())) {
+ const auto &ar = find_pma(a, paddr, pma_index);
+ if (likely(ar.is_readable())) {
+ if (likely(ar.is_memory())) {
[[maybe_unused]] auto note = a.make_scoped_note("read memory");
const auto faddr = replace_tlb_entry(a, vaddr, paddr, pma_index);
a.template read_memory_word(faddr, pma_index, pval);
return {true, pc};
}
- if (likely(pma.get_istart_IO())) {
+ if (likely(ar.is_device())) {
[[maybe_unused]] auto note = a.make_scoped_note("read device");
- const uint64_t offset = paddr - pma.get_start();
+ const uint64_t offset = paddr - ar.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
- const bool status = pma.get_device_noexcept().get_driver()->read(pma.get_device_noexcept().get_context(),
- &da, offset, &val, log2_size_v);
+ const bool status = ar.read_device(&da, offset, log2_size_v, &val);
if (likely(status)) {
*pval = static_cast(val);
// device logs its own state accesses
@@ -1087,18 +1084,17 @@ static NO_INLINE std::pair write_virtual_memory_slow(c
return {execute_status::failure, pc};
}
uint64_t pma_index = 0;
- auto &pma = find_pma_entry(a, paddr, pma_index);
- if (likely(pma.get_istart_W())) {
- if (likely(pma.get_istart_M())) {
+ auto &ar = find_pma(a, paddr, pma_index);
+ if (likely(ar.is_writeable())) {
+ if (likely(ar.is_memory())) {
const auto faddr = replace_tlb_entry(a, vaddr, paddr, pma_index);
a.write_memory_word(faddr, pma_index, static_cast(val64));
return {execute_status::success, pc};
}
- if (likely(pma.get_istart_IO())) {
- const uint64_t offset = paddr - pma.get_start();
+ if (likely(ar.is_device())) {
+ const uint64_t offset = paddr - ar.get_start();
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_v);
+ auto status = ar.write_device(&da, offset, log2_size_v, static_cast(static_cast(val64)));
// If we do not know how to write, we treat this as a PMA violation
if (likely(status != execute_status::failure)) {
return {status, pc};
@@ -1150,11 +1146,11 @@ static auto dump_insn([[maybe_unused]] const STATE_ACCESS a, [[maybe_unused]] ui
[[maybe_unused]] auto note = a.make_scoped_note("dump_insn");
uint64_t ppc = pc;
if (!translate_virtual_address(a, &ppc, pc, PTE_XWR_X_SHIFT)) {
- D_PRINTF("v %08" PRIx64, ppc);
+ d_printf("v %08" PRIx64, ppc);
} else {
- D_PRINTF("p %08" PRIx64, ppc);
+ d_printf("p %08" PRIx64, ppc);
}
- D_PRINTF(": %08" PRIx32 " %s\n", insn, name);
+ d_printf(": %08" PRIx32 " %s\n", insn, name);
#endif
return a.make_scoped_note(name);
}
@@ -2091,7 +2087,7 @@ static NO_INLINE uint64_t read_csr(const STATE_ACCESS a, uint64_t mcycle, CSR_ad
default:
// Invalid CSRs
#ifdef DUMP_INVALID_CSR
- D_PRINTF("csr_read: invalid CSR=0x%x\n", static_cast(csraddr));
+ d_printf("csr_read: invalid CSR=0x%x\n", static_cast(csraddr));
#endif
return read_csr_fail(status);
}
@@ -2439,9 +2435,9 @@ static inline execute_status write_csr_fcsr(const STATE_ACCESS a, uint64_t val)
template
static NO_INLINE execute_status write_csr(const STATE_ACCESS a, uint64_t mcycle, CSR_address csraddr, uint64_t val) {
#if defined(DUMP_CSR)
- D_PRINTF("csr_write: csr=0x%03x val=0x", static_cast(csraddr));
+ d_printf("csr_write: csr=0x%03x val=0x", static_cast(csraddr));
print_uint64_t(val);
- D_PRINTF("\n");
+ d_printf("\n");
#endif
if (unlikely(csr_is_read_only(csraddr))) {
return execute_status::failure;
@@ -2584,7 +2580,7 @@ static NO_INLINE execute_status write_csr(const STATE_ACCESS a, uint64_t mcycle,
default:
// Invalid CSRs
#ifdef DUMP_INVALID_CSR
- D_PRINTF("csr_write: invalid CSR=0x%x\n", static_cast(csraddr));
+ d_printf("csr_write: invalid CSR=0x%x\n", static_cast(csraddr));
#endif
return execute_status::failure;
}
@@ -5396,10 +5392,10 @@ static FORCE_INLINE fetch_status fetch_translate_pc_slow(const STATE_ACCESS a, u
return fetch_status::exception;
}
// Walk memory map to find the range that contains the physical address
- const auto &pma = find_pma_entry