diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..d58d5c6c6 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +third-party/** linguist-vendored diff --git a/src/Makefile b/src/Makefile index f69dcb85a..e183c8b51 100644 --- a/src/Makefile +++ b/src/Makefile @@ -84,8 +84,8 @@ SLIRP_INC=-I$(BREW_PREFIX)/libslirp/include # Macports installation else ifneq (,$(PORT_PREFIX)) INSTALL_PREFIX=/opt/local -BOOST_LIB_DIR=-L$(INSTALL_PREFIX)/libexec/boost/1.81/lib -BOOST_INC=-I$(INSTALL_PREFIX)/libexec/boost/1.81/include +BOOST_LIB_DIR=-L$(INSTALL_PREFIX)/libexec/boost/1.87/lib +BOOST_INC=-I$(INSTALL_PREFIX)/libexec/boost/1.87/include SLIRP_LIB=-L$(INSTALL_PREFIX)/lib -lslirp SLIRP_INC=-I$(INSTALL_PREFIX)/include @@ -133,8 +133,8 @@ ifeq ($(slirp),yes) # Workaround for building with macports lua-luarocks installation machine.o: INCS+=$(SLIRP_INC) machine.clang-tidy: INCS+=$(SLIRP_INC) -virtio-net-carrier-slirp.o: INCS+=$(SLIRP_INC) -virtio-net-carrier-slirp.clang-tidy: INCS+=$(SLIRP_INC) +virtio-net-user-address-range.o: INCS+=$(SLIRP_INC) +virtio-net-user-address-range.clang-tidy: INCS+=$(SLIRP_INC) #INCS+=$(SLIRP_INC) LIBCARTESI_COMMON_LIBS+=$(SLIRP_LIB) else @@ -345,52 +345,43 @@ c-api: $(LIBCARTESI) $(LIBCARTESI_MERKLE_TREE) $(LIBCARTESI_JSONRPC) .PHONY: all generate use clean lint format format-lua check-format check-format-lua luacartesi hash c-api compile_flags.txt LIBCARTESI_OBJS:= \ - pma-driver.o \ - clint.o \ - clint-factory.o \ - plic.o \ - plic-factory.o \ - virtio-factory.o \ - virtio-device.o \ - virtio-console.o \ - virtio-p9fs.o \ - virtio-net.o \ - virtio-net-carrier-tuntap.o \ - virtio-net-carrier-slirp.o \ - dtb.o \ - os.o \ - htif.o \ - htif-factory.o \ - shadow-state.o \ - shadow-state-factory.o \ - shadow-pmas-factory.o \ - shadow-tlb.o \ - shadow-tlb-factory.o \ - shadow-uarch-state.o \ - shadow-uarch-state-factory.o \ - pma.o \ - machine.o \ - machine-config.o \ - json-util.o \ base64.o \ + clint-address-range.o \ + dtb.o \ + htif-address-range.o \ interpret.o \ - virtual-machine.o \ - sha3.o \ + json-util.o \ + machine-c-api.o \ + machine-config.o \ machine-merkle-tree.o \ + machine.o \ + memory-address-range.o \ + os.o \ + plic-address-range.o \ pristine-merkle-tree.o \ - machine-c-api.o \ + replay-step-state-access-interop.o \ + send-cmio-response.o \ + sha3.o \ + shadow-state-address-range.o \ + shadow-tlb-address-range.o \ + shadow-uarch-state-address-range.o \ + uarch-pristine-hash.o \ uarch-pristine-ram.o \ uarch-pristine-state-hash.o \ - uarch-pristine-hash.o \ - uarch-interpret.o \ - uarch-step.o \ uarch-reset-state.o \ - send-cmio-response.o \ - replay-step-state-access-interop.o + uarch-step.o \ + local-machine.o \ + uarch-interpret.o \ + virtio-address-range.o \ + virtio-console-address-range.o \ + virtio-p9fs-address-range.o \ + virtio-net-address-range.o \ + virtio-net-tuntap-address-range.o \ + virtio-net-user-address-range.o CARTESI_CLUA_OBJS:= \ clua.o \ - clua-i-virtual-machine.o \ + clua-i-machine.o \ uarch-pristine-ram.o \ uarch-pristine-state-hash.o \ uarch-pristine-hash.o @@ -411,7 +402,7 @@ MERKLE_TREE_HASH_OBJS:= \ merkle-tree-hash.o LIBCARTESI_JSONRPC_OBJS:= \ - jsonrpc-virtual-machine.o \ + jsonrpc-machine.o \ os.o \ jsonrpc-machine-c-api.o \ uarch-pristine-ram.o \ diff --git a/src/address-range-constants.h b/src/address-range-constants.h new file mode 100644 index 000000000..1c4fdb65f --- /dev/null +++ b/src/address-range-constants.h @@ -0,0 +1,77 @@ +// Copyright Cartesi and individual authors (see AUTHORS) +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along +// with this program (see COPYING). If not, see . +// + +#ifndef ADDRESS_RANGE_CONSTANTS_H +#define ADDRESS_RANGE_CONSTANTS_H + +#include + +#include "address-range-defines.h" + +namespace cartesi { + +/// \brief Fixed address ranges. +enum AR_ranges : uint64_t { + AR_SHADOW_STATE_START = EXPAND_UINT64_C(AR_SHADOW_STATE_START_DEF), ///< Start of shadow state range + AR_SHADOW_STATE_LENGTH = EXPAND_UINT64_C(AR_SHADOW_STATE_LENGTH_DEF), ///< Length of shadow state range + AR_PMAS_START = EXPAND_UINT64_C(AR_PMAS_START_DEF), ///< Start of PMAS list range + AR_PMAS_LENGTH = EXPAND_UINT64_C(AR_PMAS_LENGTH_DEF), ///< Length of PMAS list range + AR_DTB_START = EXPAND_UINT64_C(AR_DTB_START_DEF), ///< Start of DTB range + AR_DTB_LENGTH = EXPAND_UINT64_C(AR_DTB_LENGTH_DEF), ///< Length of DTB range + AR_SHADOW_TLB_START = EXPAND_UINT64_C(AR_SHADOW_TLB_START_DEF), ///< Start of shadow TLB range + AR_SHADOW_TLB_LENGTH = EXPAND_UINT64_C(AR_SHADOW_TLB_LENGTH_DEF), ///< Length of shadow TLB range + AR_SHADOW_UARCH_STATE_START = + EXPAND_UINT64_C(AR_SHADOW_UARCH_STATE_START_DEF), ///< Start of uarch shadow state range + AR_SHADOW_UARCH_STATE_LENGTH = + EXPAND_UINT64_C(AR_SHADOW_UARCH_STATE_LENGTH_DEF), ///< Length of uarch shadow state range + AR_CLINT_START = EXPAND_UINT64_C(AR_CLINT_START_DEF), ///< Start of CLINT range + AR_CLINT_LENGTH = EXPAND_UINT64_C(AR_CLINT_LENGTH_DEF), ///< Length of CLINT range + AR_PLIC_START = EXPAND_UINT64_C(AR_PLIC_START_DEF), ///< Start of PLIC range + AR_PLIC_LENGTH = EXPAND_UINT64_C(AR_PLIC_LENGTH_DEF), ///< Length of PLIC range + AR_HTIF_START = EXPAND_UINT64_C(AR_HTIF_START_DEF), ///< Start of HTIF range + AR_HTIF_LENGTH = EXPAND_UINT64_C(AR_HTIF_LENGTH_DEF), ///< Length of HTIF range + AR_UARCH_RAM_START = EXPAND_UINT64_C(AR_UARCH_RAM_START_DEF), ///< Start of uarch RAM range + AR_UARCH_RAM_LENGTH = EXPAND_UINT64_C(AR_UARCH_RAM_LENGTH_DEF), ///< Length of uarch RAM range + AR_CMIO_RX_BUFFER_START = EXPAND_UINT64_C(AR_CMIO_RX_BUFFER_START_DEF), ///< Start of CMIO RX buffer range + AR_CMIO_RX_BUFFER_LOG2_SIZE = EXPAND_UINT64_C(AR_CMIO_RX_BUFFER_LOG2_SIZE_DEF), ///< Log2 of CMIO RX buffer range + AR_CMIO_RX_BUFFER_LENGTH = (UINT64_C(1) << AR_CMIO_RX_BUFFER_LOG2_SIZE_DEF), ///< Length of CMIO RX buffer range + AR_CMIO_TX_BUFFER_START = EXPAND_UINT64_C(AR_CMIO_TX_BUFFER_START_DEF), ///< Start of CMIO TX buffer range + AR_CMIO_TX_BUFFER_LOG2_SIZE = EXPAND_UINT64_C(AR_CMIO_TX_BUFFER_LOG2_SIZE_DEF), ///< Log2 of CMIO TX buffer range + AR_CMIO_TX_BUFFER_LENGTH = (UINT64_C(1) << AR_CMIO_TX_BUFFER_LOG2_SIZE_DEF), ///< Length of CMIO TX buffer range + AR_DRIVE_START = EXPAND_UINT64_C(AR_DRIVE_START_DEF), ///< Start address for flash drive ranges + AR_DRIVE_OFFSET = EXPAND_UINT64_C(AR_DRIVE_OFFSET_DEF), ///< Offset for extra flash drive ranges + + AR_FIRST_VIRTIO_START = EXPAND_UINT64_C(AR_FIRST_VIRTIO_START_DEF), ///< Start of first VIRTIO range + AR_VIRTIO_LENGTH = EXPAND_UINT64_C(AR_VIRTIO_LENGTH_DEF), ///< Length of each VIRTIO range + AR_LAST_VIRTIO_END = EXPAND_UINT64_C(AR_LAST_VIRTIO_END_DEF), ///< End of last VIRTIO range + + AR_RAM_START = EXPAND_UINT64_C(AR_RAM_START_DEF), ///< Start of RAM range +}; + +/// \brief PMA constants. +enum AR_constants : uint64_t { + AR_PAGE_SIZE_LOG2 = EXPAND_UINT64_C(AR_PAGE_SIZE_LOG2_DEF), ///< Log2 of physical memory page size. + AR_PAGE_SIZE = (UINT64_C(1) << AR_PAGE_SIZE_LOG2_DEF), ///< Physical memory page size. +}; + +/// \brief PMA masks. +enum AR_masks : uint64_t { + AR_ADDRESSABLE_MASK = ((UINT64_C(1) << 56) - 1) ///< Mask for addressable ranges. +}; + +} // namespace cartesi + +#endif diff --git a/src/address-range-defines.h b/src/address-range-defines.h new file mode 100644 index 000000000..4f49e9141 --- /dev/null +++ b/src/address-range-defines.h @@ -0,0 +1,58 @@ +// Copyright Cartesi and individual authors (see AUTHORS) +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along +// with this program (see COPYING). If not, see . +// + +#ifndef AR_DEFINES_H +#define AR_DEFINES_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage,cppcoreguidelines-macro-to-enum,modernize-macro-to-enum) +#define AR_SHADOW_STATE_START_DEF 0x0 ///< Shadow start address +#define AR_SHADOW_STATE_LENGTH_DEF 0x1000 ///< Shadow length in bytes +#define AR_PMAS_START_DEF 0x10000 ///< PMA Array start address +#define AR_PMAS_LENGTH_DEF 0x1000 ///< PMA Array length in bytes +#define AR_SHADOW_TLB_START_DEF 0x20000 ///< TLB start address +#define AR_SHADOW_TLB_LENGTH_DEF 0x6000 ///< TLB length in bytes +#define AR_SHADOW_UARCH_STATE_START_DEF 0x400000 ///< microarchitecture shadow state start address +#define AR_SHADOW_UARCH_STATE_LENGTH_DEF 0x1000 ///< microarchitecture shadow state length +#define AR_UARCH_RAM_START_DEF 0x600000 ///< microarchitecture RAM start address +#define AR_UARCH_RAM_LENGTH_DEF 0x200000 ///< microarchitecture RAM length +#define AR_CLINT_START_DEF 0x2000000 ///< CLINT start address +#define AR_CLINT_LENGTH_DEF 0xC0000 ///< CLINT length in bytes +#define AR_PLIC_START_DEF 0x40100000 ///< Start of PLIC range +#define AR_PLIC_LENGTH_DEF 0x00400000 ///< Length of PLIC range +#define AR_HTIF_START_DEF 0x40008000 ///< HTIF base address (to_host) +#define AR_HTIF_LENGTH_DEF 0x1000 ///< HTIF length in bytes +#define AR_FIRST_VIRTIO_START_DEF 0x40010000 ///< Start of first VIRTIO range +#define AR_VIRTIO_LENGTH_DEF 0x1000 ///< Length of each VIRTIO range +#define AR_LAST_VIRTIO_END_DEF 0x40020000 ///< End of last VIRTIO range +#define AR_DTB_START_DEF 0x7ff00000 ///< DTB start address +#define AR_DTB_LENGTH_DEF 0x100000 ///< DTB length in bytes +#define AR_CMIO_RX_BUFFER_START_DEF 0x60000000 ///< CMIO RX buffer start address +#define AR_CMIO_RX_BUFFER_LOG2_SIZE_DEF 21 ///< log2 of CMIO RX buffer length in bytes +#define AR_CMIO_TX_BUFFER_START_DEF 0x60800000 ///< CMIO TX buffer start address +#define AR_CMIO_TX_BUFFER_LOG2_SIZE_DEF 21 ///< log2 of CMIO TX buffer length in bytes +#define AR_DRIVE_START_DEF 0x80000000000000 ///< Start PMA address for flash drives +#define AR_DRIVE_OFFSET_DEF 0x10000000000000 ///< PMA offset for extra flash drives + +#define AR_RAM_START_DEF 0x80000000 ///< RAM start address + +#define AR_PAGE_SIZE_LOG2_DEF 12 ///< log2 of physical memory page size. + +// helper for using UINT64_C with defines +#ifndef EXPAND_UINT64_C +#define EXPAND_UINT64_C(a) UINT64_C(a) +#endif +// NOLINTEND(cppcoreguidelines-macro-usage,cppcoreguidelines-macro-to-enum,modernize-macro-to-enum) +#endif /* end of include guard: AR_DEFINES_H */ diff --git a/src/machine-memory-range-descr.h b/src/address-range-description.h similarity index 74% rename from src/machine-memory-range-descr.h rename to src/address-range-description.h index 787275b9d..325425b98 100644 --- a/src/machine-memory-range-descr.h +++ b/src/address-range-description.h @@ -14,8 +14,8 @@ // with this program (see COPYING). If not, see . // -#ifndef MACHINE_MEMORY_RANGE_DESCR_H -#define MACHINE_MEMORY_RANGE_DESCR_H +#ifndef ADDRESS_RANGE_DESCRIPTION_H +#define ADDRESS_RANGE_DESCRIPTION_H #include #include @@ -23,15 +23,15 @@ namespace cartesi { -/// \brief Description of memory range used for introspection (i.e., get_memory_ranges()) -struct machine_memory_range_descr { +/// \brief Description of an address range used for introspection (i.e., get_address_ranges()) +struct address_range_description { uint64_t start = 0; ///< Start of memory range uint64_t length = 0; ///< Length of memory range std::string description; ///< User-friendly description for memory range }; -/// \brief List of memory range descriptions used for introspection (i.e., get_memory_ranges()) -using machine_memory_range_descrs = std::vector; +/// \brief List of address range descriptions used for introspection (i.e., get_address_ranges()) +using address_range_descriptions = std::vector; } // namespace cartesi diff --git a/src/address-range.h b/src/address-range.h new file mode 100644 index 000000000..be681ccfb --- /dev/null +++ b/src/address-range.h @@ -0,0 +1,389 @@ +// Copyright Cartesi and individual authors (see AUTHORS) +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify it under +// the terms of the GNU Lesser General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) any +// later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along +// with this program (see COPYING). If not, see . +// + +#ifndef ADDRESS_RANGE_H +#define ADDRESS_RANGE_H + +#include +#include +#include +#include +#include + +#include "assert-printf.h" +#include "i-device-state-access.h" +#include "interpret.h" +#include "pmas.h" + +namespace cartesi { + +// Forward declarations +class machine; + +/// \file +/// \brief Physical address range + +/// \brief Physical Address Range. +/// \details The target's physical address layout is described by an array of specializations of such ranges. +class address_range { + + std::array m_description; ///< Description of address range for use in error messages. + uint64_t m_start; ///< Target physical address where range starts. + uint64_t m_length; ///< Length of range, in bytes. + uint64_t m_length_bit_ceil; ///< Smallest power of 2 that is not smaller than length, in bytes. + pmas_flags m_flags; ///< Physical memory attribute flags for range. + +public: + /// \brief Noexcept constexpr constructor for empty ranges with description + /// \detail Can be used to initialize a constexpr empty range + template + explicit constexpr address_range(const char (&description)[N]) noexcept : + m_description{}, + m_start{0}, + m_length{0}, + m_length_bit_ceil{0}, + m_flags{} { + for (unsigned i = 0; i < std::min(N, m_description.size() - 1); ++i) { + m_description[i] = description[i]; + } + } + + address_range(const address_range &other) = default; + address_range &operator=(const address_range &other) = default; + address_range(address_range &&other) = default; + address_range &operator=(address_range &&other) = default; + // NOLINTNEXTLINE(hicpp-use-equals-default,modernize-use-equals-default) + constexpr virtual ~address_range() {}; // = default; // doesn't work due to bug in gcc + + template + [[noreturn]] + static void ABRTF(ABRT abrt, const char (&fmt)[N], ARGS... args) { + char buf[256]{}; + std::ignore = snprintf(buf, std::size(buf), fmt, args...); + abrt(buf); + __builtin_trap(); + } + + /// \brief Constructor + /// \tparam ABRT type of function used to abort and report errors + /// \param description Description of address range for use in error messages (will be copied) + /// \param start Target physical address where range starts + /// \param length Length of range, in bytes + /// \param f Phyical memory attribute flags for range + template + address_range(const char *description, uint64_t start, uint64_t length, const pmas_flags &flags, ABRT abrt) : + m_description{}, + m_start{start}, + m_length{length}, + m_length_bit_ceil{(length >> 63) == 0 ? std::bit_ceil(length) : 0}, + m_flags{flags} { + // Non-empty description is mandatory + if (description == nullptr || *description == '\0') { + ABRTF(abrt, "address range 0x%" PRIx64 ":0x%" PRIx64 " has empty description", m_start, m_length); + } + for (unsigned i = 0; i < m_description.size() - 1 && description[i] != '\0'; ++i) { + m_description[i] = description[i]; + } + // All address ranges must be page-aligned + if ((m_length & ~PMA_ISTART_START_MASK) != 0) { + ABRTF(abrt, "length must be multiple of page size when initializing %s", description); + } + if ((m_start & ~PMA_ISTART_START_MASK) != 0) { + ABRTF(abrt, "start of %s (0x%" PRIx64 ") must be aligned to page boundary of %" PRId64 " bytes", + description, start, AR_PAGE_SIZE); + } + // It must be possible to round length up to the next power of two + if (m_length_bit_ceil == 0) { + ABRTF(abrt, "address range too long when initializing %s", description); + } + // Empty range must really be empty + if (m_length == 0) { + if (m_start != 0) { + ABRTF(abrt, "range with length 0 must start at 0 when initializing %s", description); + } + if (m_flags.M) { + ABRTF(abrt, "memory address range cannot have length 0 when initializing %s", description); + } + if (m_flags.IO) { + ABRTF(abrt, "device address range cannot have length 0 when initializing %s", description); + } + } + } + + /// \brief Checks if a range of addresses is entirely contained within this range + /// \param offset Start of range of interest, relative to start of this range + /// \param length Length of range of interest, in bytes + /// \returns True if and only if range of interest is entirely contained within this range + bool contains_relative(uint64_t offset, uint64_t length) const noexcept { + return get_length() >= length && offset <= get_length() - length; + } + + /// \brief Checks if a range of addresses is entirely contained within this range + /// \param start Target phyisical address of start of range of interest + /// \param length Length of range of interest, in bytes + /// \returns True if and only if range of interest is entirely contained within this range + bool contains_absolute(uint64_t start, uint64_t length) const noexcept { + return start >= get_start() && contains_relative(start - get_start(), length); + } + + /// \brief Returns PMA flags used during construction + /// \returns Flags + const pmas_flags &get_flags() const noexcept { + return m_flags; + } + + /// \brief Returns description of address range for use in error messages. + /// \returns Description + const char *get_description() const noexcept { + return m_description.data(); + } + + /// \brief Returns target physical address where range starts. + /// \returns Start of range + uint64_t get_start() const noexcept { + return m_start; + } + + /// \brief Returns length of range, in bytes. + /// \returns Length of range + uint64_t get_length() const noexcept { + return m_length; + } + + /// \brief Returns smallest power of 2 that is not smaller than range length, in bytes + /// \returns Bit-ceil of length of range + uint64_t get_length_bit_ceil() const noexcept { + return m_length_bit_ceil; + } + + /// \brief Test if address range is occupied by memory + /// \returns True if and only if range is occupied by memory + /// \details In this case, get_host_memory() is guaranteed not to return nullptr. + bool is_memory() const noexcept { + return m_flags.M; + } + + /// \brief Test if address range is occupied by a device + /// \returns True if and only if range is occupied by a device + /// \details In this case, read_device() and write_device() are operational. + bool is_device() const noexcept { + return m_flags.IO; + } + + /// \brief Test if address range is empty + /// \returns True if and only if range is empty + /// \details Empty ranges should be used only for sentinels. + bool is_empty() const noexcept { + return m_length == 0; + } + + /// \brief Tests if range is readable + /// \returns True if and only if range is readable from within the machine. + bool is_readable() const noexcept { + return m_flags.R; + } + + /// \brief Tests if range is writeable + /// \returns True if and only if range is writeable from within the machine. + bool is_writeable() const noexcept { + return m_flags.W; + } + + /// \brief Tests if range is executable + /// \returns True if and only if range is executable from within the machine. + bool is_executable() const noexcept { + return m_flags.X; + } + + /// \brief Tests if range is read-idempotent + /// \returns True if and only if what is read from range remains there until written to + bool is_read_idempotent() const noexcept { + return m_flags.IR; + } + + /// \brief Tests if range is write-idempotent + /// \returns True if and only if what is written to range remains there and can be read until written to again + bool is_write_idempotent() const noexcept { + return m_flags.IW; + } + + /// \brief Returns driver ID associated to range + /// \returns Teh driver ID + PMA_ISTART_DID get_driver_id() const noexcept { + return m_flags.DID; + } + + /// \brief Returns packed address range istart field as per whitepaper + /// \returns Packed address range istart + uint64_t get_istart() const noexcept { + return pmas_pack_istart(m_flags, m_start); + } + + /// \brief Returns encoded addres range ilength field as per whitepaper + /// \returns Packed address range ilength + /// \details This currently contains only the length itself + uint64_t get_ilength() const noexcept { + return get_length(); + } + + /// \brief Read contents from address range with, no side-effects. + /// \param m Reference to machine. + /// \param offset Offset within range to start reading. + /// \param length Number of bytes to read. + /// \param data Receives pointer to start of data, or nullptr if data is constant *and* pristine (filled with + /// zeros). + /// \param scratch Pointer to memory buffer that must be able to hold \p length bytes. + /// \returns True if operation succeeded, false otherwise. + bool peek(const machine &m, uint64_t offset, uint64_t length, const unsigned char **data, + unsigned char *scratch) const noexcept { + return do_peek(m, offset, length, data, scratch); + }; + + // ----- + // These are only for device ranges + // ----- + + /// \brief Reads a word from a device + /// \param da State access object through which the machine state can be accessed. + /// \param offset Where to start reading, relative to start of this range. + /// \param log2_size Log2 of size of value to read (0=uint8_t, 1=uint16_t, 2=uint32_t, 3=uint64_t). + /// \param pval Pointer to word where value will be stored. + /// \returns True if operation succeeded, false otherwise. + bool read_device(i_device_state_access *da, uint64_t offset, int log2_size, uint64_t *pval) const noexcept { + return do_read_device(da, offset, log2_size, pval); + } + + /// \brief Writes a word to a device + /// \param da State access object through which the machine state can be accessed. + /// \param offset Where to start reading, relative to start of this range. + /// \param log2_size Log2 of size of value to write (0=uint8_t, 1=uint16_t, 2=uint32_t, 3=uint64_t). + /// \param val Value to write. + /// \returns execute::failure if operation failed, otherwise a success code if operation succeeded. + execute_status write_device(i_device_state_access *da, uint64_t offset, int log2_size, uint64_t val) noexcept { + return do_write_device(da, offset, log2_size, val); + } + + // ----- + // These are only for memory ranges + // ----- + + /// \brief Returns start of associated memory region in host + /// \returns Pointer to memory + const unsigned char *get_host_memory() const noexcept { + return do_get_host_memory(); + } + + /// \brief Returns start of associated memory region in host + /// \returns Pointer to memory + unsigned char *get_host_memory() noexcept { + return do_get_host_memory(); + } + + /// \brief Mark a given page as dirty + /// \param offset Any offset in range within desired page + void mark_dirty_page(uint64_t offset) noexcept { + do_mark_dirty_page(offset); + } + + /// \brief Mark all pages in a range of interest as dirty + /// \param offset Start of range of interest, relative to start of this range + /// \param length Length of range of interest, in bytes + void mark_dirty_pages(uint64_t offset, uint64_t length) noexcept { + auto offset_aligned = offset &= ~(AR_PAGE_SIZE - 1); + const auto length_aligned = length + (offset - offset_aligned); + for (; offset_aligned < length_aligned; offset_aligned += AR_PAGE_SIZE) { + mark_dirty_page(offset_aligned); + } + } + + /// \brief Mark a given page as clean + /// \param offset Any offset in range within desired page + void mark_clean_page(uint64_t offset) noexcept { + do_mark_clean_page(offset); + } + + /// \brief Marks all pages in range as clean + void mark_pages_clean() noexcept { + do_mark_pages_clean(); + } + + /// \brief Tests if a given page is dirty + /// \param offset Any offset in range within desired page + /// \returns True if and only if page is marked dirty + bool is_page_marked_dirty(uint64_t offset) const noexcept { + return do_is_page_marked_dirty(offset); + } + +private: + // Default implementation of peek() always fails + virtual bool do_peek(const machine & /*m*/, uint64_t /*offset*/, uint64_t /*length*/, + const unsigned char ** /*data*/, unsigned char * /*scratch*/) const noexcept { + return false; + } + + // Default implementation of read_device() for non-device ranges always fails + virtual bool do_read_device(i_device_state_access * /*a*/, uint64_t /*offset*/, int /*log2_size*/, + uint64_t * /*val*/) const noexcept { + return false; + } + + // Default implementation of write_device() for non-device ranges always fails + virtual execute_status do_write_device(i_device_state_access * /*a*/, uint64_t /*offset*/, int /* log2_size */, + uint64_t /*val*/) noexcept { + return execute_status::failure; + } + + // Default implementation of get_host_memory() for non-memory ranges returns nullptr + virtual const unsigned char *do_get_host_memory() const noexcept { + return nullptr; + } + + virtual unsigned char *do_get_host_memory() noexcept { + return nullptr; + } + + // Defaul implemenation always assumes every page is always dirty + virtual void do_mark_dirty_page(uint64_t /*offset*/) noexcept { + ; + } + + virtual void do_mark_clean_page(uint64_t /*offset*/) noexcept { + ; + } + + virtual void do_mark_pages_clean() noexcept { + ; + } + + virtual bool do_is_page_marked_dirty(uint64_t /*offset*/) const noexcept { + return true; + } +}; + +template +constexpr static auto make_empty_address_range(const char (&description)[N]) { + return address_range{description}; +} + +template +static inline auto make_address_range(const char *description, uint64_t start, uint64_t length, pmas_flags f, + ABRT abrt) { + return address_range{description, start, length, f, abrt}; +} + +} // namespace cartesi + +#endif // OCCUPIED_ADDRESS_RANGE_H diff --git a/src/dump.h b/src/assert-printf.h similarity index 59% rename from src/dump.h rename to src/assert-printf.h index f7c00c538..0f0f5b41b 100644 --- a/src/dump.h +++ b/src/assert-printf.h @@ -13,22 +13,34 @@ // You should have received a copy of the GNU Lesser General Public License along // with this program (see COPYING). If not, see . // -#ifndef DUMP_H -#define DUMP_H -#include +#ifndef ASSERT_PRINTF_H +#define ASSERT_PRINTF_H + +/// \file +/// \brief Microarchitecture-dependent includes for printf and assert #ifdef MICROARCHITECTURE -template -static inline void D_PRINTF(const char (&fmt)[N], ARGS... args) { - std::ignore = printf(fmt, args...); -} +#include "uarch-runtime.h" #else +#include #include -template -static inline auto D_PRINTF(const char (&fmt)[N], ARGS... args) { - std::ignore = fprintf(stderr, fmt, args...); -} #endif -#endif // DUMP_H +#include +#include + +static inline void d_vprintf(const char *fmt, va_list ap) { + std::ignore = vfprintf(stderr, fmt, ap); +} + +// Better to use C-style variadic function that checks for format! +// NOLINTNEXTLINE(cert-dcl50-cpp) +__attribute__((__format__(__printf__, 1, 2))) static inline void d_printf(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + d_vprintf(fmt, ap); + va_end(ap); +} + +#endif diff --git a/src/cartesi-machine.lua b/src/cartesi-machine.lua index a6982ea0e..a339a0985 100755 --- a/src/cartesi-machine.lua +++ b/src/cartesi-machine.lua @@ -87,10 +87,6 @@ where options are: --ram-length= set RAM length. - --dtb-image= - name of file containing DTB image - (default: auto generated flattened device tree). - --no-bootargs clear default bootargs. @@ -106,9 +102,10 @@ where options are: : is one of label: