From db023c81a5196ac2de74c721b5962ac7708c13b6 Mon Sep 17 00:00:00 2001
From: Diego Nehab <1635557+diegonehab@users.noreply.github.com>
Date: Sat, 22 Feb 2025 20:57:23 +0000
Subject: [PATCH] test commit
---
.gitattributes | 1 +
src/Makefile | 65 +-
...ge-descr.h => address-range-description.h} | 12 +-
src/address-range.h | 392 ++++++++
...shadow-uarch-state.cpp => assert-printf.h} | 19 +-
src/{clint.cpp => clint-address-range.cpp} | 30 +-
src/clint-address-range.h | 70 ++
src/clint-factory.cpp | 32 -
src/clint-factory.h | 34 -
src/clint.h | 50 -
src/clua-i-virtual-machine.cpp | 8 +-
src/dump.h | 10 +-
src/find-pma-entry.h | 37 +-
src/{htif.cpp => htif-address-range.cpp} | 17 +-
src/htif-address-range.h | 72 ++
src/{htif.h => htif-constants.h} | 25 +-
src/htif-factory.cpp | 32 -
src/htif-factory.h | 31 -
src/i-state-access.h | 17 +-
src/i-virtual-machine.h | 8 +-
src/interpret.cpp | 30 +-
src/json-util.cpp | 20 +-
src/json-util.h | 24 +-
src/jsonrpc-discover.json | 18 +-
src/jsonrpc-machine-c-api.cpp | 1 -
src/jsonrpc-remote-machine.cpp | 8 +-
src/jsonrpc-virtual-machine.cpp | 8 +-
src/jsonrpc-virtual-machine.h | 4 +-
src/machine-c-api.cpp | 25 +-
src/machine-c-api.h | 6 +-
src/machine-config.cpp | 1 -
src/machine-reg.h | 4 +-
src/machine-state.h | 17 +-
src/machine.cpp | 872 ++++++++++--------
src/machine.h | 255 ++---
src/memory-address-range.cpp | 90 ++
src/memory-address-range.h | 143 +++
src/meta.h | 6 +
src/mock-address-range.h | 108 +++
src/mock-pma-entry.h | 169 ----
src/{plic.cpp => plic-address-range.cpp} | 35 +-
src/plic-address-range.h | 80 ++
src/{plic.h => plic-constants.h} | 19 +-
src/plic-factory.cpp | 32 -
src/plic-factory.h | 34 -
src/pma-constants.h | 67 +-
src/pma-driver.cpp | 35 -
src/pma-driver.h | 67 --
src/pma.cpp | 220 -----
src/pma.h | 603 ++----------
src/pristine-address-range.h | 59 ++
src/record-send-cmio-state-access.h | 22 +-
src/record-step-state-access.h | 16 +-
src/replay-send-cmio-state-access.h | 7 -
src/replay-step-state-access.h | 30 +-
src/riscv-constants.h | 65 +-
src/shadow-peek.h | 19 +-
...actory.cpp => shadow-pmas-address-range.h} | 25 +-
src/shadow-pmas-factory.h | 49 -
src/shadow-pmas.h | 2 -
...ory.cpp => shadow-state-address-range.cpp} | 24 +-
src/shadow-state-address-range.h | 72 ++
src/shadow-state-factory.h | 37 -
src/shadow-state.cpp | 25 -
src/shadow-state.h | 9 +-
...ctory.cpp => shadow-tlb-address-range.cpp} | 25 +-
src/shadow-tlb-address-range.h | 72 ++
src/shadow-tlb-factory.h | 2 -
src/shadow-tlb.cpp | 25 -
src/shadow-tlb.h | 3 -
...p => shadow-uarch-state-address-range.cpp} | 23 +-
src/shadow-uarch-state-address-range.h | 74 ++
src/shadow-uarch-state-factory.h | 37 -
src/shadow-uarch-state.h | 6 +-
src/state-access.h | 12 +-
src/tlb.h | 1 -
src/translate-virtual-address.h | 8 +-
src/uarch-record-state-access.h | 2 -
src/uarch-replay-state-access.h | 1 -
src/uarch-state-access.h | 23 +-
src/uarch-state.h | 8 +-
src/unique-c-ptr.h | 10 +-
...io-device.cpp => virtio-address-range.cpp} | 82 +-
...virtio-device.h => virtio-address-range.h} | 56 +-
...e.cpp => virtio-console-address-range.cpp} | 29 +-
...nsole.h => virtio-console-address-range.h} | 23 +-
src/virtio-factory.cpp | 36 -
src/virtio-factory.h | 37 -
...o-net.cpp => virtio-net-address-range.cpp} | 37 +-
...irtio-net.h => virtio-net-address-range.h} | 74 +-
src/virtio-net-carrier-tuntap.h | 60 --
...pp => virtio-net-tuntap-address-range.cpp} | 20 +-
src/virtio-net-tuntap-address-range.h | 67 ++
....cpp => virtio-net-user-address-range.cpp} | 29 +-
...lirp.h => virtio-net-user-address-range.h} | 39 +-
...p9fs.cpp => virtio-p9fs-address-range.cpp} | 71 +-
...tio-p9fs.h => virtio-p9fs-address-range.h} | 30 +-
src/virtio-serializer.h | 2 +-
src/virtual-machine.cpp | 6 +-
src/virtual-machine.h | 4 +-
tests/lua/cartesi/tests/util.lua | 2 +-
tests/lua/create-machines.lua | 6 +-
tests/lua/machine-bind.lua | 26 +-
tests/lua/machine-test.lua | 20 +-
tests/misc/Makefile | 14 +-
tests/misc/test-machine-c-api.cpp | 15 +-
tests/misc/test-utils.h | 4 +-
uarch/Makefile | 14 +-
uarch/machine-uarch-bridge-state-access.h | 26 +-
uarch/uarch-run.cpp | 6 +-
uarch/uarch-runtime.cpp | 4 +
uarch/uarch-runtime.h | 18 +-
112 files changed, 2713 insertions(+), 2900 deletions(-)
create mode 100644 .gitattributes
rename src/{machine-memory-range-descr.h => address-range-description.h} (74%)
create mode 100644 src/address-range.h
rename src/{shadow-uarch-state.cpp => assert-printf.h} (76%)
rename src/{clint.cpp => clint-address-range.cpp} (78%)
create mode 100644 src/clint-address-range.h
delete mode 100644 src/clint-factory.cpp
delete mode 100644 src/clint-factory.h
delete mode 100644 src/clint.h
rename src/{htif.cpp => htif-address-range.cpp} (91%)
create mode 100644 src/htif-address-range.h
rename src/{htif.h => htif-constants.h} (81%)
delete mode 100644 src/htif-factory.cpp
delete mode 100644 src/htif-factory.h
create mode 100644 src/memory-address-range.cpp
create mode 100644 src/memory-address-range.h
create mode 100644 src/mock-address-range.h
delete mode 100644 src/mock-pma-entry.h
rename src/{plic.cpp => plic-address-range.cpp} (91%)
create mode 100644 src/plic-address-range.h
rename src/{plic.h => plic-constants.h} (86%)
delete mode 100644 src/plic-factory.cpp
delete mode 100644 src/plic-factory.h
delete mode 100644 src/pma-driver.cpp
delete mode 100644 src/pma-driver.h
delete mode 100644 src/pma.cpp
create mode 100644 src/pristine-address-range.h
rename src/{shadow-pmas-factory.cpp => shadow-pmas-address-range.h} (63%)
delete mode 100644 src/shadow-pmas-factory.h
rename src/{shadow-state-factory.cpp => shadow-state-address-range.cpp} (63%)
create mode 100644 src/shadow-state-address-range.h
delete mode 100644 src/shadow-state-factory.h
delete mode 100644 src/shadow-state.cpp
rename src/{shadow-tlb-factory.cpp => shadow-tlb-address-range.cpp} (64%)
create mode 100644 src/shadow-tlb-address-range.h
delete mode 100644 src/shadow-tlb.cpp
rename src/{shadow-uarch-state-factory.cpp => shadow-uarch-state-address-range.cpp} (66%)
create mode 100644 src/shadow-uarch-state-address-range.h
delete mode 100644 src/shadow-uarch-state-factory.h
rename src/{virtio-device.cpp => virtio-address-range.cpp} (91%)
rename src/{virtio-device.h => virtio-address-range.h} (93%)
rename src/{virtio-console.cpp => virtio-console-address-range.cpp} (82%)
rename src/{virtio-console.h => virtio-console-address-range.h} (76%)
delete mode 100644 src/virtio-factory.cpp
delete mode 100644 src/virtio-factory.h
rename src/{virtio-net.cpp => virtio-net-address-range.cpp} (77%)
rename src/{virtio-net.h => virtio-net-address-range.h} (73%)
delete mode 100644 src/virtio-net-carrier-tuntap.h
rename src/{virtio-net-carrier-tuntap.cpp => virtio-net-tuntap-address-range.cpp} (90%)
create mode 100644 src/virtio-net-tuntap-address-range.h
rename src/{virtio-net-carrier-slirp.cpp => virtio-net-user-address-range.cpp} (92%)
rename src/{virtio-net-carrier-slirp.h => virtio-net-user-address-range.h} (61%)
rename src/{virtio-p9fs.cpp => virtio-p9fs-address-range.cpp} (96%)
rename src/{virtio-p9fs.h => virtio-p9fs-address-range.h} (94%)
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..f0e818cde 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -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,48 +345,39 @@ 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 \
+ virtual-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 \
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..0500f0ec9
--- /dev/null
+++ b/src/address-range.h
@@ -0,0 +1,392 @@
+// 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 "pma.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.
+ pma_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{} {
+ m_flags.E = true;
+ 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;
+ constexpr virtual ~address_range() = default;
+
+ 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 pma_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", m_description);
+ }
+ if ((m_start & ~PMA_ISTART_START_MASK) != 0) {
+ ABRTF(abrt, "start of %s (0x%" PRIx64 ") must be aligned to page boundary of %d bytes", m_description,
+ start, PMA_PAGE_SIZE);
+ }
+ // It must be possible to round length up to the next power of two
+ if (m_length_bit_ceil == 0) {
+ ABRTF(abrt, "range too long when initializing %s", m_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", m_description);
+ }
+ if (!m_flags.E) {
+ ABRTF(abrt, "range with length 0 must be flagged empty when initializing %s", m_description);
+ }
+ if (m_flags.M) {
+ ABRTF(abrt, "memory range cannot be empty when initializing %s", m_description);
+ }
+ if (m_flags.IO) {
+ ABRTF(abrt, "device range cannot be empty when initializing %s", m_description);
+ }
+ }
+ // Non-empty range must either be memory or device
+ if (static_cast(m_flags.M) + static_cast(m_flags.IO) + static_cast(m_flags.E) != 1) {
+ ABRTF(abrt, "range must be one of empty, memory, or device when initializing %s", m_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 pma_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
+ 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
+ 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
+ bool is_empty() const noexcept {
+ return m_flags.E;
+ }
+
+ /// \brief Tests if range is readable
+ /// \returns True if and only if range is readable
+ bool is_readable() const noexcept {
+ return m_flags.R;
+ }
+
+ /// \brief Tests if range is writeable
+ /// \returns True if and only if range is writeable
+ bool is_writeable() const noexcept {
+ return m_flags.W;
+ }
+
+ /// \brief Tests if range is executable
+ /// \returns True if and only if range is executable
+ 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 pack_pma_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 &= ~(PMA_PAGE_SIZE - 1);
+ const auto length_aligned = length + (offset - offset_aligned);
+ for (; offset_aligned < length_aligned; offset_aligned += PMA_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, pma_flags f,
+ ABRT abrt) {
+ return address_range{description, start, length, f, abrt};
+}
+
+} // namespace cartesi
+
+#endif // OCCUPIED_ADDRESS_RANGE_H
diff --git a/src/shadow-uarch-state.cpp b/src/assert-printf.h
similarity index 76%
rename from src/shadow-uarch-state.cpp
rename to src/assert-printf.h
index 1cb3d25e3..1bf16b675 100644
--- a/src/shadow-uarch-state.cpp
+++ b/src/assert-printf.h
@@ -14,14 +14,17 @@
// with this program (see COPYING). If not, see .
//
-#include "shadow-uarch-state.h"
+#ifndef PRINTF_ASSERT_H
+#define PRINTF_ASSERT_H
-#include "pma-driver.h"
+/// \file
+/// \brief Microarchitecture-dependent includes for printf and assert
-namespace cartesi {
+#ifdef MICROARCHITECTURE
+#include "uarch-runtime.h"
+#else
+#include
+#include
+#endif
-const pma_driver shadow_uarch_state_driver = {.name = "SHADOW UARCH",
- .read = device_read_error,
- .write = device_write_error};
-
-} // namespace cartesi
+#endif
diff --git a/src/clint.cpp b/src/clint-address-range.cpp
similarity index 78%
rename from src/clint.cpp
rename to src/clint-address-range.cpp
index 55dbfa200..4622fa42e 100644
--- a/src/clint.cpp
+++ b/src/clint-address-range.cpp
@@ -14,19 +14,34 @@
// 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 "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);
}
@@ -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..b1d46aef0
--- /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 "pristine-address-range.h"
+
+/// \file
+/// \brief Core-Local Interruptor device.
+
+namespace cartesi {
+
+class clint_address_range final : public pristine_address_range {
+
+ static constexpr pma_flags m_clint_flags{
+ .M = false,
+ .IO = true,
+ .E = false,
+ .R = true,
+ .W = true,
+ .X = false,
+ .IR = false,
+ .IW = false,
+ .DID = PMA_ISTART_DID::CLINT,
+ };
+
+public:
+ template
+ clint_address_range(uint64_t start, uint64_t length, ABRT abrt) :
+ pristine_address_range("CLINT device", start, 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(uint64_t start, uint64_t length, ABRT abrt) {
+ return clint_address_range{start, length, 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-i-virtual-machine.cpp b/src/clua-i-virtual-machine.cpp
index 121a4a944..8c03c4225 100644
--- a/src/clua-i-virtual-machine.cpp
+++ b/src/clua-i-virtual-machine.cpp
@@ -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);
@@ -1054,7 +1054,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},
diff --git a/src/dump.h b/src/dump.h
index f7c00c538..4cfc8e2c8 100644
--- a/src/dump.h
+++ b/src/dump.h
@@ -18,17 +18,11 @@
#include
-#ifdef MICROARCHITECTURE
-template
-static inline void D_PRINTF(const char (&fmt)[N], ARGS... args) {
- std::ignore = printf(fmt, args...);
-}
-#else
-#include
+#include "assert-printf.h"
+
template
static inline auto D_PRINTF(const char (&fmt)[N], ARGS... args) {
std::ignore = fprintf(stderr, fmt, args...);
}
-#endif
#endif // DUMP_H
diff --git a/src/find-pma-entry.h b/src/find-pma-entry.h
index 1cb857ca4..73cb36503 100644
--- a/src/find-pma-entry.h
+++ b/src/find-pma-entry.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_ADDRESS_RANGE_H
+#define FIND_ADDRESS_RANGE_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.get_length() == 0)) {
+ 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..d7fd5cde8
--- /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 "i-device-state-access.h"
+#include "pma-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 pma_flags m_htif_flags{
+ .M = false,
+ .IO = true,
+ .E = false,
+ .R = true,
+ .W = true,
+ .X = false,
+ .IR = false,
+ .IW = false,
+ .DID = PMA_ISTART_DID::HTIF,
+ };
+
+public:
+ template
+ htif_address_range(uint64_t start, uint64_t length, ABRT abrt) :
+ pristine_address_range("HTIF device", start, 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(uint64_t start, uint64_t length, ABRT abrt) {
+ return htif_address_range{start, length, 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-state-access.h b/src/i-state-access.h
index 6e7be2874..4fc9bbb25 100644
--- a/src/i-state-access.h
+++ b/src/i-state-access.h
@@ -36,12 +36,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 {};
@@ -121,7 +115,6 @@ class i_state_access { // CRTP
}
public:
- using pma_entry = i_state_access_pma_entry_t;
using fast_addr = i_state_access_fast_addr_t;
/// \brief Works as printf if we are dumping state accesses, otherwise does nothing
@@ -274,11 +267,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,
+ pma_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
diff --git a/src/i-virtual-machine.h b/src/i-virtual-machine.h
index 2ea1dd3cd..b307d8d6d 100644
--- a/src/i-virtual-machine.h
+++ b/src/i-virtual-machine.h
@@ -21,9 +21,9 @@
#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"
@@ -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.
@@ -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;
diff --git a/src/interpret.cpp b/src/interpret.cpp
index 02a272b65..282344f4b 100644
--- a/src/interpret.cpp
+++ b/src/interpret.cpp
@@ -995,22 +995,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 +1086,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};
@@ -5396,10 +5394,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(a, paddr, pma_index);
+ const auto &ar = find_pma(a, paddr, pma_index);
// We only execute directly from RAM (as in "random access memory")
// If the range is not memory or not executable, this as a PMA violation
- if (unlikely(!pma.get_istart_M() || !pma.get_istart_X())) {
+ if (unlikely(!ar.is_memory() || !ar.is_executable())) {
pc = raise_exception(a, pc, MCAUSE_INSN_ACCESS_FAULT, vaddr);
return fetch_status::exception;
}
diff --git a/src/json-util.cpp b/src/json-util.cpp
index 3df07f1ff..e59cf5f6c 100644
--- a/src/json-util.cpp
+++ b/src/json-util.cpp
@@ -33,12 +33,12 @@
#include
#include "access-log.h"
+#include "address-range-description.h"
#include "base64.h"
#include "bracket-note.h"
#include "interpret.h"
#include "jsonrpc-fork-result.h"
#include "machine-config.h"
-#include "machine-memory-range-descr.h"
#include "machine-merkle-tree.h"
#include "machine-runtime-config.h"
#include "machine.h"
@@ -1639,7 +1639,7 @@ template void ju_get_opt_field(const nlohmann::json &j, const std::
const std::string &path);
template
-void ju_get_opt_field(const nlohmann::json &j, const K &key, machine_memory_range_descr &value,
+void ju_get_opt_field(const nlohmann::json &j, const K &key, address_range_description &value,
const std::string &path) {
if (!contains(j, key)) {
return;
@@ -1651,23 +1651,23 @@ void ju_get_opt_field(const nlohmann::json &j, const K &key, machine_memory_rang
ju_get_opt_field(jconfig, "description"s, value.description, new_path);
}
-template void ju_get_opt_field(const nlohmann::json &j, const uint64_t &key,
- machine_memory_range_descr &value, const std::string &path);
+template void ju_get_opt_field(const nlohmann::json &j, const uint64_t &key, address_range_description &value,
+ const std::string &path);
template void ju_get_opt_field(const nlohmann::json &j, const std::string &key,
- machine_memory_range_descr &value, const std::string &path);
+ address_range_description &value, const std::string &path);
template
-void ju_get_opt_field(const nlohmann::json &j, const K &key, machine_memory_range_descrs &value,
+void ju_get_opt_field(const nlohmann::json &j, const K &key, address_range_descriptions &value,
const std::string &path) {
ju_get_opt_vector_like_field(j, key, value, path);
}
template void ju_get_opt_field(const nlohmann::json &j, const uint64_t &key,
- machine_memory_range_descrs &value, const std::string &path);
+ address_range_descriptions &value, const std::string &path);
template void ju_get_opt_field(const nlohmann::json &j, const std::string &key,
- machine_memory_range_descrs &value, const std::string &path);
+ address_range_descriptions &value, const std::string &path);
template
void ju_get_opt_field(const nlohmann::json &j, const K &key, fork_result &value, const std::string &path) {
@@ -1997,11 +1997,11 @@ void to_json(nlohmann::json &j, const machine_runtime_config &runtime) {
};
}
-void to_json(nlohmann::json &j, const machine_memory_range_descr &mrd) {
+void to_json(nlohmann::json &j, const address_range_description &mrd) {
j = nlohmann::json{{"length", mrd.length}, {"start", mrd.start}, {"description", mrd.description}};
}
-void to_json(nlohmann::json &j, const machine_memory_range_descrs &mrds) {
+void to_json(nlohmann::json &j, const address_range_descriptions &mrds) {
j = nlohmann::json::array();
std::transform(mrds.cbegin(), mrds.cend(), std::back_inserter(j),
[](const auto &a) -> nlohmann::json { return a; });
diff --git a/src/json-util.h b/src/json-util.h
index 8a67c377e..5764a3dff 100644
--- a/src/json-util.h
+++ b/src/json-util.h
@@ -28,11 +28,11 @@
#include
#include "access-log.h"
+#include "address-range-description.h"
#include "bracket-note.h"
#include "interpret.h"
#include "jsonrpc-fork-result.h"
#include "machine-config.h"
-#include "machine-memory-range-descr.h"
#include "machine-merkle-tree.h"
#include "machine-runtime-config.h"
#include "machine.h"
@@ -474,24 +474,24 @@ template
void ju_get_opt_field(const nlohmann::json &j, const K &key, machine_config &value,
const std::string &path = "params/");
-/// \brief Attempts to load a machine_memory_range_descr object from a field in a JSON object
+/// \brief Attempts to load a address_range_description object from a field in a JSON object
/// \tparam K Key type (explicit extern declarations for uint64_t and std::string are provided)
/// \param j JSON object to load from
/// \param key Key to load value from
/// \param value Object to store value
/// \param path Path to j
template
-void ju_get_opt_field(const nlohmann::json &j, const K &key, machine_memory_range_descr &value,
+void ju_get_opt_field(const nlohmann::json &j, const K &key, address_range_description &value,
const std::string &path = "params/");
-/// \brief Attempts to load a machine_memory_range_descrs object from a field in a JSON object
+/// \brief Attempts to load a address_range_descriptions object from a field in a JSON object
/// \tparam K Key type (explicit extern declarations for uint64_t and std::string are provided)
/// \param j JSON object to load from
/// \param key Key to load value from
/// \param value Object to store value
/// \param path Path to j
template
-void ju_get_opt_field(const nlohmann::json &j, const K &key, machine_memory_range_descrs &value,
+void ju_get_opt_field(const nlohmann::json &j, const K &key, address_range_descriptions &value,
const std::string &path = "params/");
/// \brief Attempts to load a fork_result object from a field in a JSON object
@@ -625,8 +625,8 @@ void to_json(nlohmann::json &j, const concurrency_runtime_config &config);
void to_json(nlohmann::json &j, const htif_runtime_config &config);
void to_json(nlohmann::json &j, const machine_runtime_config &runtime);
void to_json(nlohmann::json &j, const machine::reg ®);
-void to_json(nlohmann::json &j, const machine_memory_range_descr &mrd);
-void to_json(nlohmann::json &j, const machine_memory_range_descrs &mrds);
+void to_json(nlohmann::json &j, const address_range_description &mrd);
+void to_json(nlohmann::json &j, const address_range_descriptions &mrds);
void to_json(nlohmann::json &j, const fork_result &fork_result);
void to_json(nlohmann::json &j, const semantic_version &version);
@@ -787,14 +787,14 @@ extern template void ju_get_opt_field(const nlohmann::json &j, const uint64_t &k
const std::string &base = "params/");
extern template void ju_get_opt_field(const nlohmann::json &j, const std::string &key, machine_config &value,
const std::string &base = "params/");
-extern template void ju_get_opt_field(const nlohmann::json &j, const uint64_t &key, machine_memory_range_descr &value,
+extern template void ju_get_opt_field(const nlohmann::json &j, const uint64_t &key, address_range_description &value,
const std::string &base = "params/");
-extern template void ju_get_opt_field(const nlohmann::json &j, const std::string &key,
- machine_memory_range_descr &value, const std::string &base = "params/");
-extern template void ju_get_opt_field(const nlohmann::json &j, const uint64_t &key, machine_memory_range_descrs &value,
+extern template void ju_get_opt_field(const nlohmann::json &j, const std::string &key, address_range_description &value,
+ const std::string &base = "params/");
+extern template void ju_get_opt_field(const nlohmann::json &j, const uint64_t &key, address_range_descriptions &value,
const std::string &base = "params/");
extern template void ju_get_opt_field(const nlohmann::json &j, const std::string &key,
- machine_memory_range_descrs &value, const std::string &base = "params/");
+ address_range_descriptions &value, const std::string &base = "params/");
extern template void ju_get_opt_field(const nlohmann::json &j, const uint64_t &key, fork_result &value,
const std::string &base = "params/");
extern template void ju_get_opt_field(const nlohmann::json &j, const std::string &key, fork_result &value,
diff --git a/src/jsonrpc-discover.json b/src/jsonrpc-discover.json
index 05b4e9fdc..7eb352224 100644
--- a/src/jsonrpc-discover.json
+++ b/src/jsonrpc-discover.json
@@ -712,14 +712,14 @@
}
},
{
- "name": "machine.get_memory_ranges",
- "summary": "Returns a list with descriptions for all of the machine's memory ranges",
+ "name": "machine.get_address_ranges",
+ "summary": "Returns a list with descriptions for all of the machine's address ranges",
"params": [],
"result": {
"name": "ranges",
- "description": "Array of memory range descriptions",
+ "description": "Array of address range descriptions",
"schema": {
- "$ref": "#/components/schemas/MemoryRangeDescriptionArray"
+ "$ref": "#/components/schemas/AddressRangeDescriptionArray"
}
}
},
@@ -1991,8 +1991,8 @@
"htif_fromhost_data"
]
},
- "MemoryRangeDescription": {
- "title": "MemoryRangeDescription",
+ "AddressRangeDescription": {
+ "title": "AddressRangeDescription",
"type": "object",
"properties": {
"start": {
@@ -2006,11 +2006,11 @@
}
}
},
- "MemoryRangeDescriptionArray": {
- "title": "MemoryRangeDescriptionArray",
+ "AddressRangeDescriptionArray": {
+ "title": "AddressRangeDescriptionArray",
"type": "array",
"items": {
- "$ref": "#/components/schemas/MemoryRangeDescription"
+ "$ref": "#/components/schemas/AddressRangeDescription"
}
}
}
diff --git a/src/jsonrpc-machine-c-api.cpp b/src/jsonrpc-machine-c-api.cpp
index e5fb86863..646331089 100644
--- a/src/jsonrpc-machine-c-api.cpp
+++ b/src/jsonrpc-machine-c-api.cpp
@@ -16,7 +16,6 @@
#include "jsonrpc-machine-c-api.h"
-#include
#include
#include
#include
diff --git a/src/jsonrpc-remote-machine.cpp b/src/jsonrpc-remote-machine.cpp
index 59484deab..09b0b5172 100644
--- a/src/jsonrpc-remote-machine.cpp
+++ b/src/jsonrpc-remote-machine.cpp
@@ -1334,16 +1334,16 @@ static json jsonrpc_machine_verify_dirty_page_maps_handler(const json &j,
return jsonrpc_response_ok(j, session->handler->machine->verify_dirty_page_maps());
}
-/// \brief JSONRPC handler for the machine.get_memory_ranges method
+/// \brief JSONRPC handler for the machine.get_address_ranges method
/// \param j JSON request object
/// \param session HTTP session
/// \returns JSON response object
-static json jsonrpc_machine_get_memory_ranges_handler(const json &j, const std::shared_ptr &session) {
+static json jsonrpc_machine_get_address_ranges_handler(const json &j, const std::shared_ptr &session) {
if (!session->handler->machine) {
return jsonrpc_response_invalid_request(j, "no machine");
}
jsonrpc_check_no_params(j);
- return jsonrpc_response_ok(j, session->handler->machine->get_memory_ranges());
+ return jsonrpc_response_ok(j, session->handler->machine->get_address_ranges());
}
/// \brief JSONRPC handler for the machine.send_cmio_response method
@@ -1483,7 +1483,7 @@ static json jsonrpc_dispatch_method(const json &j, const std::shared_ptr
#include "access-log.h"
+#include "address-range-description.h"
#include "base64.h"
#include "i-virtual-machine.h"
#include "interpret.h"
@@ -58,7 +59,6 @@
#include "jsonrpc-fork-result.h"
#include "jsonrpc-version.h"
#include "machine-config.h"
-#include "machine-memory-range-descr.h"
#include "machine-merkle-tree.h"
#include "machine-runtime-config.h"
#include "os-features.h"
@@ -760,9 +760,9 @@ uarch_interpreter_break_reason jsonrpc_virtual_machine::do_run_uarch(uint64_t ua
return result;
}
-machine_memory_range_descrs jsonrpc_virtual_machine::do_get_memory_ranges() const {
- machine_memory_range_descrs result;
- request("machine.get_memory_ranges", std::tie(), result);
+address_range_descriptions jsonrpc_virtual_machine::do_get_address_ranges() const {
+ address_range_descriptions result;
+ request("machine.get_address_ranges", std::tie(), result);
return result;
}
diff --git a/src/jsonrpc-virtual-machine.h b/src/jsonrpc-virtual-machine.h
index d7f3c9592..164133299 100644
--- a/src/jsonrpc-virtual-machine.h
+++ b/src/jsonrpc-virtual-machine.h
@@ -22,11 +22,11 @@
#include
#include "access-log.h"
+#include "address-range-description.h"
#include "i-virtual-machine.h"
#include "interpret.h"
#include "jsonrpc-fork-result.h"
#include "machine-config.h"
-#include "machine-memory-range-descr.h"
#include "machine-merkle-tree.h"
#include "machine-runtime-config.h"
#include "semantic-version.h"
@@ -123,7 +123,7 @@ class jsonrpc_virtual_machine final : public i_virtual_machine {
uint64_t do_read_word(uint64_t address) const override;
bool do_verify_merkle_tree() const override;
uarch_interpreter_break_reason do_run_uarch(uint64_t uarch_cycle_end) override;
- machine_memory_range_descrs do_get_memory_ranges() const override;
+ address_range_descriptions do_get_address_ranges() const override;
void do_send_cmio_response(uint16_t reason, const unsigned char *data, uint64_t length) override;
access_log do_log_send_cmio_response(uint16_t reason, const unsigned char *data, uint64_t length,
const access_log::type &log_type) override;
diff --git a/src/machine-c-api.cpp b/src/machine-c-api.cpp
index 6f57d0468..2209b22e0 100644
--- a/src/machine-c-api.cpp
+++ b/src/machine-c-api.cpp
@@ -32,21 +32,36 @@
#include
#include "access-log.h"
-#include "htif.h"
+#include "address-range-description.h"
+#include "htif-constants.h"
#include "i-virtual-machine.h"
#include "interpret.h"
#include "json-util.h"
#include "machine-c-api-internal.h"
#include "machine-config.h"
-#include "machine-memory-range-descr.h"
#include "machine-merkle-tree.h"
#include "machine-reg.h"
#include "machine-runtime-config.h"
#include "machine.h"
#include "os-features.h"
-#include "pma-constants.h"
+#include "pma-defines.h"
#include "virtual-machine.h"
+static_assert(PMA_CMIO_RX_BUFFER_START_DEF == CM_PMA_CMIO_RX_BUFFER_START);
+static_assert(PMA_CMIO_RX_BUFFER_LOG2_SIZE_DEF == CM_PMA_CMIO_RX_BUFFER_LOG2_SIZE);
+static_assert(PMA_CMIO_TX_BUFFER_START_DEF == CM_PMA_CMIO_TX_BUFFER_START);
+static_assert(PMA_CMIO_TX_BUFFER_LOG2_SIZE_DEF == CM_PMA_CMIO_TX_BUFFER_LOG2_SIZE);
+static_assert(PMA_RAM_START_DEF == CM_PMA_RAM_START);
+
+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);
+
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
static THREAD_LOCAL std::string last_err_msg;
@@ -973,12 +988,12 @@ cm_error cm_destroy(cm_machine *m) try {
return cm_result_failure();
}
-cm_error cm_get_memory_ranges(const cm_machine *m, const char **ranges) try {
+cm_error cm_get_address_ranges(const cm_machine *m, const char **ranges) try {
if (ranges == nullptr) {
throw std::invalid_argument("invalid memory range output");
}
const auto *cpp_m = convert_from_c(m);
- const cartesi::machine_memory_range_descrs cpp_ranges = cpp_m->get_memory_ranges();
+ const cartesi::address_range_descriptions cpp_ranges = cpp_m->get_address_ranges();
*ranges = cm_set_temp_string(cartesi::to_json(cpp_ranges).dump());
return cm_result_success();
} catch (...) {
diff --git a/src/machine-c-api.h b/src/machine-c-api.h
index 54b09739c..515863261 100644
--- a/src/machine-c-api.h
+++ b/src/machine-c-api.h
@@ -472,12 +472,12 @@ CM_API cm_error cm_replace_memory_range(cm_machine *m, uint64_t start, uint64_t
/// \returns 0 for success, non zero code for error.
CM_API cm_error cm_get_initial_config(const cm_machine *m, const char **config);
-/// \brief Returns a list with all memory ranges in the machine.
+/// \brief Returns a list with all address ranges in the machine.
/// \param m Pointer to a non-empty machine object (holds a machine instance).
-/// \param ranges Receives the memory ranges as a JSON object in a string,
+/// \param ranges Receives the address ranges as a JSON object in a string,
/// guaranteed to remain valid only until the next CM_API function is called from the same thread.
/// \returns 0 for success, non zero code for error.
-CM_API cm_error cm_get_memory_ranges(const cm_machine *m, const char **ranges);
+CM_API cm_error cm_get_address_ranges(const cm_machine *m, const char **ranges);
/// \brief Obtains the root hash of the Merkle tree.
/// \param m Pointer to a non-empty machine object (holds a machine instance).
diff --git a/src/machine-config.cpp b/src/machine-config.cpp
index df6d4e931..c52a6118d 100644
--- a/src/machine-config.cpp
+++ b/src/machine-config.cpp
@@ -21,7 +21,6 @@
#include
#include
#include
-#include
#include
#include
#include
diff --git a/src/machine-reg.h b/src/machine-reg.h
index 48f868b5c..d8865c4a9 100644
--- a/src/machine-reg.h
+++ b/src/machine-reg.h
@@ -17,8 +17,8 @@
#ifndef MACHINE_REG_H
#define MACHINE_REG_H
-#include "shadow-state.h"
-#include "shadow-uarch-state.h"
+#include "shadow-state-address-range.h"
+#include "shadow-uarch-state-address-range.h"
/// \file
/// \brief Cartesi machine registers
diff --git a/src/machine-state.h b/src/machine-state.h
index 2d3ae4dbd..8fcfa1c6e 100644
--- a/src/machine-state.h
+++ b/src/machine-state.h
@@ -14,21 +14,19 @@
// with this program (see COPYING). If not, see .
//
-#ifndef STATE_H
-#define STATE_H
+#ifndef MACHINE_STATE_H
+#define MACHINE_STATE_H
/// \file
/// \brief Cartesi machine state structure definition.
#include
#include
+#include
-#include
-
-#include "pma-constants.h"
-#include "pma.h"
+#include "address-range.h"
#include "riscv-constants.h"
-#include "shadow-tlb.h"
+#include "tlb.h"
namespace cartesi {
@@ -116,10 +114,7 @@ struct machine_state {
/// Soft yield
bool soft_yield{};
- /// Map of physical memory ranges
- boost::container::static_vector pmas;
-
- pma_entry empty_pma; ///< fallback to PMA for empty range
+ std::vector pmas; ///< Indices of address ranges that interpret can find
};
} // namespace cartesi
diff --git a/src/machine.cpp b/src/machine.cpp
index d4f37911a..64aa4fc7b 100644
--- a/src/machine.cpp
+++ b/src/machine.cpp
@@ -25,6 +25,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -32,29 +33,28 @@
#include
#include
#include
-
-#include
+#include
#include "access-log.h"
-#include "clint-factory.h"
+#include "address-range-description.h"
+#include "address-range.h"
+#include "clint-address-range.h"
#include "compiler-defines.h"
#include "device-state-access.h"
#include "dtb.h"
#include "host-addr.h"
-#include "htif-factory.h"
-#include "htif.h"
+#include "htif-address-range.h"
+#include "htif-constants.h"
#include "i-device-state-access.h"
#include "i-hasher.h"
#include "interpret.h"
#include "is-pristine.h"
#include "machine-config.h"
-#include "machine-memory-range-descr.h"
#include "machine-reg.h"
#include "machine-runtime-config.h"
-#include "plic-factory.h"
+#include "memory-address-range.h"
+#include "plic-address-range.h"
#include "pma-constants.h"
-#include "pma-defines.h"
-#include "pma.h"
#include "record-send-cmio-state-access.h"
#include "record-step-state-access.h"
#include "replay-send-cmio-state-access.h"
@@ -62,14 +62,10 @@
#include "riscv-constants.h"
#include "rtc.h"
#include "send-cmio-response.h"
-#include "shadow-pmas-factory.h"
-#include "shadow-pmas.h"
-#include "shadow-state-factory.h"
-#include "shadow-state.h"
-#include "shadow-tlb-factory.h"
-#include "shadow-tlb.h"
-#include "shadow-uarch-state-factory.h"
-#include "shadow-uarch-state.h"
+#include "shadow-pmas-address-range.h"
+#include "shadow-state-address-range.h"
+#include "shadow-tlb-address-range.h"
+#include "shadow-uarch-state-address-range.h"
#include "state-access.h"
#include "strict-aliasing.h"
#include "tlb.h"
@@ -85,13 +81,10 @@
#include "uarch-state-access.h"
#include "uarch-step.h"
#include "unique-c-ptr.h"
-#include "virtio-console.h"
-#include "virtio-device.h"
-#include "virtio-factory.h"
-#include "virtio-net-carrier-slirp.h"
-#include "virtio-net-carrier-tuntap.h"
-#include "virtio-net.h"
-#include "virtio-p9fs.h"
+#include "virtio-console-address-range.h"
+#include "virtio-net-tuntap-address-range.h"
+#include "virtio-net-user-address-range.h"
+#include "virtio-p9fs-address-range.h"
/// \file
/// \brief Cartesi machine implementation
@@ -100,115 +93,55 @@ namespace cartesi {
using namespace std::string_literals;
-const pma_entry::flags machine::m_ram_flags{.R = true,
- .W = true,
- .X = true,
- .IR = true,
- .IW = true,
- .DID = PMA_ISTART_DID::memory};
-
-// When we pass a RNG seed in a FDT stored in DTB,
-// Linux will wipe out its contents as a security measure,
-// therefore we need to make DTB writable, otherwise boot will hang.
-const pma_entry::flags machine::m_dtb_flags{.R = true,
- .W = true,
- .X = true,
- .IR = true,
- .IW = true,
- .DID = PMA_ISTART_DID::memory};
-
-const pma_entry::flags machine::m_flash_drive_flags{.R = true,
- .W = true,
- .X = false,
- .IR = true,
- .IW = true,
- .DID = PMA_ISTART_DID::flash_drive};
-
-const pma_entry::flags machine::m_cmio_rx_buffer_flags{.R = true,
- .W = false,
- .X = false,
- .IR = true,
- .IW = true,
- .DID = PMA_ISTART_DID::cmio_rx_buffer};
-
-const pma_entry::flags machine::m_cmio_tx_buffer_flags{.R = true,
- .W = true,
- .X = false,
- .IR = true,
- .IW = true,
- .DID = PMA_ISTART_DID::cmio_tx_buffer};
-
-pma_entry machine::make_memory_range_pma_entry(const std::string &description, const memory_range_config &c) {
- if (c.image_filename.empty()) {
- return make_callocd_memory_pma_entry(description, c.start, c.length);
- }
- return make_mmapd_memory_pma_entry(description, c.start, c.length, c.image_filename, c.shared);
-}
-
-pma_entry machine::make_flash_drive_pma_entry(const std::string &description, const memory_range_config &c) {
- return make_memory_range_pma_entry(description, c).set_flags(m_flash_drive_flags);
-}
-
-pma_entry machine::make_cmio_rx_buffer_pma_entry(const cmio_buffer_config &c) {
- const auto description = "cmio rx buffer memory range"s;
- if (!c.image_filename.empty()) {
- return make_mmapd_memory_pma_entry(description, PMA_CMIO_RX_BUFFER_START, PMA_CMIO_RX_BUFFER_LENGTH,
- c.image_filename, c.shared)
- .set_flags(m_cmio_rx_buffer_flags);
- }
- return make_callocd_memory_pma_entry(description, PMA_CMIO_RX_BUFFER_START, PMA_CMIO_RX_BUFFER_LENGTH)
- .set_flags(m_cmio_rx_buffer_flags);
-}
-
-pma_entry machine::make_cmio_tx_buffer_pma_entry(const cmio_buffer_config &c) {
- const auto description = "cmio tx buffer memory range"s;
- if (!c.image_filename.empty()) {
- return make_mmapd_memory_pma_entry(description, PMA_CMIO_TX_BUFFER_START, PMA_CMIO_TX_BUFFER_LENGTH,
- c.image_filename, c.shared)
- .set_flags(m_cmio_tx_buffer_flags);
- }
- return make_callocd_memory_pma_entry(description, PMA_CMIO_TX_BUFFER_START, PMA_CMIO_TX_BUFFER_LENGTH)
- .set_flags(m_cmio_tx_buffer_flags);
-}
-
-pma_entry &machine::register_pma_entry(pma_entry &&pma) {
- if (decltype(m_s.pmas)::capacity() <= m_s.pmas.size()) {
- throw std::runtime_error{"too many PMAs when adding "s + pma.get_description()};
- }
- auto start = pma.get_start();
- if ((start & (PMA_PAGE_SIZE - 1)) != 0) {
- throw std::invalid_argument{"start of "s + pma.get_description() + " ("s + std::to_string(start) +
- ") must be aligned to page boundary of "s + std::to_string(PMA_PAGE_SIZE) + " bytes"s};
- }
- auto length = pma.get_length();
- if ((length & (PMA_PAGE_SIZE - 1)) != 0) {
- throw std::invalid_argument{"length of "s + pma.get_description() + " ("s + std::to_string(length) +
- ") must be multiple of page size "s + std::to_string(PMA_PAGE_SIZE)};
- }
- // Check PMA range, when not the sentinel PMA entry
- if (length != 0 || start != 0) {
- if (length == 0) {
- throw std::invalid_argument{"length of "s + pma.get_description() + " cannot be zero"s};
- }
- // Checks if PMA is in addressable range, safe unsigned overflows
- if (start > PMA_ADDRESSABLE_MASK || (length - 1) > (PMA_ADDRESSABLE_MASK - start)) {
- throw std::invalid_argument{
- "range of "s + pma.get_description() + " must use at most 56 bits to be addressable"s};
- }
+static const auto throw_invalid_argument = [](const char *err) { throw std::invalid_argument{err}; };
+
+/// \brief Creates a memory address range.
+/// \param d Description of address range for use in error messages.
+/// \param start Target physical address where range starts.
+/// \param length Length of range, in bytes.
+/// \param f Flags for address range.
+/// \param image_filename Path to backing file.
+/// \param shared If true, changes to memory range reflect in backing file.
+/// \returns New address range with flags already set.
+/// \details If \p image_filename is non-empty, return a memory-mapped range, otherwise use calloc.
+static inline auto make_memory_address_range(const std::string &d, uint64_t start, uint64_t length, pma_flags flags,
+ const std::string &image_filename, bool shared) {
+ if (image_filename.empty() && shared) {
+ throw std::invalid_argument{"shared address range requires non-empty image filename when initializing " + d};
+ }
+ if (image_filename.empty() || length > static_cast(os_get_file_length(image_filename.c_str()))) {
+ return make_callocd_memory_address_range(d, start, length, flags, image_filename);
}
+ return make_mmapd_memory_address_range(d, start, length, flags, image_filename, shared);
+}
+
+void machine::check_address_range(const address_range &ar, register_where where) {
+ if (!where.interpret && !where.merkle) {
+ throw std::runtime_error{"address range "s + ar.get_description() + " must be registered somwhere"s};
+ }
+ if (where.interpret && m_s.pmas.size() >= PMA_MAX) {
+ throw std::runtime_error{"too many address ranges when adding "s + ar.get_description()};
+ }
+ const auto start = ar.get_start();
+ const auto length = ar.get_length();
+ // Checks if new range is machine addressable space (safe unsigned overflows)
+ if (start > PMA_ADDRESSABLE_MASK || (length > 0 && (length - 1) > (PMA_ADDRESSABLE_MASK - start))) {
+ throw std::invalid_argument{
+ "address range of "s + ar.get_description() + " must use at most 56 bits to be addressable"s};
+ }
+ const auto length_bit_ceil = ar.get_length_bit_ceil();
// Range A overlaps with B if A starts before B ends and A ends after B starts
- for (const auto &existing_pma : m_s.pmas) {
- if (start < existing_pma.get_start() + existing_pma.get_length() && start + length > existing_pma.get_start()) {
- throw std::invalid_argument{"range of "s + pma.get_description() + " overlaps with range of existing "s +
- existing_pma.get_description()};
+ for (const auto &existing : m_ars) {
+ const auto existing_start = existing->get_start();
+ const auto existing_length_bit_ceil = existing->get_length_bit_ceil();
+ if (start < existing_start + existing_length_bit_ceil && start + length_bit_ceil > existing_start) {
+ throw std::invalid_argument{"address range of "s + ar.get_description() +
+ " overlaps with address range of existing "s + existing->get_description()};
}
}
- pma.set_index(static_cast(m_s.pmas.size()));
- m_s.pmas.push_back(std::move(pma));
- return m_s.pmas.back();
}
-static bool DID_is_protected(PMA_ISTART_DID DID) {
+static bool is_protected(PMA_ISTART_DID DID) {
switch (DID) {
case PMA_ISTART_DID::memory:
case PMA_ISTART_DID::flash_drive:
@@ -220,15 +153,16 @@ static bool DID_is_protected(PMA_ISTART_DID DID) {
}
}
-void machine::replace_memory_range(const memory_range_config &range) {
- for (auto &pma : m_s.pmas) {
- if (pma.get_start() == range.start && pma.get_length() == range.length) {
- const auto curr = pma.get_istart_DID();
- if (pma.get_length() == 0 || DID_is_protected(curr)) {
- throw std::invalid_argument{"attempt to replace a protected range "s + pma.get_description()};
+void machine::replace_memory_range(const memory_range_config &config) {
+ for (auto &existing : m_ars) {
+ if (existing->get_start() == config.start && existing->get_length() == config.length) {
+ if (!existing->is_memory() || is_protected(existing->get_driver_id())) {
+ throw std::invalid_argument{"attempt to replace a protected range "s + existing->get_description()};
}
- // replace range preserving original flags
- pma = make_memory_range_pma_entry(pma.get_description(), range).set_flags(pma.get_flags());
+ // Replace range, preserving original flags.
+ // This will automatically start with all pages dirty.
+ existing = make_moved_unique(make_memory_address_range(existing->get_description(), existing->get_start(),
+ existing->get_length(), existing->get_flags(), config.image_filename, config.shared));
return;
}
}
@@ -245,22 +179,35 @@ void machine::init_uarch(const uarch_config &c) {
write_reg(machine_reg_enum(reg::uarch_x0, i), c.processor.x[i]);
}
// Register shadow state
- m_us.shadow_state = make_shadow_uarch_state_pma_entry(PMA_SHADOW_UARCH_STATE_START, PMA_SHADOW_UARCH_STATE_LENGTH);
+ m_us.shadow_state = ®ister_address_range(make_shadow_uarch_state_address_range(PMA_SHADOW_UARCH_STATE_START,
+ PMA_SHADOW_UARCH_STATE_LENGTH, throw_invalid_argument),
+ register_where{.merkle = true, .interpret = false});
// Register RAM
+ if (uarch_pristine_ram_len > PMA_UARCH_RAM_LENGTH) {
+ throw std::runtime_error("embedded uarch RAM image does not fit in uarch memory");
+ }
+ static constexpr pma_flags uram_flags{
+ .M = true,
+ .IO = false,
+ .E = false,
+ .R = true,
+ .W = true,
+ .X = true,
+ .IR = true,
+ .IW = true,
+ .DID = PMA_ISTART_DID::memory,
+ };
constexpr auto ram_description = "uarch RAM";
- if (!c.ram.image_filename.empty()) {
- // Load RAM image from file
- m_us.ram =
- make_callocd_memory_pma_entry(ram_description, PMA_UARCH_RAM_START, UARCH_RAM_LENGTH, c.ram.image_filename)
- .set_flags(m_ram_flags);
+ if (c.ram.image_filename.empty()) {
+ m_us.ram = ®ister_address_range(
+ make_callocd_memory_address_range(ram_description, PMA_UARCH_RAM_START, UARCH_RAM_LENGTH, uram_flags),
+ register_where{.merkle = true, .interpret = false});
+ memcpy(m_us.ram->get_host_memory(), uarch_pristine_ram, uarch_pristine_ram_len);
} else {
- // Load embedded pristine RAM image
- m_us.ram = make_callocd_memory_pma_entry(ram_description, PMA_UARCH_RAM_START, PMA_UARCH_RAM_LENGTH)
- .set_flags(m_ram_flags);
- if (uarch_pristine_ram_len > m_us.ram.get_length()) {
- throw std::runtime_error("embedded uarch RAM image does not fit in uarch ram PMA");
- }
- memcpy(m_us.ram.get_memory().get_host_memory(), uarch_pristine_ram, uarch_pristine_ram_len);
+ m_us.ram =
+ ®ister_address_range(make_memory_address_range(ram_description, PMA_UARCH_RAM_START, UARCH_RAM_LENGTH,
+ uram_flags, c.ram.image_filename, false /* not shared */),
+ register_where{.merkle = true, .interpret = false});
}
}
@@ -334,19 +281,47 @@ void machine::init_processor(processor_config &p, const machine_runtime_config &
write_reg(reg::iunrep, p.iunrep);
}
-void machine::init_ram_pma(const ram_config &ram) {
- register_pma_entry(
- make_callocd_memory_pma_entry("RAM"s, PMA_RAM_START, ram.length, ram.image_filename).set_flags(m_ram_flags));
-}
-
-void machine::init_flash_drive_pmas(flash_drive_configs &flash_drive) {
+void machine::init_ram_ar(const ram_config &ram) {
+ // Flags for RAM
+ static constexpr pma_flags ram_flags{
+ .M = true,
+ .IO = false,
+ .E = false,
+ .R = true,
+ .W = true,
+ .X = true,
+ .IR = true,
+ .IW = true,
+ .DID = PMA_ISTART_DID::memory,
+ };
+ if (ram.length == 0) {
+ throw std::invalid_argument("RAM length cannot be zero");
+ }
+ register_address_range(
+ make_callocd_memory_address_range("RAM"s, PMA_RAM_START, ram.length, ram_flags, ram.image_filename),
+ register_where{.merkle = true, .interpret = true});
+}
+
+void machine::init_flash_drive_ars(flash_drive_configs &flash_drive) {
+ // Flags for flash drives
+ static const pma_flags flash_flags{
+ .M = true,
+ .IO = false,
+ .E = false,
+ .R = true,
+ .W = true,
+ .X = false,
+ .IR = true,
+ .IW = true,
+ .DID = PMA_ISTART_DID::flash_drive,
+ };
// Register all flash drives
int i = 0; // NOLINT(misc-const-correctness)
for (auto &f : flash_drive) {
const std::string flash_description = "flash drive "s + std::to_string(i);
// Auto detect flash drive start address
if (f.start == UINT64_C(-1)) {
- f.start = PMA_DRIVE_START + PMA_DRIVE_OFFSET_DEF * i;
+ f.start = PMA_DRIVE_START + PMA_DRIVE_OFFSET * i;
}
// Auto detect flash drive image length
if (f.length == UINT64_C(-1)) {
@@ -364,69 +339,80 @@ void machine::init_flash_drive_pmas(flash_drive_configs &flash_drive) {
}
f.length = length;
}
- register_pma_entry(make_flash_drive_pma_entry(flash_description, f));
+ register_address_range(
+ make_memory_address_range(flash_description, f.start, f.length, flash_flags, f.image_filename, f.shared),
+ register_where{.merkle = true, .interpret = true});
i++;
}
}
-void machine::init_virtio_pmas(const virtio_configs &v, uint64_t iunrep) {
+void machine::init_virtio_ars(const virtio_configs &cs, uint64_t iunrep) {
// Initialize VirtIO devices
- if (!v.empty()) {
- // VirtIO devices are disallowed in unreproducible mode
- if (iunrep == 0) {
- throw std::invalid_argument{"virtio devices are only supported in unreproducible machines"};
- }
- for (const auto &vdev_config_entry : v) {
- std::visit(
- [&](const auto &vdev_config) {
- using T = std::decay_t;
- std::string pma_name = "VirtIO device"; // NOLINT(misc-const-correctness): // no, can't be const
- std::unique_ptr vdev;
- if constexpr (std::is_same_v) {
- pma_name = "VirtIO Console";
- vdev = std::make_unique(m_vdevs.size());
- } else if constexpr (std::is_same_v) {
+ if (cs.empty()) {
+ return;
+ }
+ // VirtIO devices are disallowed in unreproducible mode
+ if (iunrep == 0) {
+ throw std::invalid_argument{"virtio devices are only supported in unreproducible machines"};
+ }
+ uint32_t virtio_idx = 0;
+ for (const auto &c : cs) {
+ const auto where = register_where{.merkle = false, .interpret = true};
+ const auto visitor = overloads{
+ [this, virtio_idx, where](const virtio_console_config &) {
+ const auto start = PMA_FIRST_VIRTIO_START + (virtio_idx * PMA_VIRTIO_LENGTH);
+ register_address_range(make_virtio_console_address_range(start, PMA_VIRTIO_LENGTH, virtio_idx), where);
+ },
+ [this, virtio_idx, where](const virtio_p9fs_config &c) {
#ifdef HAVE_POSIX_FS
- pma_name = "VirtIO 9P";
- vdev = std::make_unique(m_vdevs.size(), vdev_config.tag,
- vdev_config.host_directory);
+ const auto start = PMA_FIRST_VIRTIO_START + (virtio_idx * PMA_VIRTIO_LENGTH);
+ register_address_range(
+ make_virtio_p9fs_address_range(start, PMA_VIRTIO_LENGTH, virtio_idx, c.tag, c.host_directory),
+ where);
#else
- throw std::invalid_argument("virtio 9p device is unsupported in this platform");
+ (void) c;
+ (void) this;
+ (void) virtio_idx;
+ (void) where;
+ throw std::invalid_argument{"virtio 9p device is unsupported in this platform"};
#endif
- } else if constexpr (std::is_same_v) {
-#ifdef HAVE_SLIRP
- pma_name = "VirtIO Net User";
- vdev = std::make_unique(m_vdevs.size(),
- std::make_unique(vdev_config));
+ },
+ [this, virtio_idx, where](const virtio_net_tuntap_config &c) {
+#ifdef HAVE_TUNTAP
+ const auto start = PMA_FIRST_VIRTIO_START + (virtio_idx * PMA_VIRTIO_LENGTH);
+ register_address_range(
+ make_virtio_net_tuntap_address_range(start, PMA_VIRTIO_LENGTH, virtio_idx, c.iface), where);
#else
- throw std::invalid_argument("virtio network user device is unsupported in this platform");
-
+ (void) c;
+ (void) this;
+ (void) virtio_idx;
+ (void) where;
+ throw std::invalid_argument("virtio network TUN/TAP device is unsupported in this platform");
#endif
- } else if constexpr (std::is_same_v) {
-#ifdef HAVE_TUNTAP
- pma_name = "VirtIO Net TUN/TAP";
- vdev = std::make_unique(m_vdevs.size(),
- std::make_unique(vdev_config.iface));
+ },
+ [this, virtio_idx, where](const virtio_net_user_config &c) {
+#ifdef HAVE_SLIRP
+ const auto start = PMA_FIRST_VIRTIO_START + (virtio_idx * PMA_VIRTIO_LENGTH);
+ register_address_range(make_virtio_net_user_address_range(start, PMA_VIRTIO_LENGTH, virtio_idx, c),
+ where);
#else
-
- throw std::invalid_argument("virtio network TUN/TAP device is unsupported in this platform");
+ (void) c;
+ (void) this;
+ (void) virtio_idx;
+ (void) where;
+ throw std::invalid_argument("virtio network user device is unsupported in this platform");
#endif
- } else {
- throw std::invalid_argument("invalid virtio device configuration");
- }
- register_pma_entry(
- make_virtio_pma_entry(PMA_FIRST_VIRTIO_START + (vdev->get_virtio_index() * PMA_VIRTIO_LENGTH),
- PMA_VIRTIO_LENGTH, pma_name, &virtio_driver, vdev.get()));
- m_vdevs.push_back(std::move(vdev));
- },
- vdev_config_entry);
- }
+ },
+ [](const auto &) { throw std::invalid_argument("invalid virtio device configuration"); }};
+ std::visit(visitor, c);
+ ++virtio_idx;
}
}
-void machine::init_htif_pma(const htif_config &h, const htif_runtime_config &r, uint64_t iunrep) {
+void machine::init_htif_ar(const htif_config &h) {
// Register HTIF device
- register_pma_entry(make_htif_pma_entry(PMA_HTIF_START, PMA_HTIF_LENGTH));
+ register_address_range(make_htif_address_range(PMA_HTIF_START, PMA_HTIF_LENGTH, throw_invalid_argument),
+ register_where{.merkle = false, .interpret = true});
// Copy HTIF state to from config to machine
write_reg(reg::htif_tohost, h.tohost);
write_reg(reg::htif_fromhost, h.fromhost);
@@ -439,78 +425,93 @@ void machine::init_htif_pma(const htif_config &h, const htif_runtime_config &r,
const uint64_t htif_iyield = static_cast(h.yield_manual) << HTIF_YIELD_CMD_MANUAL |
static_cast(h.yield_automatic) << HTIF_YIELD_CMD_AUTOMATIC;
write_reg(reg::htif_iyield, htif_iyield);
- // Initialize TTY if console input is enabled
- if (h.console_getchar || has_virtio_console()) {
- if (iunrep == 0) {
- throw std::invalid_argument{"TTY stdin is only supported in unreproducible machines"};
- }
- os_open_tty();
- }
- os_silence_putchar(r.no_console_putchar);
-}
-
-void machine::init_cmio_pmas(const cmio_config &c) {
- // Register cmio memory ranges
- register_pma_entry(make_cmio_tx_buffer_pma_entry(c.tx_buffer));
- register_pma_entry(make_cmio_rx_buffer_pma_entry(c.rx_buffer));
}
-void machine::init_merkle_pmas() {
- // Include machine PMAs in set considered by the Merkle tree.
- for (auto &pma : m_s.pmas) {
- if (pma.get_length() != 0) {
- m_merkle_pmas.push_back(&pma);
- }
- }
- m_merkle_pmas.push_back(&m_us.shadow_state);
- m_merkle_pmas.push_back(&m_us.ram);
- // Sort it by increasing start address
- std::ranges::sort(m_merkle_pmas, [](const auto *a, const auto *b) { return a->get_start() < b->get_start(); });
-}
-
-void machine::init_memory_range_descrs() {
- // Initialize memory range descriptions returned by get_memory_ranges method
- for (const auto *pma : m_merkle_pmas) {
- if (pma->get_length() != 0) {
- m_mrds.push_back(machine_memory_range_descr{.start = pma->get_start(),
- .length = pma->get_length(),
- .description = pma->get_description()});
- }
- }
+void machine::init_cmio_ars(const cmio_config &c) {
+ static const pma_flags tx_flags{
+ .M = true,
+ .IO = false,
+ .E = false,
+ .R = true,
+ .W = true,
+ .X = false,
+ .IR = true,
+ .IW = true,
+ .DID = PMA_ISTART_DID::cmio_tx_buffer,
+ };
+ static const pma_flags rx_flags{
+ .M = true,
+ .IO = false,
+ .E = false,
+ .R = true,
+ .W = false,
+ .X = false,
+ .IR = true,
+ .IW = true,
+ .DID = PMA_ISTART_DID::cmio_rx_buffer,
+ };
+ register_address_range(make_memory_address_range("CMIO tx buffer memory range"s, PMA_CMIO_TX_BUFFER_START,
+ PMA_CMIO_TX_BUFFER_LENGTH, tx_flags, c.tx_buffer.image_filename, c.tx_buffer.shared),
+ register_where{.merkle = true, .interpret = true});
+ register_address_range(make_memory_address_range("CMIO rx buffer memory range"s, PMA_CMIO_RX_BUFFER_START,
+ PMA_CMIO_RX_BUFFER_LENGTH, rx_flags, c.rx_buffer.image_filename, c.rx_buffer.shared),
+ register_where{.merkle = true, .interpret = true});
+}
+
+void machine::init_merkle_ars() {
+ // Sort indices by the starting address of the range they point to in the m_ars array
+ std::ranges::sort(
+ m_merkle_ars, [](const auto &a, const auto &b) { return a.get_start() < b.get_start(); },
+ [this](const auto i) { return *m_ars[i]; });
+}
+
+void machine::init_ars_descriptions() {
+ // Initialize memory range descriptions returned by get_address_ranges method
+ auto src = m_ars | std::views::filter([](auto &ar) { return ar->get_length() != 0; }) |
+ std::views::transform([](auto &ar) {
+ return address_range_description{.start = ar->get_start(),
+ .length = ar->get_length(),
+ .description = ar->get_description()};
+ });
+ std::ranges::copy(src, std::back_inserter(m_ards));
+ std::ranges::sort(m_ards, [](auto &a, auto &b) { return a.start < b.start; });
}
-void machine::init_clint_pma(const clint_config &c) {
+void machine::init_clint_ar(const clint_config &c) {
// Register CLINT device
- register_pma_entry(make_clint_pma_entry(PMA_CLINT_START, PMA_CLINT_LENGTH));
+ register_address_range(make_clint_address_range(PMA_CLINT_START, PMA_CLINT_LENGTH, throw_invalid_argument),
+ register_where{.merkle = false, .interpret = true});
// Copy CLINT state to from config to machine
write_reg(reg::clint_mtimecmp, c.mtimecmp);
}
-void machine::init_plic_pma(const plic_config &p) {
+void machine::init_plic_ar(const plic_config &p) {
// Register PLIC device
- register_pma_entry(make_plic_pma_entry(PMA_PLIC_START, PMA_PLIC_LENGTH));
+ register_address_range(make_plic_address_range(PMA_PLIC_START, PMA_PLIC_LENGTH, throw_invalid_argument),
+ register_where{.merkle = false, .interpret = true});
// Copy PLIC state from config to machine
write_reg(reg::plic_girqpend, p.girqpend);
write_reg(reg::plic_girqsrvd, p.girqsrvd);
}
-void machine::init_sentinel_pmas() {
+void machine::init_sentinel_ars() {
// Last, add empty sentinels until we reach capacity (need at least one sentinel)
- register_pma_entry(make_empty_pma_entry("sentinel"s, 0, 0));
- // NOLINTNEXTLINE(readability-static-accessed-through-instance)
- if (m_s.pmas.capacity() != PMA_MAX) {
- throw std::logic_error{"PMAs array must be able to hold at least PMA_MAX entries"};
- }
+ // ??D I will remove these sentinels from the list
+ register_address_range(make_empty_address_range("sentinel"), register_where{.merkle = false, .interpret = true});
while (m_s.pmas.size() < PMA_MAX) {
- register_pma_entry(make_empty_pma_entry("sentinel"s, 0, 0));
+ register_address_range(make_empty_address_range("sentinel"),
+ register_where{.merkle = false, .interpret = true});
}
}
-void machine::init_shadow_pmas_contents(pma_entry &shadow_pmas) const {
- // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast)
- shadow_pmas_init(m_s.pmas,
- reinterpret_cast(shadow_pmas.get_memory_noexcept().get_host_memory()));
- // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast)
+void machine::init_shadow_pmas_contents(memory_address_range &shadow_pmas) const {
+ static_assert(sizeof(shadow_pmas_state) == PMA_MAX * 2 * sizeof(uint64_t), "inconsistent shadow PMAs length");
+ static_assert(PMA_SHADOW_PMAS_LENGTH >= sizeof(shadow_pmas_state), "shadow PMAs not long enough");
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ auto &dest = *reinterpret_cast(shadow_pmas.get_host_memory());
+ std::ranges::transform(m_s.pmas, dest.begin(), [this](auto i) {
+ return shadow_pmas_entry{.istart = m_ars[i]->get_istart(), .ilength = m_ars[i]->get_ilength()};
+ });
}
void machine::init_tlb_contents(const std::string &image_filename) {
@@ -535,9 +536,28 @@ void machine::init_tlb_contents(const std::string &image_filename) {
}
}
-void machine::init_dtb_contents(const machine_config &c, pma_entry &dtb) {
+address_range &machine::init_dtb_ar(const dtb_config &c) {
+ // When we pass a RNG seed in a FDT stored in DTB, Linux will wipe out its contents as a security measure,
+ // therefore we need to make DTB writable, otherwise boot will hang.
+ static constexpr pma_flags dtb_flags{
+ .M = true,
+ .IO = false,
+ .E = false,
+ .R = true,
+ .W = true,
+ .X = true,
+ .IR = true,
+ .IW = true,
+ .DID = PMA_ISTART_DID::memory,
+ };
+ return register_address_range(make_memory_address_range("DTB"s, PMA_DTB_START, PMA_DTB_LENGTH, dtb_flags,
+ c.image_filename, false /* not shared */),
+ register_where{.merkle = true, .interpret = true});
+}
+
+void machine::init_dtb_contents(const machine_config &c, address_range &dtb) {
if (c.dtb.image_filename.empty()) {
- dtb_init(c, dtb.get_memory().get_host_memory(), PMA_DTB_LENGTH);
+ dtb_init(c, dtb.get_host_memory(), PMA_DTB_LENGTH);
}
}
@@ -547,23 +567,26 @@ machine::machine(machine_config c, machine_runtime_config r) : m_c{std::move(c)}
init_uarch(m_c.uarch);
init_processor(m_c.processor, m_r);
m_s.soft_yield = m_r.soft_yield;
- init_ram_pma(m_c.ram);
+ init_ram_ar(m_c.ram);
// Will populate when initialization of PMAs is done
- pma_entry &dtb =
- register_pma_entry(make_callocd_memory_pma_entry("DTB"s, PMA_DTB_START, PMA_DTB_LENGTH, m_c.dtb.image_filename)
- .set_flags(m_dtb_flags));
- init_flash_drive_pmas(m_c.flash_drive);
- init_cmio_pmas(m_c.cmio);
- init_htif_pma(m_c.htif, m_r.htif, m_c.processor.iunrep);
- init_clint_pma(m_c.clint);
- init_plic_pma(m_c.plic);
+ auto &dtb = init_dtb_ar(m_c.dtb);
+ init_flash_drive_ars(m_c.flash_drive);
+ init_cmio_ars(m_c.cmio);
+ init_htif_ar(m_c.htif);
+ init_clint_ar(m_c.clint);
+ init_plic_ar(m_c.plic);
// Will populate when initialization of PMAs is done
- register_pma_entry(make_shadow_tlb_pma_entry(PMA_SHADOW_TLB_START, PMA_SHADOW_TLB_LENGTH));
- register_pma_entry(make_shadow_state_pma_entry(PMA_SHADOW_STATE_START, PMA_SHADOW_STATE_LENGTH));
+ register_address_range(
+ make_shadow_tlb_address_range(PMA_SHADOW_TLB_START, PMA_SHADOW_TLB_LENGTH, throw_invalid_argument),
+ register_where{.merkle = true, .interpret = false});
+ register_address_range(
+ make_shadow_state_address_range(PMA_SHADOW_STATE_START, PMA_SHADOW_STATE_LENGTH, throw_invalid_argument),
+ register_where{.merkle = true, .interpret = false});
// Will populate when initialization of PMAs is done
- pma_entry &shpmas = register_pma_entry(make_shadow_pmas_pma_entry(PMA_SHADOW_PMAS_START, PMA_SHADOW_PMAS_LENGTH));
- init_virtio_pmas(m_c.virtio, m_c.processor.iunrep);
- init_sentinel_pmas();
+ auto &shpmas = register_address_range(make_shadow_pmas_address_range(PMA_SHADOW_PMAS_START, PMA_SHADOW_PMAS_LENGTH),
+ register_where{.merkle = true, .interpret = true});
+ init_virtio_ars(m_c.virtio, m_c.processor.iunrep);
+ init_sentinel_ars();
// Populate shadow PMAs contents.
// This must be done after all PMA entries are already registered, so we encode them into the shadow
init_shadow_pmas_contents(shpmas);
@@ -573,14 +596,26 @@ machine::machine(machine_config c, machine_runtime_config r) : m_c{std::move(c)}
// Initialize DTB contents.
// This must be done after all PMA entries are already registered, so we can lookup flash drive parameters
init_dtb_contents(m_c, dtb);
- init_merkle_pmas();
- init_memory_range_descrs();
+ init_merkle_ars();
+ init_ars_descriptions();
+ init_tty(m_c.htif, m_r.htif, m_c.processor.iunrep);
// Disable SIGPIPE handler, because this signal can be raised and terminate the emulator process
// when calling write() on closed file descriptors.
// This can happen with the stdout console file descriptors or network file descriptors.
os_disable_sigpipe();
}
+void machine::init_tty(const htif_config &h, const htif_runtime_config &r, uint64_t iunrep) const {
+ // Initialize TTY if console input is enabled
+ if (h.console_getchar || has_virtio_console()) {
+ if (iunrep == 0) {
+ throw std::invalid_argument{"TTY stdin is only supported in unreproducible machines"};
+ }
+ os_open_tty();
+ }
+ os_silence_putchar(r.no_console_putchar);
+}
+
static void load_hash(const std::string &dir, machine::hash_type &h) {
auto name = dir + "/hash";
auto fp = make_unique_fopen(name.c_str(), "rb");
@@ -608,16 +643,16 @@ machine::machine(const std::string &dir, machine_runtime_config r) : machine{mac
}
void machine::prepare_virtio_devices_select(select_fd_sets *fds, uint64_t *timeout_us) {
- for (auto &vdev : m_vdevs) {
- vdev->prepare_select(fds, timeout_us);
+ for (auto *v : m_virtio_ars) {
+ v->prepare_select(fds, timeout_us);
}
}
// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
bool machine::poll_selected_virtio_devices(int select_ret, select_fd_sets *fds, i_device_state_access *da) {
bool interrupt_requested = false; // NOLINT(misc-const-correctness)
- for (auto &vdev : m_vdevs) {
- interrupt_requested |= vdev->poll_selected(select_ret, fds, da);
+ for (auto *v : m_virtio_ars) {
+ interrupt_requested |= v->poll_selected(select_ret, fds, da);
}
return interrupt_requested;
}
@@ -631,13 +666,13 @@ bool machine::poll_virtio_devices(uint64_t *timeout_us, i_device_state_access *d
}
bool machine::has_virtio_devices() const {
- return !m_vdevs.empty();
+ return !m_virtio_ars.empty();
}
bool machine::has_virtio_console() const {
// When present, the console device is guaranteed to be the first VirtIO device,
// therefore we only need to check the first device.
- return !m_vdevs.empty() && m_vdevs[0]->get_device_id() == VIRTIO_DEVICE_CONSOLE;
+ return has_virtio_devices() && m_virtio_ars[0]->get_device_id() == VIRTIO_DEVICE_CONSOLE;
}
bool machine::has_htif_console() const {
@@ -743,18 +778,16 @@ machine_config machine::get_serialization_config() const {
return c;
}
-static void store_device_pma(const machine &m, const pma_entry &pma, const std::string &dir) {
- if (!pma.get_istart_IO()) {
- throw std::runtime_error{"attempt to save non-device PMA"};
+static void store_device_address_range(const machine &m, const address_range &ar, const std::string &dir) {
+ if (!ar.is_device()) {
+ throw std::runtime_error{"attempt to save non-device address range "s + ar.get_description()};
}
auto scratch = make_unique_calloc(PMA_PAGE_SIZE); // will throw if it fails
- auto name = machine_config::get_image_filename(dir, pma.get_start(), pma.get_length());
- auto fp = make_unique_fopen(name.c_str(), "wb");
- for (uint64_t page_start_in_range = 0; page_start_in_range < pma.get_length();
- page_start_in_range += PMA_PAGE_SIZE) {
+ auto name = machine_config::get_image_filename(dir, ar.get_start(), ar.get_length());
+ auto fp = make_unique_fopen(name.c_str(), "wb"); // will throw if it fails
+ for (uint64_t offset = 0; offset < ar.get_length(); offset += PMA_PAGE_SIZE) {
const unsigned char *page_data = nullptr;
- auto peek = pma.get_peek();
- if (!peek(pma, m, page_start_in_range, PMA_PAGE_SIZE, &page_data, scratch.get())) {
+ if (!ar.peek(m, offset, PMA_PAGE_SIZE, &page_data, scratch.get())) {
throw std::runtime_error{"peek failed"};
}
if (page_data == nullptr) {
@@ -767,14 +800,13 @@ static void store_device_pma(const machine &m, const pma_entry &pma, const std::
}
}
-static void store_memory_pma(const pma_entry &pma, const std::string &dir) {
- if (!pma.get_istart_M()) {
- throw std::runtime_error{"attempt to save non-memory PMA"};
+static void store_memory_address_range(const address_range &ar, const std::string &dir) {
+ if (!ar.is_memory()) {
+ throw std::runtime_error{"attempt to save non-memory address-range "s + ar.get_description()};
}
- auto name = machine_config::get_image_filename(dir, pma.get_start(), pma.get_length());
+ auto name = machine_config::get_image_filename(dir, ar.get_start(), ar.get_length());
auto fp = make_unique_fopen(name.c_str(), "wb");
- const pma_memory &mem = pma.get_memory();
- if (fwrite(mem.get_host_memory(), 1, pma.get_length(), fp.get()) != pma.get_length()) {
+ if (fwrite(ar.get_host_memory(), 1, ar.get_length(), fp.get()) != ar.get_length()) {
throw std::runtime_error{"error writing to '" + name + "'"};
}
}
@@ -788,8 +820,8 @@ host_addr machine::get_host_addr(uint64_t paddr, uint64_t pma_index) const {
}
void machine::mark_dirty_page(uint64_t paddr, uint64_t pma_index) {
- auto &pma = m_s.pmas[static_cast(pma_index)];
- pma.mark_dirty_page(paddr - pma.get_start());
+ auto &ar = read_pma(pma_index);
+ ar.mark_dirty_page(paddr - ar.get_start());
}
void machine::mark_dirty_page(host_addr haddr, uint64_t pma_index) {
@@ -832,8 +864,8 @@ void machine::check_shadow_tlb(TLB_set_index set_index, uint64_t slot_index, uin
if (pma_index >= m_s.pmas.size()) {
throw std::domain_error{prefix + "pma_index is out of range"s};
}
- const auto &pma = m_s.pmas[pma_index];
- if (pma.get_length() == 0 || !pma.get_istart_M()) {
+ const auto &ar = read_pma(pma_index);
+ if (ar.get_length() == 0 || !ar.is_memory()) {
throw std::invalid_argument{prefix + "pma_index does not point to memory range"s};
}
if ((vaddr_page & PAGE_OFFSET_MASK) != 0) {
@@ -843,8 +875,8 @@ void machine::check_shadow_tlb(TLB_set_index set_index, uint64_t slot_index, uin
if ((paddr_page & PAGE_OFFSET_MASK) != 0) {
throw std::invalid_argument{prefix + "vp_offset is not aligned"s};
}
- const auto pma_end = pma.get_start() + (pma.get_length() - PMA_PAGE_SIZE);
- if (paddr_page < pma.get_start() || paddr_page > pma_end) {
+ const auto pma_end = ar.get_start() + (ar.get_length() - PMA_PAGE_SIZE);
+ if (paddr_page < ar.get_start() || paddr_page > pma_end) {
throw std::invalid_argument{prefix + "vp_offset is inconsistent with pma_index"s};
}
} else if (pma_index != TLB_INVALID_PMA_INDEX || vp_offset != 0) {
@@ -871,48 +903,40 @@ void machine::write_shadow_tlb(TLB_set_index set_index, uint64_t slot_index, uin
}
host_addr machine::get_hp_offset(uint64_t pma_index) const {
- if (pma_index >= m_s.pmas.size()) {
- throw std::domain_error{"PMA index is out of range (" + std::to_string(pma_index) + ")"};
- }
- const auto &pma = m_s.pmas[static_cast(pma_index)];
- if (!pma.get_istart_M()) {
- throw std::domain_error{"PMA is not memory (" + pma.get_description() + ")"};
+ const auto &ar = read_pma(pma_index);
+ if (!ar.is_memory()) {
+ throw std::domain_error{"PMA index is not of memory range ("s + ar.get_description() + ")"};
}
- auto haddr = cast_ptr_to_host_addr(pma.get_memory().get_host_memory());
- auto paddr = pma.get_start();
+ auto haddr = cast_ptr_to_host_addr(ar.get_host_memory());
+ auto paddr = ar.get_start();
return paddr - haddr;
}
-//??D now that m_merkle_pmas is sorted by start, maybe change this to a binary search?
-const pma_entry &machine::find_pma_entry(uint64_t paddr, uint64_t length) const {
- const static auto sentinel = make_empty_pma_entry("sentinel", 0, 0);
- for (const auto *pma : m_merkle_pmas) {
- // Check if data is in range
- if (paddr >= pma->get_start() && pma->get_length() >= length &&
- paddr - pma->get_start() <= pma->get_length() - length) {
- return *pma;
+//??D now that m_merkle_ars is sorted by start, maybe change this to a binary search?
+const address_range &machine::find_address_range(uint64_t paddr, uint64_t length) const noexcept {
+ static constexpr auto sentinel = make_empty_address_range("sentinel");
+ for (const auto &ar : m_ars) {
+ if (ar->contains_absolute(paddr, length)) {
+ return *ar;
}
}
return sentinel;
}
-void machine::store_pmas(const machine_config &c, const std::string &dir) const {
+void machine::store_address_ranges(const machine_config &c, const std::string &dir) const {
if (read_reg(reg::iunrep) != 0) {
throw std::runtime_error{"cannot store PMAs of unreproducible machines"};
}
- store_memory_pma(find_pma_entry(PMA_DTB_START), dir);
- store_memory_pma(find_pma_entry(PMA_RAM_START), dir);
- store_device_pma(*this, find_pma_entry(PMA_SHADOW_TLB_START), dir);
- // Could iterate over PMAs checking for those with a drive DID
- // but this is easier
+ store_memory_address_range(find_address_range(PMA_DTB_START), dir);
+ store_memory_address_range(find_address_range(PMA_RAM_START), dir);
+ store_device_address_range(*this, find_address_range(PMA_SHADOW_TLB_START), dir);
+ // Could iterate over PMAs checking for those with a drive DID but this is easier
for (const auto &f : c.flash_drive) {
- store_memory_pma(find_pma_entry(f.start), dir);
- }
- store_memory_pma(find_pma_entry(PMA_CMIO_RX_BUFFER_START), dir);
- store_memory_pma(find_pma_entry(PMA_CMIO_TX_BUFFER_START), dir);
- if (!m_us.ram.get_istart_E()) {
- store_memory_pma(m_us.ram, dir);
+ store_memory_address_range(find_address_range(f.start), dir);
}
+ store_memory_address_range(find_address_range(PMA_CMIO_RX_BUFFER_START), dir);
+ store_memory_address_range(find_address_range(PMA_CMIO_TX_BUFFER_START), dir);
+ store_memory_address_range(find_address_range(PMA_UARCH_RAM_START), dir);
}
static void store_hash(const machine::hash_type &h, const std::string &dir) {
@@ -937,7 +961,7 @@ void machine::store(const std::string &dir) const {
}
auto c = get_serialization_config();
c.store(dir);
- store_pmas(c, dir);
+ store_address_ranges(c, dir);
}
void machine::dump_insn_hist() {
@@ -1782,15 +1806,16 @@ void machine::mark_write_tlb_dirty_pages() const {
if (hot_slot.vaddr_page != TLB_INVALID_PAGE) {
auto haddr_page = hot_slot.vaddr_page + hot_slot.vh_offset;
const auto &cold_slot = cold_set[slot_index];
- if (cold_slot.pma_index >= m_s.pmas.size()) {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
+ auto &ar = const_cast(read_pma(cold_slot.pma_index));
+ if (ar.get_length() == 0 || !ar.is_memory()) {
throw std::runtime_error{"could not mark dirty page for a TLB entry: TLB is corrupt"};
}
auto paddr_page = get_paddr(haddr_page, cold_slot.pma_index);
- pma_entry &pma = m_s.pmas[cold_slot.pma_index];
- if (!pma.contains(paddr_page, PMA_PAGE_SIZE)) {
+ if (!ar.contains_absolute(paddr_page, PMA_PAGE_SIZE)) {
throw std::runtime_error{"could not mark dirty page for a TLB entry: TLB is corrupt"};
}
- pma.mark_dirty_page(paddr_page - pma.get_start());
+ ar.mark_dirty_page(paddr_page - ar.get_start());
}
}
}
@@ -1807,19 +1832,17 @@ bool machine::verify_dirty_page_maps() const {
// Go over the write TLB and mark as dirty all pages currently there
mark_write_tlb_dirty_pages();
// Now go over all memory PMAs verifying that all dirty pages are marked
- for (const auto &pma : m_s.pmas) {
- auto peek = pma.get_peek();
- for (uint64_t page_start_in_range = 0; page_start_in_range < pma.get_length();
- page_start_in_range += PMA_PAGE_SIZE) {
- const uint64_t page_address = pma.get_start() + page_start_in_range;
- if (pma.get_istart_M()) {
+ for (const auto &ar : m_ars) {
+ for (uint64_t offset = 0; offset < ar->get_length(); offset += PMA_PAGE_SIZE) {
+ const uint64_t page_address = ar->get_start() + offset;
+ if (ar->is_memory()) {
const unsigned char *page_data = nullptr;
- peek(pma, *this, page_start_in_range, PMA_PAGE_SIZE, &page_data, scratch.get());
+ ar->peek(*this, offset, PMA_PAGE_SIZE, &page_data, scratch.get());
hash_type stored;
hash_type real;
m_t.get_page_node_hash(page_address, stored);
m_t.get_page_node_hash(h, page_data, real);
- const bool marked_dirty = pma.is_page_marked_dirty(page_start_in_range);
+ const bool marked_dirty = ar->is_page_marked_dirty(offset);
const bool is_dirty = (real != stored);
if (is_dirty && !marked_dirty) {
broken = true;
@@ -1829,8 +1852,8 @@ bool machine::verify_dirty_page_maps() const {
std::cerr << " got " << real << '\n';
break;
}
- } else if (pma.get_istart_IO()) {
- if (!pma.is_page_marked_dirty(page_start_in_range)) {
+ } else if (ar->is_device()) {
+ if (!ar->is_page_marked_dirty(offset)) {
broken = true;
std::cerr << std::setfill('0') << std::setw(8) << std::hex << page_address
<< " should have been dirty\n";
@@ -1856,10 +1879,9 @@ bool machine::update_merkle_tree() const {
mark_write_tlb_dirty_pages();
// Now go over all PMAs and updating the Merkle tree
m_t.begin_update();
- for (const auto &pma : m_merkle_pmas) {
- auto peek = pma->get_peek();
+ for (auto &ar : m_merkle_ars | std::views::transform([this](auto i) -> address_range & { return *m_ars[i]; })) {
// Each PMA has a number of pages
- auto pages_in_range = (pma->get_length() + PMA_PAGE_SIZE - 1) / PMA_PAGE_SIZE;
+ auto pages_in_range = (ar.get_length() + PMA_PAGE_SIZE - 1) / PMA_PAGE_SIZE;
// For each PMA, we launch as many threads (n) as defined on concurrency
// runtime config or as the hardware supports.
const uint64_t n = get_task_concurrency(m_r.concurrency.update_merkle_tree);
@@ -1872,15 +1894,15 @@ bool machine::update_merkle_tree() const {
// Thread j is responsible for page i if i % n == j.
for (uint64_t i = j; i < pages_in_range; i += n) {
const uint64_t page_start_in_range = i * PMA_PAGE_SIZE;
- const uint64_t page_address = pma->get_start() + page_start_in_range;
+ const uint64_t page_address = ar.get_start() + page_start_in_range;
const unsigned char *page_data = nullptr;
// Skip any clean pages
- if (!pma->is_page_marked_dirty(page_start_in_range)) {
+ if (!ar.is_page_marked_dirty(page_start_in_range)) {
continue;
}
// If the peek failed, or if it returned a page for update but
// we failed updating it, the entire process failed
- if (!peek(*pma, *this, page_start_in_range, PMA_PAGE_SIZE, &page_data, scratch.get())) {
+ if (!ar.peek(*this, page_start_in_range, PMA_PAGE_SIZE, &page_data, scratch.get())) {
return false;
}
if (page_data != nullptr) {
@@ -1914,7 +1936,7 @@ bool machine::update_merkle_tree() const {
return false;
}
// Otherwise, mark all pages in PMA as clean and move on to next
- pma->mark_pages_clean();
+ ar.mark_pages_clean();
}
const bool ret = m_t.end_update(gh);
return ret;
@@ -1925,8 +1947,8 @@ bool machine::update_merkle_tree_page(uint64_t address) {
"PMA and machine_merkle_tree page sizes must match");
// Align address to beginning of page
address &= ~(PMA_PAGE_SIZE - 1);
- auto &pma = find_pma_entry(address);
- const uint64_t page_start_in_range = address - pma.get_start();
+ auto &ar = find_address_range(address);
+ const uint64_t page_start_in_range = address - ar.get_start();
machine_merkle_tree::hasher_type h;
auto scratch = make_unique_calloc(PMA_PAGE_SIZE, std::nothrow_t{});
if (!scratch) {
@@ -1934,13 +1956,12 @@ bool machine::update_merkle_tree_page(uint64_t address) {
}
m_t.begin_update();
const unsigned char *page_data = nullptr;
- auto peek = pma.get_peek();
- if (!peek(pma, *this, page_start_in_range, PMA_PAGE_SIZE, &page_data, scratch.get())) {
+ if (!ar.peek(*this, page_start_in_range, PMA_PAGE_SIZE, &page_data, scratch.get())) {
m_t.end_update(h);
return false;
}
if (page_data != nullptr) {
- const uint64_t page_address = pma.get_start() + page_start_in_range;
+ const uint64_t page_address = ar.get_start() + page_start_in_range;
hash_type hash;
m_t.get_page_node_hash(h, page_data, hash);
if (!m_t.update_page_node_hash(page_address, hash)) {
@@ -1948,7 +1969,7 @@ bool machine::update_merkle_tree_page(uint64_t address) {
return false;
}
}
- pma.mark_clean_page(page_start_in_range);
+ ar.mark_clean_page(page_start_in_range);
return m_t.end_update(h);
}
@@ -2038,17 +2059,16 @@ machine_merkle_tree::proof_type machine::get_proof(uint64_t address, int log2_si
// or entirely outside it.
if (log2_size < machine_merkle_tree::get_log2_page_size()) {
const uint64_t length = UINT64_C(1) << log2_size;
- const auto &pma = find_pma_entry(address, length);
+ const auto &ar = find_address_range(address, length);
auto scratch = make_unique_calloc(PMA_PAGE_SIZE);
const unsigned char *page_data = nullptr;
// If the PMA range is empty, we know the desired range is
// entirely outside of any non-pristine PMA.
// Therefore, the entire page where it lies is also pristine
// Otherwise, the entire desired range is inside it.
- if (!pma.get_istart_E()) {
- const uint64_t page_start_in_range = (address - pma.get_start()) & (~(PMA_PAGE_SIZE - 1));
- auto peek = pma.get_peek();
- if (!peek(pma, *this, page_start_in_range, PMA_PAGE_SIZE, &page_data, scratch.get())) {
+ if (ar.get_length() != 0) {
+ const uint64_t page_start_in_range = (address - ar.get_start()) & (~(PMA_PAGE_SIZE - 1));
+ if (!ar.peek(*this, page_start_in_range, PMA_PAGE_SIZE, &page_data, scratch.get())) {
throw std::runtime_error{"PMA peek failed"};
}
}
@@ -2066,6 +2086,27 @@ machine_merkle_tree::proof_type machine::get_proof(uint64_t address, int log2_si
return get_proof(address, log2_size, skip_merkle_tree_update);
}
+template
+static inline void foreach_aligned_chunk(uint64_t start, uint64_t length, uint64_t alignment, F f) {
+ // Optional first chunk brings start to alignment
+ if (const auto rem = start % alignment; rem != 0) {
+ const auto first_length = std::min(length, alignment - rem);
+ f(start, first_length);
+ start += first_length;
+ length -= first_length;
+ }
+ // Intermediate chunks start aligned and cover exactly alignment bytes
+ while (length >= alignment) {
+ f(start, alignment);
+ start += alignment;
+ length -= alignment;
+ }
+ // Last chunk completes the span
+ if (length != 0) {
+ f(start, length);
+ }
+}
+
void machine::read_memory(uint64_t paddr, unsigned char *data, uint64_t length) const {
if (length == 0) {
return;
@@ -2073,6 +2114,8 @@ void machine::read_memory(uint64_t paddr, unsigned char *data, uint64_t length)
if (data == nullptr) {
throw std::invalid_argument{"invalid data buffer"};
}
+ //??D this loop can be much improved and simplified... I don't remember why I am going page
+ //??D by page when inside an occupied address range... Will fix.
// Compute the distance between the initial paddr and the first page boundary
const uint64_t align_paddr = (paddr & PAGE_OFFSET_MASK) != 0 ? (paddr | PAGE_OFFSET_MASK) + 1 : paddr;
uint64_t align_length = align_paddr - paddr;
@@ -2080,25 +2123,24 @@ void machine::read_memory(uint64_t paddr, unsigned char *data, uint64_t length)
align_length = (align_length == 0) ? page_size : align_length;
// First peek goes at most to the next page boundary, or up to length
uint64_t peek_length = std::min(align_length, length);
- // The outer loop finds the PMA for all peeks performed by the inner loop
- // The inner loop peeks at most min(page_size, length) from the PMA per iteration
+ // The outer loop finds the address range for all peeks performed by the inner loop
+ // The inner loop peeks at most min(page_size, length) from the range per iteration
// All peeks but the absolute first peek start at a page boundary.
// That first peek reads at most up to the next page boundary.
// So the inner loop iterations never cross page boundaries.
for (;;) {
- const auto &pma = find_pma_entry(paddr, peek_length);
- const auto peek = pma.get_peek();
- const auto pma_start = pma.get_start();
- const auto pma_empty = pma.get_istart_E();
- const auto pma_length = pma.get_length();
- // If the PMA is empty, the inner loop will break after a single iteration.
+ const auto &ar = find_address_range(paddr, peek_length);
+ const auto pma_start = ar.get_start();
+ const auto occupied = ar.get_length() != 0;
+ const auto pma_length = ar.get_length();
+ // If we are in an unoccupied range, the inner loop will break after a single iteration.
// But it is safe to return pristine data for that one iteration, without even peeking.
// This is because the inner iteration never reads past a page boundary, and the next
- // non-empty PMA starts at the earliest on the next page boundary after paddr.
+ // occupied range starts at the earliest on the next page boundary after paddr.
for (;;) {
const unsigned char *peek_data = nullptr;
- // If non-empty PMA, peek, otherwise leave peek_data as nullptr (i.e. pristine)
- if (!pma_empty && !peek(pma, *this, paddr - pma_start, peek_length, &peek_data, data)) {
+ // If we found an occupied range, peek, otherwise leave peek_data as nullptr (i.e. pristine)
+ if (occupied && !ar.peek(*this, paddr - pma_start, peek_length, &peek_data, data)) {
throw std::runtime_error{"peek failed"};
}
// If the chunk is pristine, copy zero data to buffer
@@ -2117,13 +2159,13 @@ void machine::read_memory(uint64_t paddr, unsigned char *data, uint64_t length)
paddr += peek_length;
data += peek_length;
peek_length = std::min(page_size, length);
- // If the PMA was empty, break to check if next read is in another PMA
- if (pma_empty) {
+ // If we are outside any valid range, break to check if next read hits a valid range
+ if (!occupied) {
break;
}
- // If the next read does not fit in current PMA, break to get the next one
+ // If the next read does not fit in current range, break to get the next one
// There can be no overflow in the condition.
- // Since the PMA is non-empty, (paddr-pma_start) >= 0.
+ // Since the range is occupied, (paddr-pma_start) >= 0.
// Moreover, pma_length >= page_size.
// Since, peek_length <= page_size, we get (pma_length-peek_length) >= 0.
if (paddr - pma_start >= pma_length - peek_length) {
@@ -2140,28 +2182,51 @@ void machine::write_memory(uint64_t paddr, const unsigned char *data, uint64_t l
if (data == nullptr) {
throw std::invalid_argument{"invalid data buffer"};
}
- auto &pma = find_pma_entry(paddr, length);
- if (pma.get_istart_IO()) {
+ auto &ar = find_address_range(paddr, length);
+ if (ar.is_device()) {
throw std::invalid_argument{"attempted write to device memory range"};
}
- if (!pma.get_istart_M() || pma.get_istart_E()) {
- throw std::invalid_argument{"address range not entirely in single memory range"};
+ if (ar.get_length() == 0 || !ar.is_memory()) {
+ throw std::invalid_argument{"address range to write is not entirely in single memory range"};
}
- if (DID_is_protected(pma.get_istart_DID())) {
+ if (is_protected(ar.get_driver_id())) {
throw std::invalid_argument{"attempt to write to protected memory range"};
}
- pma.write_memory(paddr, data, length);
+ foreach_aligned_chunk(paddr, length, PMA_PAGE_SIZE, [&ar, paddr, data](auto chunk_start, auto chunk_length) {
+ const auto *src = data + (chunk_start - paddr);
+ const auto offset = chunk_start - ar.get_start();
+ auto *dest = ar.get_host_memory() + offset;
+ if (memcmp(dest, src, chunk_length) != 0) {
+ // Page is different, we have to copy memory
+ memcpy(dest, src, chunk_length);
+ ar.mark_dirty_page(offset);
+ }
+ });
}
-void machine::fill_memory(uint64_t address, uint8_t data, uint64_t length) {
+void machine::fill_memory(uint64_t paddr, uint8_t val, uint64_t length) {
if (length == 0) {
return;
}
- auto &pma = find_pma_entry(address, length);
- if (!pma.get_istart_M() || pma.get_istart_E()) {
- throw std::invalid_argument{"address range not entirely in memory PMA"};
+ auto &ar = find_address_range(paddr, length);
+ if (ar.is_device()) {
+ throw std::invalid_argument{"attempted fill to device memory range"};
+ }
+ if (ar.get_length() == 0 || !ar.is_memory()) {
+ throw std::invalid_argument{"address range to fill is not entirely in memory PMA"};
}
- pma.fill_memory(address, data, length);
+ if (is_protected(ar.get_driver_id())) {
+ throw std::invalid_argument{"attempt fill to protected memory range"};
+ }
+ // The case of filling a range with zeros is special and optimized for uarch reset
+ foreach_aligned_chunk(paddr, length, PMA_PAGE_SIZE, [&ar, val](auto chunk_start, auto chunk_length) {
+ const auto offset = chunk_start - ar.get_start();
+ const auto dest = ar.get_host_memory() + offset;
+ if (val != 0 || !is_pristine(dest, chunk_length)) {
+ memset(dest, val, chunk_length);
+ ar.mark_dirty_page(offset);
+ }
+ });
}
void machine::read_virtual_memory(uint64_t vaddr_start, unsigned char *data, uint64_t length) {
@@ -2273,21 +2338,22 @@ void machine::write_word(uint64_t paddr, uint64_t val) {
return;
}
// Otherwise, try the slow path
- auto &pma = find_pma_entry(paddr, sizeof(uint64_t));
- if (pma.get_istart_E() || !pma.get_istart_M()) {
+ auto &ar = find_address_range(paddr, sizeof(uint64_t));
+ if (ar.get_length() == 0 || !ar.is_memory() || ar.get_host_memory() == nullptr) {
std::ostringstream err;
- err << "attempted memory write to " << pma.get_description() << " at address 0x" << std::hex << paddr << "("
+ err << "attempted memory write to " << ar.get_description() << " at address 0x" << std::hex << paddr << "("
<< std::dec << paddr << ")";
throw std::runtime_error{err.str()};
}
- if (!pma.get_istart_W()) {
+ if (!ar.is_writeable()) {
std::ostringstream err;
- err << "attempted memory write to (non-writeable) " << pma.get_description() << " at address 0x" << std::hex
- << paddr << "(" << std::dec << paddr << ")";
+ err << "attempted memory write to read-only " << ar.get_description() << " at address 0x" << std::hex << paddr
+ << "(" << std::dec << paddr << ")";
throw std::runtime_error{err.str()};
}
- const auto offset = paddr - pma.get_start();
- aliased_aligned_write(pma.get_memory().get_host_memory() + offset, val);
+ const auto offset = paddr - ar.get_start();
+ aliased_aligned_write(ar.get_host_memory() + offset, val);
+ ar.mark_dirty_page(offset);
}
void machine::send_cmio_response(uint16_t reason, const unsigned char *data, uint64_t length) {
@@ -2339,12 +2405,13 @@ void machine::reset_uarch() {
write_reg(machine_reg_enum(reg::uarch_x0, i), UARCH_X_INIT);
}
// Load embedded pristine RAM image
- if (uarch_pristine_ram_len > m_us.ram.get_length()) {
- throw std::runtime_error("embedded uarch ram image does not fit in uarch ram pma");
- }
+ const auto uram_length = m_us.ram->get_length();
+ const auto uram_start = m_us.ram->get_start();
// Reset RAM to initial state
- m_us.ram.fill_memory(m_us.ram.get_start(), 0, m_us.ram.get_length());
- m_us.ram.write_memory(m_us.ram.get_start(), uarch_pristine_ram, uarch_pristine_ram_len);
+ write_memory(uram_start, uarch_pristine_ram, uarch_pristine_ram_len);
+ if (uram_length > uarch_pristine_ram_len) {
+ fill_memory(uram_start + uarch_pristine_ram_len, 0, uram_length - uarch_pristine_ram_len);
+ }
}
access_log machine::log_reset_uarch(const access_log::type &log_type) {
@@ -2384,8 +2451,8 @@ void machine::verify_reset_uarch(const hash_type &root_hash_before, const access
extern template UArchStepStatus uarch_step(uarch_record_state_access &a);
access_log machine::log_step_uarch(const access_log::type &log_type) {
- if (m_us.ram.get_istart_E()) {
- throw std::runtime_error("microarchitecture RAM is not present");
+ if (read_reg(reg::iunrep) != 0) {
+ throw std::runtime_error("microarchitecture cannot be used with unreproducible machines");
}
hash_type root_hash_before;
get_root_hash(root_hash_before);
@@ -2430,9 +2497,6 @@ uarch_interpreter_break_reason machine::run_uarch(uint64_t uarch_cycle_end) {
if (read_reg(reg::iunrep) != 0) {
throw std::runtime_error("microarchitecture cannot be used with unreproducible machines");
}
- if (m_us.ram.get_istart_E()) {
- throw std::runtime_error("microarchitecture RAM is not present");
- }
const uarch_state_access a(*this);
return uarch_interpret(a, uarch_cycle_end);
}
diff --git a/src/machine.h b/src/machine.h
index 03ab519d5..34cfe8164 100644
--- a/src/machine.h
+++ b/src/machine.h
@@ -26,26 +26,25 @@
#include
#include
-#include
#include
#include "access-log.h"
+#include "address-range-description.h"
+#include "address-range.h"
#include "host-addr.h"
#include "i-device-state-access.h"
#include "interpret.h"
#include "machine-config.h"
-#include "machine-memory-range-descr.h"
#include "machine-merkle-tree.h"
#include "machine-reg.h"
#include "machine-runtime-config.h"
#include "machine-state.h"
#include "os.h"
#include "pma-constants.h"
-#include "pma.h"
#include "shadow-tlb.h"
#include "uarch-interpret.h"
#include "uarch-state.h"
-#include "virtio-device.h"
+#include "virtio-address-range.h"
namespace cartesi {
@@ -61,55 +60,74 @@ constexpr skip_merkle_tree_update_t skip_merkle_tree_update;
/// \brief Cartesi Machine implementation
class machine final {
private:
- mutable machine_state m_s; ///< Big machine state
- mutable uarch_state m_us; ///< Microarchitecture state
- mutable machine_merkle_tree m_t; ///< Merkle tree of state
- std::vector m_merkle_pmas; ///< PMAs considered by the Merkle tree: from big machine and uarch
- machine_config m_c; ///< Copy of initialization config
- machine_runtime_config m_r; ///< Copy of initialization runtime config
- machine_memory_range_descrs m_mrds; ///< List of memory ranges returned by get_memory_ranges().
-
- boost::container::static_vector, VIRTIO_MAX> m_vdevs; ///< Array of VirtIO devices
-
- static const pma_entry::flags m_dtb_flags; ///< PMA flags used for DTB
- static const pma_entry::flags m_ram_flags; ///< PMA flags used for RAM
- static const pma_entry::flags m_flash_drive_flags; ///< PMA flags used for flash drives
- static const pma_entry::flags m_cmio_rx_buffer_flags; ///< PMA flags used for cmio rx buffer
- static const pma_entry::flags m_cmio_tx_buffer_flags; ///< PMA flags used for cmio tx buffer
-
- std::unordered_map m_counters;
-
- /// \brief Allocates a new PMA entry.
- /// \param pma PMA entry to add to machine.
- /// \returns Reference to corresponding entry in machine state.
- pma_entry ®ister_pma_entry(pma_entry &&pma);
-
- /// \brief Creates a new PMA entry reflecting a memory range configuration.
- /// \param description Informative description of PMA entry for use in error messages
- /// \param c Memory range configuration.
- /// \returns New PMA entry (with default flags).
- static pma_entry make_memory_range_pma_entry(const std::string &description, const memory_range_config &c);
-
- /// \brief Creates a new flash drive PMA entry.
- /// \param description Informative description of PMA entry for use in error messages
- /// \param c Memory range configuration.
- /// \returns New PMA entry with flash drive flags already set.
- static pma_entry make_flash_drive_pma_entry(const std::string &description, const memory_range_config &c);
-
- /// \brief Creates a new cmio rx buffer PMA entry.
- // \param c cmio buffer configuration for rx buffer.
- /// \returns New PMA entry with rx buffer flags already set.
- static pma_entry make_cmio_rx_buffer_pma_entry(const cmio_buffer_config &c);
-
- /// \brief Creates a new cmio tx buffer PMA entry.
- // \param c cmio buffer configuration for tx buffer.
- /// \returns New PMA entry with tx buffer flags already set.
- static pma_entry make_cmio_tx_buffer_pma_entry(const cmio_buffer_config &c);
-
- /// \brief Saves PMAs into files for serialization
+ mutable machine_state m_s; ///< Big machine state
+ mutable uarch_state m_us; ///< Microarchitecture state
+ mutable std::vector> m_ars; ///< All address ranges
+ mutable machine_merkle_tree m_t; ///< Merkle tree of state
+ machine_config m_c; ///< Copy of initialization config
+ machine_runtime_config m_r; ///< Copy of initialization runtime config
+ std::vector m_merkle_ars; ///< Indices of address ranges that the Mekrle tree can find
+ std::vector m_virtio_ars; ///< VirtIO address ranges
+ address_range_descriptions m_ards; ///< Address range descriptions listed by get_address_ranges()
+ std::unordered_map m_counters; ///< Counters used for statistics collection
+
+ ///< Where to register an address range
+ struct register_where {
+ bool merkle; //< Register with Merkle tree, so it appears in the root hash
+ bool interpret; //< Register so interpret can see (and it also appears as a PMA entries in memory)
+ };
+
+ /// \brief Checks if address range can be registered.
+ /// \param ar Address range object to register.
+ /// \param where Where to register the address range.
+ void check_address_range(const address_range &ar, register_where where);
+
+ /// \brief Registers a new address range.
+ /// \tparam AR An address range or derived type.
+ /// \param ar The address range object to register (as an r-value).
+ /// \param where Where to register the address range.
+ /// \returns Reference to address range object after it is moved inside the machine.
+ /// \details The r-value address range is moved to the heap, and the pointer holding it is added to a container.
+ /// Once the address range is moved to the heap, its address will remain valid until it is replaced by
+ /// a call to replace_memory_range(), or until the machine is destroyed.
+ /// This means pointers to address ranges remain valid even after subsequent calls to register_address_range(),
+ /// but may be invalidated by calls to replace_address_range().
+ /// For a stronger guarantee, when an address range is replaced, the pointer to the new address range
+ /// overwrites the pointer to the old address range at the same index in the container.
+ /// This means the an index into the container that owns all address ranges will always refers to same address range
+ /// after subsequent calls to register_address_range() and calls to replace_address_range() as well.
+ /// \details Besides the container that stores the address ranges, the machine maintains subsets of address ranges.
+ /// The "merkle" address range container lists the indices of the address ranges taht will be considered by
+ /// the Merkle tree during the computation of the state hash.
+ /// The "interpret" address range container lists the indices of the address ranges that will be visible from within
+ /// the interpreter.
+ /// When registering an address range with the machine, one must specify \p where else to register it.
+ /// The "virtio" address range container holds pointers to every virtio address range that has been registered.
+ template
+ AR ®ister_address_range(AR &&ar, register_where where)
+ requires std::is_rvalue_reference_v && std::derived_from
+ {
+ check_address_range(ar, where); // Check if we can register it
+ auto ptr = make_moved_unique(std::forward(ar)); // Move object to heap, now owned by ptr
+ AR &ar_ref = *ptr; // Get reference to object, already in heap, to return later
+ const auto index = m_ars.size(); // Get index new address range will occupy
+ m_ars.push_back(std::move(ptr)); // Move ptr to list of address ranges
+ if (where.interpret) { // Register with interpreter
+ m_s.pmas.push_back(index);
+ }
+ if (where.merkle) { // Register with Merkle tree
+ m_merkle_ars.push_back(index);
+ }
+ if constexpr (std::is_convertible_v) { // Register with VirtIO
+ m_virtio_ars.push_back(&ar_ref);
+ }
+ return ar_ref; // Return reference to object in heap
+ }
+
+ /// \brief Saves address ranges into files for serialization
/// \param config Machine config to be stored
- /// \param directory Directory where PMAs will be stored
- void store_pmas(const machine_config &config, const std::string &directory) const;
+ /// \param directory Directory where address ranges will be stored
+ void store_address_ranges(const machine_config &config, const std::string &directory) const;
/// \brief Returns offset that converts between machine host addresses and target physical addresses
/// \param pma_index Index of the memory PMA for the desired offset
@@ -124,62 +142,70 @@ class machine final {
/// \param r Machine runtime configuration
void init_processor(processor_config &p, const machine_runtime_config &r);
- /// \brief Initializes RAM PMA
+ /// \brief Initializes RAM address range
/// \param ram RAM configuration
- void init_ram_pma(const ram_config &ram);
+ void init_ram_ar(const ram_config &ram);
/// \brief Initializes flash drive PMAs
/// \param flash_drive Flash drive configurations
- void init_flash_drive_pmas(flash_drive_configs &flash_drive);
+ void init_flash_drive_ars(flash_drive_configs &flash_drive);
/// \brief Initializes VirtIO device PMAs
- /// \param v VirtIO configurations
+ /// \param cs VirtIO configurations
/// \param iunrep Initial value of iunrep CSR
- void init_virtio_pmas(const virtio_configs &v, uint64_t iunrep);
+ void init_virtio_ars(const virtio_configs &cs, uint64_t iunrep);
+
+ /// \brief Initializes HTIF device address range
+ /// \param h HTIF configuration
+ void init_htif_ar(const htif_config &h);
- /// \brief Initializes HTIF device PMA
+ /// \brief Initializes TTY if needed
/// \param h HTIF configuration
/// \param r HTIF runtime configuration
/// \param iunrep Initial value of iunrep CSR
- void init_htif_pma(const htif_config &h, const htif_runtime_config &r, uint64_t iunrep);
+ void init_tty(const htif_config &h, const htif_runtime_config &r, uint64_t iunrep) const;
- /// \brief Initializes CLINT device PMA
+ /// \brief Initializes CLINT device address range
/// \param c CLINT configuration
- void init_clint_pma(const clint_config &c);
+ void init_clint_ar(const clint_config &c);
- /// \brief Initializes PLIC device PMA
+ /// \brief Initializes PLIC device address range
/// \param p PLIC configuration
- void init_plic_pma(const plic_config &p);
+ void init_plic_ar(const plic_config &p);
- /// \brief Initializes CMIO PMAs
+ /// \brief Initializes CMIO address ranges
/// \param c CMIO configuration
- void init_cmio_pmas(const cmio_config &c);
+ void init_cmio_ars(const cmio_config &c);
- /// \brief Initializes the PMAs used to compute the Merkle tree
- /// \detail This can only be called after all PMAs have been added
- void init_merkle_pmas();
+ /// \brief Initializes the address ranges involced in the Merkle tree
+ /// \detail This can only be called after all address ranges have been registerd
+ void init_merkle_ars();
- /// \brief Initializes the PMAs descriptions returned by get_memory_ranges()
- /// \detail This can only be called after the Merkle tree PMAs have been initialized
- void init_memory_range_descrs();
+ /// \brief Initializes the address range descriptions returned by get_address_ranges()
+ /// \detail This can only be called after all address ranges have been registered
+ void init_ars_descriptions();
- /// \brief Fill up PMA list with sentinel empty PMAs
- void init_sentinel_pmas();
+ /// \brief Fill up address range list with sentinels
+ void init_sentinel_ars();
/// \brief Initializes contents of the shadow PMAs memory
/// \param shadow_pmas PMA entry for the shadow PMAs
/// \detail This can only be called after all PMAs have been added
- void init_shadow_pmas_contents(pma_entry &shadow_pmas) const;
+ void init_shadow_pmas_contents(memory_address_range &shadow_pmas) const;
/// \brief Initializes contents of machine TLB, from image in disk or with default values
/// \param image_filename File containing image, or empty for default values
/// \detail This can only be called after all PMAs have been added
void init_tlb_contents(const std::string &image_filename);
+ /// \brief Initializes DTB address range
+ /// \param c DTB configuration
+ address_range &init_dtb_ar(const dtb_config &c);
+
/// \brief Initializes contents of machine DTB, if image was not available
/// \param c Machine configuration
/// \param dtb PMA entry for the shadow PMAs
- static void init_dtb_contents(const machine_config &c, pma_entry &dtb);
+ static void init_dtb_contents(const machine_config &c, address_range &dtb);
/// \brief Dumps statistics
void dump_stats();
@@ -303,8 +329,8 @@ class machine final {
}
/// \brief Returns a list of descriptions for all PMA entries registered in the machine, sorted by start
- machine_memory_range_descrs get_memory_ranges() const {
- return m_mrds;
+ address_range_descriptions get_address_ranges() const {
+ return m_ards;
}
/// \brief Wait for external interrupts requests.
@@ -439,16 +465,18 @@ class machine final {
/// \brief Writes a chunk of data to machine memory, by its target physical address and length.
/// \param paddr Target physical address to start writing to.
/// \param data Buffer that contains data to write. Must be at least \p length bytes long.
- /// \param length Number of bytes to write starting from \p data to \p paddr.
+ /// \param length Number of bytes to write from \p data to \p paddr.
/// \details Unlike read_memory(), the entire chunk of data, from \p paddr to \p paddr + \p length,
- /// must reside entirely in the same memory range. Moreover, it cannot be mapped to a device.
+ /// must reside entirely in the same address range. Moreover, it cannot be mapped to a device.
void write_memory(uint64_t paddr, const unsigned char *data, uint64_t length);
/// \brief Fills a memory range with a single byte.
- /// \param address Physical address to start filling.
- /// \param data Byte to fill memory with.
- /// \param length Size of memory range to fill.
- void fill_memory(uint64_t address, uint8_t data, uint64_t length);
+ /// \param paddr Target physical address to start filling.
+ /// \param val Byte to fill memory with.
+ /// \param length Number of bytes to write starting at \p paddr.
+ /// \details Unlike read_memory(), the entire chunk of data, from \p paddr to \p paddr + \p length,
+ /// must reside entirely in the same address range. Moreover, it cannot be mapped to a device.
+ void fill_memory(uint64_t paddr, uint8_t val, uint64_t length);
/// \brief Reads a chunk of data from the machine virtual memory.
/// \param vaddr_start Virtual address to start reading.
@@ -467,33 +495,51 @@ class machine final {
/// \returns The corresponding physical address.
uint64_t translate_virtual_address(uint64_t vaddr);
- /// \brief Obtain PMA entry from the machine state that covers a given physical memory region
- /// \brief Microarchitecture PMAs are not considered.
- /// \param s Pointer to machine state.
- /// \param paddr Start of physical memory region.
- /// \param length Length of physical memory region.
- /// \returns Corresponding entry if found, or a sentinel entry
- /// for an empty range.
- const pma_entry &find_pma_entry(uint64_t paddr, uint64_t length) const;
+ /// \brief Returns the address range associated to the PMA at a given index
+ /// \param index Index of desired address range
+ /// \returns Desired address range, or an empty sentinel if index is out of bounds
+ const address_range &read_pma(uint64_t index) const noexcept {
+ if (index >= m_s.pmas.size()) {
+ static constexpr address_range sentinel{"sentinel"};
+ return sentinel;
+ }
+ // NOLINTNEXTLINE(bugprone-narrowing-conversions)
+ return *m_ars[static_cast(m_s.pmas[static_cast(index)])];
+ }
+
+ /// \brief Returns the address range associated to the PMA at a given index
+ /// \param index Index of desired address range
+ /// \returns Desired address range, or an empty sentinel if index is out of bounds
+ address_range &read_pma(uint64_t index) noexcept {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
+ return const_cast(std::as_const(*this).read_pma(index));
+ }
+
+ /// \brief Obtain address range from the machine state that covers a given physical memory region
+ /// \param paddr Target physical address of start of region.
+ /// \param length Length of region, in bytes.
+ /// \returns Corresponding address range if found, or an empty sentinel otherwise.
+ /// \warning Microarchitecture address ranges are not considered in the search.
+ const address_range &find_address_range(uint64_t paddr, uint64_t length) const noexcept;
- pma_entry &find_pma_entry(uint64_t paddr, uint64_t length) {
+ address_range &find_address_range(uint64_t paddr, uint64_t length) noexcept {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
- return const_cast(std::as_const(*this).find_pma_entry(paddr, length));
+ return const_cast(std::as_const(*this).find_address_range(paddr, length));
}
- /// \brief Obtain PMA entry covering a physical memory word
+ /// \brief Obtain address range from the machine state that covers a given word in physical memory
/// \tparam T Type of word.
- /// \param s Pointer to machine state.
- /// \param paddr Target physical address.
- /// \returns Corresponding entry if found, or a sentinel entry for an empty range.
+ /// \param paddr Target physical address of word.
+ /// \returns Corresponding address range if found, or an empty sentinel otherwise.
+ /// \warning Microarchitecture address ranges are not considered in the search.
template
- const pma_entry &find_pma_entry(uint64_t paddr) const {
- return find_pma_entry(paddr, sizeof(T));
+ const address_range &find_address_range(uint64_t paddr) const {
+ return find_address_range(paddr, sizeof(T));
}
template
- pma_entry &find_pma_entry(uint64_t paddr) {
- return find_pma_entry(paddr, sizeof(T));
+ address_range &find_address_range(uint64_t paddr) {
+ return find_address_range(paddr, sizeof(T));
}
/// \brief Go over the write TLB and mark as dirty all pages currently there.
@@ -519,10 +565,9 @@ class machine final {
void set_runtime_config(machine_runtime_config r);
/// \brief Replaces a memory range.
- /// \param range Configuration of the new memory range.
- /// \details The machine must contain an existing memory range
- /// matching the start and length specified in range.
- void replace_memory_range(const memory_range_config &range);
+ /// \param config Configuration of the new memory range.
+ /// \details The machine must contain an existing memory range matching the start and length specified in range.
+ void replace_memory_range(const memory_range_config &config);
/// \brief Sends cmio response
/// \param reason Reason for sending response.
diff --git a/src/memory-address-range.cpp b/src/memory-address-range.cpp
new file mode 100644
index 000000000..a8d05ec63
--- /dev/null
+++ b/src/memory-address-range.cpp
@@ -0,0 +1,90 @@
+// 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 "memory-address-range.h"
+
+namespace cartesi {
+
+using namespace std::string_literals;
+
+class base_error : public std::invalid_argument {
+public:
+ explicit base_error(const char *err) : std::invalid_argument{err} {
+ ;
+ }
+};
+
+memory_address_range::memory_address_range(const std::string &description, uint64_t start, uint64_t length,
+ const pma_flags &flags, const std::string &image_filename, const mmapd &m) try :
+ address_range(description.c_str(), start, length, flags, [](const char *err) { throw base_error{err}; }),
+ m_ptr{make_unique_mmap(image_filename.c_str(), length, m.shared)},
+ m_host_memory{std::get(m_ptr).get()} {
+ if (!is_memory()) {
+ throw std::invalid_argument{"memory range must be flagged memory when initializing "s + description};
+ }
+ m_dirty_page_map.resize((get_length() / (8 * PMA_PAGE_SIZE)) + 1, 0xff);
+} catch (base_error &b) {
+ throw; // already contains the description
+} catch (std::exception &e) {
+ throw std::invalid_argument{e.what() + " when initializing "s + description};
+} catch (...) {
+ throw std::invalid_argument{"unknown exception when initializing "s + description};
+}
+
+memory_address_range::memory_address_range(const std::string &description, uint64_t start, uint64_t length,
+ const pma_flags &flags, const std::string &image_filename, const callocd & /*c*/) try :
+ address_range(description.c_str(), start, length, flags, [](const char *err) { throw base_error{err}; }),
+ m_ptr{make_unique_calloc(length)},
+ m_host_memory{std::get(m_ptr).get()} {
+ if (!is_memory()) {
+ throw std::invalid_argument{"memory range must be flagged memory when initializing "s + description};
+ }
+ m_dirty_page_map.resize((length / (8 * PMA_PAGE_SIZE)) + 1, 0xff);
+ // Try to load image file, if any
+ if (!image_filename.empty()) {
+ auto fp = make_unique_fopen(image_filename.c_str(), "rb", std::nothrow_t{});
+ if (!fp) {
+ throw std::system_error{errno, std::generic_category(), "error opening image file '"s + image_filename};
+ }
+ // Get file size
+ if (fseek(fp.get(), 0, SEEK_END) != 0) {
+ throw std::system_error{errno, std::generic_category(),
+ "error obtaining length of image file '"s + image_filename};
+ }
+ const auto file_length = static_cast(ftello(fp.get()));
+ if (fseek(fp.get(), 0, SEEK_SET) != 0) {
+ throw std::system_error{errno, std::generic_category(),
+ "error obtaining length of image file '"s + image_filename};
+ }
+ // Check against PMA range size
+ if (file_length > length) {
+ throw std::runtime_error{"image file '"s + image_filename + "' is too large for range"s};
+ }
+ // Read to host memory
+ const auto read_length = static_cast(fread(m_host_memory, 1, file_length, fp.get()));
+ if (read_length != file_length) {
+ throw std::runtime_error{"error reading from image file '"s + image_filename};
+ }
+ }
+} catch (base_error &b) {
+ throw; // already contains the description
+} catch (std::exception &e) {
+ throw std::invalid_argument{e.what() + " when initializing "s + description};
+} catch (...) {
+ throw std::invalid_argument{"unknown exception when initializing "s + description};
+}
+
+} // namespace cartesi
diff --git a/src/memory-address-range.h b/src/memory-address-range.h
new file mode 100644
index 000000000..7beb6a56f
--- /dev/null
+++ b/src/memory-address-range.h
@@ -0,0 +1,143 @@
+// 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 MEMORY_ADDRESS_RANGE_H
+#define MEMORY_ADDRESS_RANGE_H
+
+#include
+#include
+
+#include "address-range.h"
+#include "unique-c-ptr.h"
+
+namespace cartesi {
+
+// Forward declarations
+class machine;
+
+/// \file
+/// \brief An address range occupied by memory
+
+class memory_address_range : public address_range {
+
+ using callocd_ptr = unique_calloc_ptr;
+ using mmapd_ptr = unique_mmap_ptr;
+
+ std::variant
+ m_ptr;
+
+ unsigned char *m_host_memory; ///< Start of associated memory region in host.
+ std::vector m_dirty_page_map; ///< Map of dirty pages.
+
+public:
+ using ptr_type = std::unique_ptr;
+
+ /// \brief Mmap'd range data (shared or not).
+ struct mmapd {
+ bool shared;
+ };
+
+ /// \brief Constructor for mmap'd ranges.
+ /// \param description Description of address range for use in error messages
+ /// \param start Start of address range
+ /// \param length Length of address range
+ /// \param flags Range flags
+ /// \param image_filename Path to backing file.
+ /// \param m Mmap'd range data (shared or not).
+ memory_address_range(const std::string &description, uint64_t start, uint64_t length, const pma_flags &flags,
+ const std::string &image_filename, const mmapd &m);
+
+ /// \brief Calloc'd range data (just a tag).
+ struct callocd {};
+
+ /// \brief Constructor for calloc'd ranges.
+ /// \param description Description of address range for use in error messages
+ /// \param start Start of address range
+ /// \param length Length of address range
+ /// \param flags Range flags
+ /// \param image_filename Path to backing file.
+ /// \param c Calloc'd range data (just a tag).
+ memory_address_range(const std::string &description, uint64_t start, uint64_t length, const pma_flags &flags,
+ const std::string &image_filename, const callocd & /*c*/);
+
+ memory_address_range(const memory_address_range &) = delete;
+ memory_address_range &operator=(const memory_address_range &) = delete;
+ memory_address_range &operator=(memory_address_range &&) noexcept = delete;
+
+ ~memory_address_range() override = default;
+ memory_address_range(memory_address_range &&) noexcept = default;
+
+private:
+ unsigned char *do_get_host_memory() noexcept override {
+ return m_host_memory;
+ }
+
+ const unsigned char *do_get_host_memory() const noexcept override {
+ return m_host_memory;
+ }
+
+ void do_mark_dirty_page(uint64_t offset) noexcept override {
+ auto page_index = offset >> PMA_constants::PMA_PAGE_SIZE_LOG2;
+ auto map_index = page_index >> 3;
+ assert(map_index < m_dirty_page_map.size());
+ m_dirty_page_map[map_index] |= (1 << (page_index & 7));
+ }
+
+ void do_mark_clean_page(uint64_t offset) noexcept override {
+ auto page_index = offset >> PMA_constants::PMA_PAGE_SIZE_LOG2;
+ auto map_index = page_index >> 3;
+ assert(map_index < m_dirty_page_map.size());
+ m_dirty_page_map[map_index] &= ~(1 << (page_index & 7));
+ }
+
+ void do_mark_pages_clean() noexcept override {
+ std::fill(m_dirty_page_map.begin(), m_dirty_page_map.end(), 0);
+ }
+
+ bool do_is_page_marked_dirty(uint64_t offset) const noexcept override {
+ auto page_index = offset >> PMA_constants::PMA_PAGE_SIZE_LOG2;
+ auto map_index = page_index >> 3;
+ assert(map_index < m_dirty_page_map.size());
+ return (m_dirty_page_map[map_index] & (1 << (page_index & 7))) != 0;
+ }
+
+ bool do_peek(const machine & /*m*/, uint64_t offset, uint64_t length, const unsigned char **data,
+ unsigned char * /*scratch*/) const noexcept override {
+ if (contains_relative(offset, length)) {
+ *data = get_host_memory() + offset;
+ return true;
+ }
+ *data = nullptr;
+ return false;
+ }
+};
+
+static inline auto make_callocd_memory_address_range(const std::string &description, uint64_t start, uint64_t length,
+ pma_flags flags, const std::string &image_filename = {}) {
+ return memory_address_range{description, start, length, flags, image_filename, memory_address_range::callocd{}};
+}
+
+static inline auto make_mmapd_memory_address_range(const std::string &description, uint64_t start, uint64_t length,
+ pma_flags flags, const std::string &image_filename, bool shared) {
+ return memory_address_range{description, start, length, flags, image_filename, memory_address_range::mmapd{shared}};
+}
+
+} // namespace cartesi
+
+#endif
diff --git a/src/meta.h b/src/meta.h
index 1a1f28a10..1bf94a03c 100644
--- a/src/meta.h
+++ b/src/meta.h
@@ -85,6 +85,12 @@ struct log2_size {
static constexpr int value = 3;
};
+// helper type for visitor
+template
+struct overloads : Ts... {
+ using Ts::operator()...;
+};
+
/// \endcond
} // namespace cartesi
diff --git a/src/mock-address-range.h b/src/mock-address-range.h
new file mode 100644
index 000000000..30b3cc5a9
--- /dev/null
+++ b/src/mock-address-range.h
@@ -0,0 +1,108 @@
+// 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 MOCK_ADDRESS_RANGE_H
+#define MOCK_ADDRESS_RANGE_H
+
+#include
+#include
+#include
+
+#include "address-range.h"
+#include "clint-address-range.h"
+#include "htif-address-range.h"
+#include "plic-address-range.h"
+#include "pma.h"
+#include "shadow-state-address-range.h"
+#include "shadow-tlb-address-range.h"
+
+namespace cartesi {
+
+using mock_address_range = std::variant;
+
+using mock_address_ranges = std::array;
+
+template
+static inline mock_address_range check_mock_flags(AR &&ar, const pma_flags &flags, ABRT abrt)
+ requires std::is_rvalue_reference_v && std::derived_from
+{
+ if (ar.get_flags() != flags) {
+ abrt("incompatible flags in mock address range");
+ __builtin_trap();
+ return std::monostate{};
+ }
+ return std::forward(ar);
+}
+
+template
+static inline mock_address_range make_mock_address_range(uint64_t istart, uint64_t ilength, ABRT abrt) {
+ uint64_t start{};
+ auto flags = unpack_pma_istart(istart, start);
+ if (flags.M) {
+ return make_address_range(pma_get_DID_name(flags.DID), start, ilength, flags, abrt);
+ }
+ if (flags.E) {
+ return make_address_range("empty", start, ilength, flags, abrt);
+ }
+ switch (flags.DID) {
+ case PMA_ISTART_DID::shadow_state:
+ return check_mock_flags(make_shadow_state_address_range(start, ilength, abrt), flags, abrt);
+ case PMA_ISTART_DID::shadow_TLB:
+ return check_mock_flags(make_shadow_tlb_address_range(start, ilength, abrt), flags, abrt);
+ case PMA_ISTART_DID::CLINT:
+ return check_mock_flags(make_clint_address_range(start, ilength, abrt), flags, abrt);
+ case PMA_ISTART_DID::PLIC:
+ return check_mock_flags(make_plic_address_range(start, ilength, abrt), flags, abrt);
+ case PMA_ISTART_DID::HTIF:
+ return check_mock_flags(make_htif_address_range(start, ilength, abrt), flags, abrt);
+ default:
+ abrt("unhandled mock address range");
+ __builtin_trap();
+ return std::monostate{};
+ }
+};
+
+template
+address_range &get_mock_address_range(mock_address_range &mock, ABRT abrt) {
+ //??D I'm hoping the compiler optimizes this to what amounts to an if and a cast
+ static_assert(std::is_same_v, std::monostate>);
+ switch (mock.index()) {
+ case 1:
+ return std::get<1>(mock);
+ case 2:
+ return std::get<2>(mock);
+ case 3:
+ return std::get<3>(mock);
+ case 4:
+ return std::get<4>(mock);
+ case 5:
+ return std::get<5>(mock);
+ case 6:
+ return std::get<6>(mock);
+ default: {
+ static auto unhandled = make_empty_address_range("unhandled mock address range");
+ abrt("unhandled mock address range");
+ __builtin_trap();
+ return unhandled;
+ }
+ }
+ static_assert(std::variant_size_v == 7);
+}
+
+} // namespace cartesi
+
+#endif
diff --git a/src/mock-pma-entry.h b/src/mock-pma-entry.h
deleted file mode 100644
index 75cda69cb..000000000
--- a/src/mock-pma-entry.h
+++ /dev/null
@@ -1,169 +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 MOCK_PMA_ENTRY_H
-#define MOCK_PMA_ENTRY_H
-
-#include "clint.h"
-#include "htif.h"
-#include "plic.h"
-#include "pma-constants.h"
-#include "shadow-state.h"
-#include "shadow-tlb.h"
-
-namespace cartesi {
-
-class mock_pma_entry {
-public:
- struct flags {
- bool M;
- bool IO;
- bool E;
- bool R;
- bool W;
- bool X;
- bool IR;
- bool IW;
- PMA_ISTART_DID DID;
- };
-
-private:
- uint64_t m_pma_index;
- uint64_t m_start;
- uint64_t m_length;
- flags m_flags;
- const pma_driver *m_driver{nullptr};
-
- static constexpr flags split_flags(uint64_t istart) {
- flags f{};
- f.M = ((istart & PMA_ISTART_M_MASK) >> PMA_ISTART_M_SHIFT) != 0;
- f.IO = ((istart & PMA_ISTART_IO_MASK) >> PMA_ISTART_IO_SHIFT) != 0;
- f.E = ((istart & PMA_ISTART_E_MASK) >> PMA_ISTART_E_SHIFT) != 0;
- f.R = ((istart & PMA_ISTART_R_MASK) >> PMA_ISTART_R_SHIFT) != 0;
- f.W = ((istart & PMA_ISTART_W_MASK) >> PMA_ISTART_W_SHIFT) != 0;
- f.X = ((istart & PMA_ISTART_X_MASK) >> PMA_ISTART_X_SHIFT) != 0;
- f.IR = ((istart & PMA_ISTART_IR_MASK) >> PMA_ISTART_IR_SHIFT) != 0;
- f.IW = ((istart & PMA_ISTART_IW_MASK) >> PMA_ISTART_IW_SHIFT) != 0;
- f.DID = static_cast((istart & PMA_ISTART_DID_MASK) >> PMA_ISTART_DID_SHIFT);
- return f;
- }
-
-public:
- template
- mock_pma_entry(uint64_t pma_index, uint64_t istart, uint64_t ilength, ERR_F errf) :
- m_pma_index{pma_index},
- m_start{istart & PMA_ISTART_START_MASK},
- m_length{ilength},
- m_flags{split_flags(istart)} {
- if (m_flags.IO) {
- switch (m_flags.DID) {
- case PMA_ISTART_DID::shadow_state:
- m_driver = &shadow_state_driver;
- break;
- case PMA_ISTART_DID::shadow_TLB:
- m_driver = &shadow_tlb_driver;
- break;
- case PMA_ISTART_DID::CLINT:
- m_driver = &clint_driver;
- break;
- case PMA_ISTART_DID::PLIC:
- m_driver = &plic_driver;
- break;
- case PMA_ISTART_DID::HTIF:
- m_driver = &htif_driver;
- break;
- default:
- errf("unsupported device in mock_pma_entry");
- break;
- }
- }
- }
-
- uint64_t get_index() const {
- return m_pma_index;
- }
-
- flags get_flags() const {
- return m_flags;
- }
-
- uint64_t get_start() const {
- return m_start;
- }
-
- uint64_t get_length() const {
- return m_length;
- }
-
- bool get_istart_M() const {
- return m_flags.M;
- }
-
- bool get_istart_IO() const {
- return m_flags.IO;
- }
-
- bool get_istart_E() const {
- return m_flags.E;
- }
-
- bool get_istart_R() const {
- return m_flags.R;
- }
-
- bool get_istart_W() const {
- return m_flags.W;
- }
-
- bool get_istart_X() const {
- return m_flags.X;
- }
-
- bool get_istart_IR() const {
- return m_flags.IR;
- }
-
- PMA_ISTART_DID get_istart_DID() const {
- return m_flags.DID;
- }
-
- const auto *get_driver() const {
- return m_driver;
- }
-
- const auto &get_device_noexcept() const {
- return *this;
- }
-
- static void *get_context() {
- return nullptr;
- }
-
- // NOLINTNEXTLINE(readability-convert-member-functions-to-static)
- void mark_dirty_page(uint64_t address_in_range) {
- (void) address_in_range;
- // Dummy implementation.
- }
-};
-
-template
-static inline mock_pma_entry make_mock_pma_entry(uint64_t index, uint64_t istart, uint64_t ilength, ERR_F errf) {
- return mock_pma_entry{index, istart, ilength, errf};
-}
-
-} // namespace cartesi
-
-#endif
diff --git a/src/plic.cpp b/src/plic-address-range.cpp
similarity index 91%
rename from src/plic.cpp
rename to src/plic-address-range.cpp
index 3eac5b174..cd8148561 100644
--- a/src/plic.cpp
+++ b/src/plic-address-range.cpp
@@ -14,17 +14,14 @@
// with this program (see COPYING). If not, see .
//
-#include "plic.h"
+#include "plic-address-range.h"
-#include
+#include "assert-printf.h"
#include
-#include
#include "i-device-state-access.h"
#include "interpret.h"
#include "pma-constants.h"
-#include "pma-defines.h"
-#include "pma-driver.h"
#include "riscv-constants.h"
// Enable these defines to debug PLIC
@@ -52,7 +49,7 @@ static uint32_t plic_read_pending(i_device_state_access *a) {
}
/// \brief Called only by the driver when it begins serving a pending interrupt request.
-static bool plic_read_claim_complete(i_device_state_access *a, uint64_t *val) {
+static bool plic_read_claim_complete(i_device_state_access *a, uint64_t *pval) {
const uint32_t girqpend = a->read_plic_girqpend();
uint32_t girqsrvd = a->read_plic_girqsrvd();
uint32_t ipmask = girqpend & ~girqsrvd;
@@ -68,7 +65,7 @@ static bool plic_read_claim_complete(i_device_state_access *a, uint64_t *val) {
girqsrvd |= irq_mask;
a->write_plic_girqsrvd(girqsrvd);
// The PLIC will then return the interrupt source id to the target
- *val = irq_id;
+ *pval = irq_id;
// If all pending interrupts have been served, reset mip.
ipmask = girqpend & ~girqsrvd;
if (ipmask == 0) {
@@ -76,10 +73,10 @@ static bool plic_read_claim_complete(i_device_state_access *a, uint64_t *val) {
}
} else {
// The PLIC will return an id of zero, if there were no pending interrupts for the target
- *val = 0;
+ *pval = 0;
}
#ifdef DEBUG_PLIC
- std::ignore = fprintf(stderr, "plic: claim irq_id=%d\n", (int) *val);
+ std::ignore = fprintf(stderr, "plic: claim irq_id=%d\n", (int) *pval);
#endif
return true;
}
@@ -108,8 +105,8 @@ static execute_status plic_write_claim_complete(i_device_state_access *a, uint32
return execute_status::success;
}
-/// \brief PLIC device read callback. See ::pma_read.
-static bool plic_read(void * /*context*/, i_device_state_access *a, uint64_t offset, uint64_t *val, int log2_size) {
+bool plic_address_range::do_read_device(i_device_state_access *a, uint64_t offset, int log2_size,
+ uint64_t *pval) const noexcept {
#ifdef DEBUG_PLIC_MMIO
std::ignore = fprintf(stderr, "plic: mmio read offset=0x%lx log2_size=%d\n", (long) offset, log2_size);
#endif
@@ -153,28 +150,28 @@ static bool plic_read(void * /*context*/, i_device_state_access *a, uint64_t off
case plic_csr_rel_addr::priority31:
// A valid implementation can hardwire all input priority levels.
// We hardwire all supported interrupt sources to the lowest priority
- *val = PLIC_LOWEST_IRQ_PRIORITY;
+ *pval = PLIC_LOWEST_IRQ_PRIORITY;
return true;
case plic_csr_rel_addr::pending:
- *val = plic_read_pending(a);
+ *pval = plic_read_pending(a);
return true;
case plic_csr_rel_addr::enabled:
// A valid implementation can hardwire interrupt routing to a fixed hart context.
// We hardwire all supported interrupt source to be always enabled in context 0.
- *val = PLIC_ENABLED_IRQ_MASK;
+ *pval = PLIC_ENABLED_IRQ_MASK;
return true;
case plic_csr_rel_addr::claim_complete:
- return plic_read_claim_complete(a, val);
+ return plic_read_claim_complete(a, pval);
default:
// Other PLIC CSRs are WARL hardwired to 0
- *val = 0;
+ *pval = 0;
return true;
}
}
/// \brief PLIC device read callback. See ::pma_write.
-static execute_status plic_write(void * /*context*/, i_device_state_access *a, uint64_t offset, uint64_t val,
- int log2_size) {
+execute_status plic_address_range::do_write_device(i_device_state_access *a, uint64_t offset, int log2_size,
+ uint64_t val) noexcept {
#ifdef DEBUG_PLIC_MMIO
std::ignore =
fprintf(stderr, "plic: mmio write offset=0x%lx log2_size=%d val=0x%x\n", (long) offset, log2_size, (int) val);
@@ -220,6 +217,4 @@ void plic_reset_pending_irq(i_device_state_access *a, uint32_t irq_id) {
}
}
-const pma_driver plic_driver = {.name = "PLIC", .read = plic_read, .write = plic_write};
-
} // namespace cartesi
diff --git a/src/plic-address-range.h b/src/plic-address-range.h
new file mode 100644
index 000000000..e430a1974
--- /dev/null
+++ b/src/plic-address-range.h
@@ -0,0 +1,80 @@
+// 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 PLIC_ADDRESS_RANGE_H
+#define PLIC_ADDRESS_RANGE_H
+
+#include
+
+#include "i-device-state-access.h"
+#include "plic-constants.h"
+#include "pristine-address-range.h"
+
+/// \file
+/// \brief Platform-Level Interrupt Controller address range.
+
+namespace cartesi {
+
+/// \brief Sets a new pending interrupt request.
+/// \details This is called only by devices to notify an external interrupt.
+void plic_set_pending_irq(i_device_state_access *a, uint32_t irq_id);
+
+/// \brief Clears a pending interrupt request.
+/// \details This is called only by devices to remove an external interrupt notification.
+void plic_reset_pending_irq(i_device_state_access *a, uint32_t irq_id);
+
+class plic_address_range final : public pristine_address_range {
+
+ static constexpr pma_flags m_plic_flags{
+ .M = false,
+ .IO = true,
+ .E = false,
+ .R = true,
+ .W = true,
+ .X = false,
+ .IR = false,
+ .IW = false,
+ .DID = PMA_ISTART_DID::PLIC,
+ };
+
+public:
+ template
+ plic_address_range(uint64_t start, uint64_t length, ABRT abrt) :
+ pristine_address_range("PLIC device", start, length, m_plic_flags, abrt) {
+ ;
+ }
+
+ plic_address_range(const plic_address_range &other) = default;
+ plic_address_range &operator=(const plic_address_range &other) = default;
+ plic_address_range(plic_address_range &&other) = default;
+ plic_address_range &operator=(plic_address_range &&other) = default;
+ ~plic_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 plic_address_range make_plic_address_range(uint64_t start, uint64_t length, ABRT abrt) {
+ return plic_address_range{start, length, abrt};
+}
+
+} // namespace cartesi
+
+#endif
diff --git a/src/plic.h b/src/plic-constants.h
similarity index 86%
rename from src/plic.h
rename to src/plic-constants.h
index a719e1402..d6eda8311 100644
--- a/src/plic.h
+++ b/src/plic-constants.h
@@ -14,15 +14,13 @@
// with this program (see COPYING). If not, see .
//
-#ifndef PLIC_H
-#define PLIC_H
+#ifndef PLIC_CONSTANTS_H
+#define PLIC_CONSTANTS_H
#include
-#include "pma-driver.h"
-
/// \file
-/// \brief Clock interruptor device.
+/// \brief Platform-Level Interrupt Controller constants.
namespace cartesi {
@@ -74,17 +72,6 @@ enum plic_csr_rel_addr : uint64_t {
// .. Interrupt threshold and claim_complete for other sources and contexts (unsupported)
};
-/// \brief Sets a new pending interrupt request.
-/// \details This is called only by devices to notify an external interrupt.
-void plic_set_pending_irq(i_device_state_access *a, uint32_t irq_id);
-
-/// \brief Clears a pending interrupt request.
-/// \details This is called only by devices to remove an external interrupt notification.
-void plic_reset_pending_irq(i_device_state_access *a, uint32_t irq_id);
-
-/// \brief Global PLIC device driver instance
-extern const pma_driver plic_driver;
-
} // namespace cartesi
#endif
diff --git a/src/plic-factory.cpp b/src/plic-factory.cpp
deleted file mode 100644
index 01bb24686..000000000
--- a/src/plic-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 "plic-factory.h"
-
-#include
-
-#include "plic.h"
-#include "pma-constants.h"
-#include "pma.h"
-
-namespace cartesi {
-
-pma_entry make_plic_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::PLIC};
- return make_device_pma_entry("PLIC device", start, length, pma_peek_pristine, &plic_driver).set_flags(f);
-}
-
-} // namespace cartesi
diff --git a/src/plic-factory.h b/src/plic-factory.h
deleted file mode 100644
index 5ac9e26d6..000000000
--- a/src/plic-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 PLIC_FACTORY_H
-#define PLIC_FACTORY_H
-
-#include
-
-#include "pma.h"
-
-namespace cartesi {
-
-/// \brief Creates a PMA entry for the PLIC device
-/// \param start Start address for memory range.
-/// \param length Length of memory range.
-/// \returns Corresponding PMA entry
-pma_entry make_plic_pma_entry(uint64_t start, uint64_t length);
-
-} // namespace cartesi
-
-#endif
diff --git a/src/pma-constants.h b/src/pma-constants.h
index 38d9df885..723437296 100644
--- a/src/pma-constants.h
+++ b/src/pma-constants.h
@@ -19,14 +19,10 @@
#include
-#include "machine-c-api.h"
#include "pma-defines.h"
namespace cartesi {
-/// \file
-/// \brief Physical memory attributes constants.
-
/// \brief Fixed PMA ranges.
enum PMA_ranges : uint64_t {
PMA_SHADOW_STATE_START = EXPAND_UINT64_C(PMA_SHADOW_STATE_START_DEF), ///< Start of processor shadow range
@@ -93,7 +89,7 @@ enum PMA_ISTART_shifts {
PMA_ISTART_IR_SHIFT = 6,
PMA_ISTART_IW_SHIFT = 7,
PMA_ISTART_DID_SHIFT = 8,
- PMA_ISTART_START_SHIFT = 12,
+ PMA_ISTART_START_SHIFT = PMA_PAGE_SIZE_LOG2_DEF
};
/// \brief PMA istart masks
@@ -112,57 +108,20 @@ enum PMA_ISTART_masks : uint64_t {
/// \brief PMA device ids
enum class PMA_ISTART_DID {
- memory = PMA_MEMORY_DID_DEF, ///< DID for memory
- shadow_state = PMA_SHADOW_STATE_DID_DEF, ///< DID for shadow device
- shadow_pmas = PMA_SHADOW_PMAS_DID_DEF, ///< DID for shadow pma array device
- shadow_TLB = PMA_SHADOW_TLB_DID_DEF, ///< DID for shadow TLB device
- flash_drive = PMA_FLASH_DRIVE_DID_DEF, ///< DID for drive device
- CLINT = PMA_CLINT_DID_DEF, ///< DID for CLINT device
- PLIC = PMA_PLIC_DID_DEF, ///< DID for PLIC device
- HTIF = PMA_HTIF_DID_DEF, ///< DID for HTIF device
- VIRTIO = PMA_VIRTIO_DID_DEF, ///< DID for VirtIO devices
- cmio_rx_buffer = PMA_CMIO_RX_BUFFER_DID_DEF, ///< DID for cmio receive buffer
- cmio_tx_buffer = PMA_CMIO_TX_BUFFER_DID_DEF, ///< DID for cmio transmit buffer
- shadow_uarch = PMA_SHADOW_UARCH_STATE_DID_DEF, ///< DID for shadow uarch state device
+ memory = PMA_MEMORY_DID_DEF, ///< DID for memory
+ shadow_state = PMA_SHADOW_STATE_DID_DEF, ///< DID for shadow device
+ shadow_pmas = PMA_SHADOW_PMAS_DID_DEF, ///< DID for shadow pma array device
+ shadow_TLB = PMA_SHADOW_TLB_DID_DEF, ///< DID for shadow TLB device
+ flash_drive = PMA_FLASH_DRIVE_DID_DEF, ///< DID for drive device
+ CLINT = PMA_CLINT_DID_DEF, ///< DID for CLINT device
+ PLIC = PMA_PLIC_DID_DEF, ///< DID for PLIC device
+ HTIF = PMA_HTIF_DID_DEF, ///< DID for HTIF device
+ VIRTIO = PMA_VIRTIO_DID_DEF, ///< DID for VirtIO devices
+ cmio_rx_buffer = PMA_CMIO_RX_BUFFER_DID_DEF, ///< DID for cmio receive buffer
+ cmio_tx_buffer = PMA_CMIO_TX_BUFFER_DID_DEF, ///< DID for cmio transmit buffer
+ shadow_uarch_state = PMA_SHADOW_UARCH_STATE_DID_DEF, ///< DID for shadow uarch state device
};
-static constexpr const char *pma_get_DID_name(PMA_ISTART_DID did) {
- switch (did) {
- case PMA_ISTART_DID::memory:
- return "DID.memory";
- case PMA_ISTART_DID::shadow_state:
- return "DID.shadow_state";
- case PMA_ISTART_DID::shadow_pmas:
- return "DID.shadow_pmas";
- case PMA_ISTART_DID::shadow_TLB:
- return "DID.shadow_TLB";
- case PMA_ISTART_DID::flash_drive:
- return "DID.flash_drive";
- case PMA_ISTART_DID::CLINT:
- return "DID.CLINT";
- case PMA_ISTART_DID::PLIC:
- return "DID.PLIC";
- case PMA_ISTART_DID::HTIF:
- return "DID.HTIF";
- case PMA_ISTART_DID::VIRTIO:
- return "DID.VIRTIO";
- case PMA_ISTART_DID::cmio_rx_buffer:
- return "DID.cmio_rx_buffer";
- case PMA_ISTART_DID::cmio_tx_buffer:
- return "DID.cmio_tx_buffer";
- case PMA_ISTART_DID::shadow_uarch:
- return "DID.shadow_uarch";
- default:
- return "DID.unknown";
- }
-}
-
-static_assert(PMA_CMIO_RX_BUFFER_START_DEF == CM_PMA_CMIO_RX_BUFFER_START);
-static_assert(PMA_CMIO_RX_BUFFER_LOG2_SIZE_DEF == CM_PMA_CMIO_RX_BUFFER_LOG2_SIZE);
-static_assert(PMA_CMIO_TX_BUFFER_START_DEF == CM_PMA_CMIO_TX_BUFFER_START);
-static_assert(PMA_CMIO_TX_BUFFER_LOG2_SIZE_DEF == CM_PMA_CMIO_TX_BUFFER_LOG2_SIZE);
-static_assert(PMA_RAM_START_DEF == CM_PMA_RAM_START);
-
} // namespace cartesi
#endif
diff --git a/src/pma-driver.cpp b/src/pma-driver.cpp
deleted file mode 100644
index cc5b38e9e..000000000
--- a/src/pma-driver.cpp
+++ /dev/null
@@ -1,35 +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 "pma-driver.h"
-
-#include
-
-#include "interpret.h"
-
-namespace cartesi {
-
-bool device_read_error(void * /*context*/, i_device_state_access * /*a*/, uint64_t /*offset*/, uint64_t * /*val*/,
- int /*log2_size*/) {
- return false;
-}
-
-execute_status device_write_error(void * /*context*/, i_device_state_access * /*a*/, uint64_t /*offset*/,
- uint64_t /*val*/, int /*log2_size*/) {
- return execute_status::failure;
-}
-
-} // namespace cartesi
diff --git a/src/pma-driver.h b/src/pma-driver.h
deleted file mode 100644
index c3534610b..000000000
--- a/src/pma-driver.h
+++ /dev/null
@@ -1,67 +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 PMA_DRIVER_H
-#define PMA_DRIVER_H
-
-#include
-
-#include "interpret.h"
-
-namespace cartesi {
-
-/// \file
-/// \brief Declares pma_driver, which provides callback functions for reading and writing to device memory ranges
-
-// Forward declarations
-class i_device_state_access;
-
-/// \brief Prototype for callback invoked when machine wants to read from a device range.
-/// \param context Device-specific context
-/// \param da Object through which the machine state can be accessed.
-/// \param offset Offset of requested value from range base address.
-/// \param val Pointer to word where value will be stored.
-/// \param log2_size log2 of size of value to read (0 = uint8_t, 1 = uint16_t, 2 = uint32_t, 3 = uint64_t).
-/// \returns True if operation succeeded, false otherwise.
-using device_read = bool (*)(void *context, i_device_state_access *da, uint64_t offset, uint64_t *val, int log2_size);
-
-/// \brief Default read callback issues error on reads.
-bool device_read_error(void *context, i_device_state_access *da, uint64_t offset, uint64_t *val, int log2_size);
-
-/// \brief Prototype for callback invoked when machine wants to write to a range.
-/// \param context Device-specific context
-/// \param da Object through which the machine state can be accessed.
-/// \param offset Offset of requested value from range base address.
-/// \param val Word to be written at \p offset.
-/// \param log2_size log2 of size of value to read (0 = uint8_t, 1 = uint16_t, 2 = uint32_t, 3 = uint64_t).
-/// \returns execute::failure if operation failed, otherwise other success enumeration if operation succeeded.
-using device_write = execute_status (*)(void *context, i_device_state_access *da, uint64_t offset, uint64_t val,
- int log2_size);
-
-/// \brief Default write callback issues error on write.
-execute_status device_write_error(void *context, i_device_state_access *da, uint64_t offset, uint64_t val,
- int log2_size);
-
-/// \brief Driver for device memory ranges.
-struct pma_driver final {
- const char *name{""};
- device_read read{device_read_error};
- device_write write{device_write_error};
-};
-
-} // namespace cartesi
-
-#endif
diff --git a/src/pma.cpp b/src/pma.cpp
deleted file mode 100644
index 6f566cec5..000000000
--- a/src/pma.cpp
+++ /dev/null
@@ -1,220 +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 "pma.h"
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "is-pristine.h"
-#include "os.h"
-#include "pma-constants.h"
-#include "pma-driver.h"
-#include "unique-c-ptr.h"
-
-namespace cartesi {
-
-using namespace std::string_literals;
-
-pma_memory::pma_memory(const std::string &description, uint64_t length, const callocd & /*c*/) try :
- m_ptr{make_unique_calloc(length)},
- m_host_memory{std::get(m_ptr).get()} {
-} catch (std::exception &e) {
- throw std::runtime_error{e.what() + " when initializing "s + description};
-}
-
-pma_memory::pma_memory(const std::string &description, uint64_t length, const std::string &path, const callocd &c) :
- pma_memory{description, length, c} {
- // Try to load image file, if any
- if (!path.empty()) {
- auto fp = make_unique_fopen(path.c_str(), "rb", std::nothrow_t{});
- if (!fp) {
- throw std::system_error{errno, std::generic_category(),
- "error opening image file '"s + path + "' when initializing "s + description};
- }
- // Get file size
- if (fseek(fp.get(), 0, SEEK_END) != 0) {
- throw std::system_error{errno, std::generic_category(),
- "error obtaining length of image file '"s + path + "' when initializing "s + description};
- }
- const auto file_length = static_cast(ftello(fp.get()));
- if (fseek(fp.get(), 0, SEEK_SET) != 0) {
- throw std::system_error{errno, std::generic_category(),
- "error obtaining length of image file '"s + path + "' when initializing "s + description};
- }
- // Check against PMA range size
- if (file_length > length) {
- throw std::runtime_error{"image file '"s + path + "' of "s + description + " is too large for range"s};
- }
- // Read to host memory
- const auto read_length = static_cast(fread(m_host_memory, 1, length, fp.get()));
- if (read_length != file_length) {
- throw std::runtime_error{"error reading from image file '"s + path + "' when initializing "s + description};
- }
- }
-}
-
-pma_memory::pma_memory(const std::string &description, uint64_t length, const std::string &path, const mmapd &m) try :
- m_ptr{make_unique_mmap(path.c_str(), length, m.shared)},
- m_host_memory{std::get(m_ptr).get()} {
-} catch (std::exception &e) {
- throw std::runtime_error{e.what() + " when initializing "s + description};
-}
-
-uint64_t pma_entry::get_istart() const {
- uint64_t istart = m_start;
- istart |= (static_cast