diff --git a/src/Makefile b/src/Makefile index 61edde020..64ae107a3 100644 --- a/src/Makefile +++ b/src/Makefile @@ -337,6 +337,7 @@ LIBCARTESI_OBJS:= \ virtio-net-carrier-slirp.o \ dtb.o \ os.o \ + os-mmap.o \ htif.o \ htif-factory.o \ shadow-state.o \ diff --git a/src/json-util.cpp b/src/json-util.cpp index 7e0ae792a..da7abedfd 100644 --- a/src/json-util.cpp +++ b/src/json-util.cpp @@ -789,6 +789,7 @@ void ju_get_opt_field(const nlohmann::json &j, const K &key, machine_runtime_con ju_get_opt_field(j[key], "skip_root_hash_store"s, value.skip_root_hash_store, path + to_string(key) + "/"); ju_get_opt_field(j[key], "skip_version_check"s, value.skip_version_check, path + to_string(key) + "/"); ju_get_opt_field(j[key], "soft_yield"s, value.soft_yield, path + to_string(key) + "/"); + ju_get_opt_field(j[key], "backing_storage"s, value.backing_storage, path + to_string(key) + "/"); } template void ju_get_opt_field(const nlohmann::json &j, const uint64_t &key, machine_runtime_config &value, @@ -1971,6 +1972,7 @@ void to_json(nlohmann::json &j, const machine_runtime_config &runtime) { {"skip_root_hash_store", runtime.skip_root_hash_store}, {"skip_version_check", runtime.skip_version_check}, {"soft_yield", runtime.soft_yield}, + {"backing_storage", runtime.backing_storage}, }; } diff --git a/src/machine-runtime-config.h b/src/machine-runtime-config.h index 649ea4aaf..d79e6e83e 100644 --- a/src/machine-runtime-config.h +++ b/src/machine-runtime-config.h @@ -18,6 +18,7 @@ #define MACHINE_RUNTIME_CONFIG_H #include +#include /// \file /// \brief Runtime configuration for machines. @@ -42,6 +43,7 @@ struct machine_runtime_config { bool skip_root_hash_store{}; bool skip_version_check{}; bool soft_yield{}; + std::string backing_storage{}; }; /// \brief CONCURRENCY constants diff --git a/src/machine.cpp b/src/machine.cpp index fa184d2ad..09016f171 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -108,39 +108,6 @@ const pma_entry::flags machine::m_cmio_tx_buffer_flags{ PMA_ISTART_DID::cmio_tx_buffer // DID }; -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_config &c) { - const auto description = "cmio rx buffer memory range"s; - if (!c.rx_buffer.image_filename.empty()) { - return make_mmapd_memory_pma_entry(description, PMA_CMIO_RX_BUFFER_START, PMA_CMIO_RX_BUFFER_LENGTH, - c.rx_buffer.image_filename, c.rx_buffer.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_config &c) { - const auto description = "cmio tx buffer memory range"s; - if (!c.tx_buffer.image_filename.empty()) { - return make_mmapd_memory_pma_entry(description, PMA_CMIO_TX_BUFFER_START, PMA_CMIO_TX_BUFFER_LENGTH, - c.tx_buffer.image_filename, c.tx_buffer.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 (m_s.pmas.capacity() <= m_s.pmas.size()) { // NOLINT(readability-static-accessed-through-instance) throw std::runtime_error{"too many PMAs when adding "s + pma.get_description()}; @@ -199,7 +166,9 @@ void machine::replace_memory_range(const memory_range_config &range) { throw std::invalid_argument{"attempt to replace a protected range "s + pma.get_description()}; } // replace range preserving original flags - pma = make_memory_range_pma_entry(pma.get_description(), range).set_flags(pma.get_flags()); + pma = make_mmapd_memory_pma_entry(pma.get_description(), range.start, range.length, range.image_filename, + range.shared) + .set_flags(pma.get_flags()); return; } } @@ -322,19 +291,22 @@ machine::machine(const machine_config &c, const machine_runtime_config &r) : write_reg(reg::iflags, m_c.processor.iflags); write_reg(reg::iunrep, m_c.processor.iunrep); - // Register RAM - if (m_c.ram.image_filename.empty()) { - register_pma_entry(make_callocd_memory_pma_entry("RAM"s, PMA_RAM_START, m_c.ram.length).set_flags(m_ram_flags)); - } else { - register_pma_entry(make_callocd_memory_pma_entry("RAM"s, PMA_RAM_START, m_c.ram.length, m_c.ram.image_filename) - .set_flags(m_ram_flags)); + // TODO(edubart): handle case when backing storage is the same dir + // TODO(edubart): remove directory + files on failure + if (!m_r.backing_storage.empty()) { + os_mkdir(m_r.backing_storage.c_str(), 0700); } + // Register uarch PMAs + m_uarch.register_pmas(m_r); + + // Register RAM + register_pma_entry(make_memory_range_pma_entry("RAM"s, m_ram_flags, PMA_RAM_START, m_c.ram.length, + m_c.ram.image_filename, false, m_r)); + // Register DTB - pma_entry &dtb = register_pma_entry((m_c.dtb.image_filename.empty() ? - make_callocd_memory_pma_entry("DTB"s, PMA_DTB_START, PMA_DTB_LENGTH) : - make_callocd_memory_pma_entry("DTB"s, PMA_DTB_START, PMA_DTB_LENGTH, m_c.dtb.image_filename)) - .set_flags(m_dtb_flags)); + pma_entry &dtb = register_pma_entry(make_memory_range_pma_entry("DTB"s, m_dtb_flags, PMA_DTB_START, PMA_DTB_LENGTH, + m_c.dtb.image_filename, false, m_r)); // Register all flash drives int i = 0; @@ -360,13 +332,18 @@ machine::machine(const machine_config &c, const machine_runtime_config &r) : } f.length = length; } - register_pma_entry(make_flash_drive_pma_entry(flash_description, f)); + register_pma_entry(make_memory_range_pma_entry(flash_description, m_flash_drive_flags, f.start, f.length, + f.image_filename, f.shared, m_r)); i++; } // Register cmio memory ranges - register_pma_entry(make_cmio_tx_buffer_pma_entry(m_c.cmio)); - register_pma_entry(make_cmio_rx_buffer_pma_entry(m_c.cmio)); + register_pma_entry( + make_memory_range_pma_entry("cmio tx buffer memory range"s, m_cmio_tx_buffer_flags, PMA_CMIO_TX_BUFFER_START, + PMA_CMIO_TX_BUFFER_LENGTH, m_c.cmio.tx_buffer.image_filename, m_c.cmio.tx_buffer.shared, m_r)); + register_pma_entry( + make_memory_range_pma_entry("cmio rx buffer memory range"s, m_cmio_rx_buffer_flags, PMA_CMIO_RX_BUFFER_START, + PMA_CMIO_RX_BUFFER_LENGTH, m_c.cmio.rx_buffer.image_filename, m_c.cmio.rx_buffer.shared, m_r)); // Register HTIF device register_pma_entry(make_htif_pma_entry(PMA_HTIF_START, PMA_HTIF_LENGTH, &m_r.htif)); @@ -485,7 +462,7 @@ machine::machine(const machine_config &c, const machine_runtime_config &r) : if (!m_c.tlb.image_filename.empty()) { // Create a temporary PMA entry just to load TLB contents from an image file pma_entry tlb_image_pma = make_mmapd_memory_pma_entry("shadow TLB device"s, PMA_SHADOW_TLB_START, - PMA_SHADOW_TLB_LENGTH, m_c.tlb.image_filename, false); + PMA_SHADOW_TLB_LENGTH, m_c.tlb.image_filename); unsigned char *hmem = tlb_image_pma.get_memory().get_host_memory(); for (uint64_t i = 0; i < PMA_TLB_SIZE; ++i) { load_tlb_entry(*this, i, hmem); @@ -771,9 +748,8 @@ static void store_hash(const machine::hash_type &h, const std::string &dir) { } void machine::store(const std::string &dir) const { - if (os_mkdir(dir.c_str(), 0700)) { - throw std::system_error{errno, std::generic_category(), "error creating directory '"s + dir + "'"s}; - } + // TODO(edubart): handle the case of saving to backing storage + os_mkdir(dir.c_str(), 0700); if (!m_r.skip_root_hash_store) { if (!update_merkle_tree()) { throw std::runtime_error{"error updating Merkle tree"}; @@ -789,6 +765,19 @@ void machine::store(const std::string &dir) const { // NOLINTNEXTLINE(modernize-use-equals-default) machine::~machine() { + if (!m_r.backing_storage.empty()) { + try { + // TODO(edubart): save root hash? + // TODO(edubart): make tlb a memory PMA? + store_device_pma(*this, find_pma_entry(PMA_SHADOW_TLB_START), m_r.backing_storage); + auto c = get_serialization_config(); + c.store(m_r.backing_storage); + } catch (std::exception &e) { + // Silently fail + (void) fprintf(stderr, "unable to save backing storage machine config: %s\n", e.what()); + } + } + // Cleanup TTY if console input was enabled if (m_c.htif.console_getchar || has_virtio_console()) { os_close_tty(); diff --git a/src/machine.h b/src/machine.h index 9d5472c3b..93248f43c 100644 --- a/src/machine.h +++ b/src/machine.h @@ -77,28 +77,6 @@ class machine final { /// \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 Optional cmio configuration - /// \returns New PMA entry with rx buffer flags already set. - static pma_entry make_cmio_rx_buffer_pma_entry(const cmio_config &cmio_config); - - /// \brief Creates a new cmio tx buffer PMA entry. - // \param c Optional cmio configuration - /// \returns New PMA entry with tx buffer flags already set. - static pma_entry make_cmio_tx_buffer_pma_entry(const cmio_config &cmio_config); - /// \brief Saves PMAs into files for serialization /// \param config Machine config to be stored /// \param directory Directory where PMAs will be stored diff --git a/src/os-features.h b/src/os-features.h index e81244deb..c98f24068 100644 --- a/src/os-features.h +++ b/src/os-features.h @@ -40,6 +40,10 @@ #define HAVE_MMAP #endif +#if !defined(NO_FLOCK) && !defined(_WIN32) && !defined(__wasi__) +#define HAVE_FLOCK +#endif + #if !defined(NO_MKDIR) #define HAVE_MKDIR #endif diff --git a/src/os-mmap.cpp b/src/os-mmap.cpp new file mode 100644 index 000000000..956e46d9c --- /dev/null +++ b/src/os-mmap.cpp @@ -0,0 +1,288 @@ +// 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 + +#include "os-features.h" +#include "os-mmap.h" +#include "unique-c-ptr.h" + +#if defined(HAVE_MMAP) +#include // open +#include // mmap/munmap +#include // fstat +#include // write/read/close +#endif + +#if defined(HAVE_FLOCK) +#include // flock +#endif + +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#include // _write/_close +#include // fstat +#include +#endif // _WIN32 + +namespace cartesi { + +using namespace std::string_literals; + +os_mmapd os_mmap(uint64_t length, int flags, const std::string &backing_path) { + if (backing_path.empty() && (flags & (OS_MMAP_LOCK_BACKING | OS_MMAP_SHARED | OS_MMAP_READONLY))) { + throw std::runtime_error{"backing file path must be specified"s}; + } + +#ifdef HAVE_MMAP + unsigned char *host_memory = nullptr; + int backing_fd = -1; + uint64_t backing_length = 0; + try { + if (!backing_path.empty()) { + // Determine file open flags + const bool writeable = (flags & OS_MMAP_SHARED) && !(flags & OS_MMAP_READONLY); + int oflags = (writeable ? O_RDWR : O_RDONLY); + oflags |= O_CLOEXEC; // to remove file locks on fork + exec + + // Try to open backing file + backing_fd = open(backing_path.c_str(), oflags); + if (backing_fd < 0) { + throw std::system_error{errno, std::generic_category(), + "could not open backing file '"s + backing_path + "'"s}; + } + + // Try to get file size + struct stat statbuf {}; + if (fstat(backing_fd, &statbuf) < 0) { + throw std::system_error{errno, std::generic_category(), + "unable to obtain length of backing file '"s + backing_path + "'"s}; + } + backing_length = static_cast(statbuf.st_size); + + // Check file length for shared mappings + if ((flags & OS_MMAP_SHARED) && backing_length != length) { + throw std::invalid_argument{"backing file '"s + backing_path + "' size ("s + + std::to_string(backing_length) + ") does not match range length ("s + std::to_string(length) + + ")"s}; + } + +#ifdef HAVE_FLOCK + // Set file lock + if (flags & OS_MMAP_LOCK_BACKING) { + const int flockop = (writeable ? LOCK_EX : LOCK_SH) | LOCK_NB; + if (flock(backing_fd, flockop) < 0) { + throw std::system_error{errno, std::generic_category(), + "could not lock backing file '"s + backing_path + "'"s}; + } + } +#endif + } + + // Determine map memory flags + int mflags = (flags & OS_MMAP_SHARED) ? MAP_SHARED : MAP_PRIVATE; + if (backing_fd < 0) { // The mapping is not backed by any file + mflags |= MAP_ANONYMOUS; + } + if (flags & OS_MMAP_NO_RESERVE) { + mflags |= MAP_NORESERVE; + } + // Determine map protection flags + int mprot = PROT_READ; + if (!(flags & OS_MMAP_READONLY)) { + mprot |= PROT_WRITE; + } + + // Try to map backing file to host memory + host_memory = static_cast(mmap(nullptr, length, mprot, mflags, backing_fd, 0)); + if (host_memory == MAP_FAILED) { + if (!backing_path.empty()) { + throw std::system_error{errno, std::generic_category(), + "could not map backing file '"s + backing_path + "' to memory"s}; + } else { + throw std::system_error{errno, std::generic_category(), "could not map memory"s}; + } + } + + if (backing_fd >= 0) { + // Retrieve system page size + const long page_size = sysconf(_SC_PAGESIZE); + if (page_size < 0) { + throw std::system_error{errno, std::generic_category(), "unable to retrieve system page size"s}; + } + // Determine length of the backing file based + const uint64_t backing_mmaped_length = (backing_length + (page_size - 1)) & ~(page_size - 1); + + if (backing_mmaped_length < length) { + unsigned char * + above_memory = // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,performance-no-int-to-ptr) + reinterpret_cast(reinterpret_cast(host_memory) + backing_mmaped_length); + const uint64_t above_length = length - backing_mmaped_length; + int above_mflags = MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS; + if (flags & OS_MMAP_NO_RESERVE) { + above_mflags |= MAP_NORESERVE; + } + + // Overwrite mapping + auto *got_above_memory = + static_cast(mmap(above_memory, above_length, mprot, above_mflags, -1, 0)); + if (got_above_memory != above_memory) { + throw std::system_error{errno, std::generic_category(), + "could not map memory space above backing file"s}; + } + } + } + + return os_mmapd{host_memory, length, flags, backing_fd, backing_length, backing_path}; + } catch (std::exception &e) { + // Close backing file + if (backing_fd >= 0) { + close(backing_fd); + } + // Unmap host memory + if (host_memory) { + munmap(host_memory, length); + } + throw; + } + +#elif defined(_WIN32) +#error "NYI" + /* + const int oflags = (shared ? _O_RDWR : _O_RDONLY) | _O_BINARY; + + // Try to open backing file + const int backing_file = _open(path, oflags); + if (backing_file < 0) { + throw std::system_error{errno, std::generic_category(), "could not open backing file '"s + path + "'"s}; + } + + // Try to get file size + struct __stat64 statbuf {}; + if (_fstat64(backing_file, &statbuf) < 0) { + _close(backing_file); + throw std::system_error{errno, std::generic_category(), + "unable to obtain length of backing file '"s + path + "'"s}; + } + + // Check that it matches range length + if (static_cast(statbuf.st_size) != length) { + _close(backing_file); + throw std::invalid_argument{"backing file '"s + path + "' size ("s + + std::to_string(static_cast(statbuf.st_size)) + ") does not match range length ("s + + std::to_string(length) + ")"s}; + } + + // Try to map backing file to host memory + DWORD flProtect = shared ? PAGE_READWRITE : PAGE_READONLY; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + HANDLE hFile = reinterpret_cast(_get_osfhandle(backing_file)); + HANDLE hFileMappingObject = CreateFileMapping(hFile, NULL, flProtect, length >> 32, length & 0xffffffff, NULL); + if (!hFileMappingObject) { + _close(backing_file); + throw std::system_error{errno, std::generic_category(), + "could not map backing file '"s + path + "' to memory"s}; + } + + DWORD dwDesiredAccess = shared ? FILE_MAP_WRITE : FILE_MAP_COPY; + auto *host_memory = static_cast(MapViewOfFile(hFileMappingObject, dwDesiredAccess, 0, 0, length)); + if (!host_memory) { + _close(backing_file); + throw std::system_error{errno, std::generic_category(), + "could not map backing file '"s + path + "' to memory"s}; + } + + // We can close the file after mapping it, because the OS will retain a reference of the file on its own + _close(backing_file); + return host_memory; + */ + +#else +#error "NYI" + /* + if (shared) { + throw std::runtime_error{"shared backing file mapping is unsupported"s}; + } + + auto fp = unique_fopen(path, "rb", std::nothrow_t{}); + if (!fp) { + throw std::system_error{errno, std::generic_category(), "error opening backing file '"s + path + "'"s}; + } + // Get file size + if (fseek(fp.get(), 0, SEEK_END)) { + throw std::system_error{errno, std::generic_category(), + "error obtaining length of backing file '"s + path + "'"s}; + } + auto backing_length = ftell(fp.get()); + if (fseek(fp.get(), 0, SEEK_SET)) { + throw std::system_error{errno, std::generic_category(), + "error obtaining length of backing file '"s + path + "'"s}; + } + // Check against PMA range size + if (static_cast(backing_length) > length) { + throw std::runtime_error{"backing file '"s + path + "' of "s + " is too large for range"s}; + } + + // use calloc to improve performance + // NOLINTNEXTLINE(cppcoreguidelines-no-malloc, cppcoreguidelines-prefer-member-initializer) + auto host_memory = static_cast(std::calloc(1, length)); + if (!host_memory) { + throw std::runtime_error{"error allocating memory"s}; + } + + // Read to host memory + auto read = fread(host_memory, 1, length, fp.get()); + (void) read; + if (ferror(fp.get())) { + throw std::system_error{errno, std::generic_category(), "error reading from backing file '"s + path + "'"s}; + } + return host_memory; + */ + +#endif // HAVE_MMAP +} + +void os_munmap(const os_mmapd &mmapd) { +#ifdef HAVE_MMAP + if (mmapd.host_memory != nullptr && mmapd.length > 0) { + munmap(mmapd.host_memory, mmapd.length); + } + if (mmapd.backing_fd != -1) { + close(mmapd.backing_fd); + } + +#elif defined(_WIN32) + if (mmapd.host_memory != nullptr) { + UnmapViewOfFile(mmapd.host_memory); + } + if (mmapd.backing_fd != -1) { + _close(mmapd.backing_fd); + } + +#else + if (mmapd.host_memory != nullptr) { + std::free(mmapd.host_memory); + } + +#endif +} + +} // namespace cartesi diff --git a/src/os-mmap.h b/src/os-mmap.h new file mode 100644 index 000000000..bb39e0266 --- /dev/null +++ b/src/os-mmap.h @@ -0,0 +1,49 @@ +// 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 OS_MMAP_H +#define OS_MMAP_H + +#include +#include + +namespace cartesi { + +enum os_mmap_flags { + OS_MMAP_SHARED = 1 << 0, ///< Share memory with the backing file + OS_MMAP_LOCK_BACKING = 1 << 1, ///< Lock backing file (for shared read or exclusive writing) + OS_MMAP_READONLY = 1 << 2, ///< Mark memory as read-only + OS_MMAP_NO_RESERVE = 1 << 3, ///< Do not reserve swap space, allowing to map large address space +}; + +struct os_mmapd { + unsigned char *host_memory{}; + uint64_t length{}; + int flags{}; + int backing_fd{-1}; + uint64_t backing_length{}; + std::string backing_path{}; +}; + +/// \brief Maps OS memory +os_mmapd os_mmap(uint64_t length, int flags = 0, const std::string &backing_path = ""); + +/// \brief Unmaps OS memory +void os_munmap(const os_mmapd &mmapd); + +} // namespace cartesi + +#endif diff --git a/src/os.cpp b/src/os.cpp index cb1a48750..dfb1ab41a 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -57,6 +57,10 @@ #include // fstat/mkdir #endif +#if defined(HAVE_FLOCK) +#include // flock +#endif + #ifdef _WIN32 #ifndef WIN32_LEAN_AND_MEAN @@ -516,158 +520,15 @@ void os_putchars(const uint8_t *data, size_t len) { } } -int os_mkdir(const char *path, int mode) { +void os_mkdir(const char *path, int mode) { #ifdef HAVE_MKDIR - return plat_mkdir(path, mode); -#else - return -1; -#endif // HAVE_MKDIR -} - -unsigned char *os_map_file(const char *path, uint64_t length, bool shared) { - if (!path || *path == '\0') { - throw std::runtime_error{"image file path must be specified"s}; - } - -#ifdef HAVE_MMAP - const int oflag = shared ? O_RDWR : O_RDONLY; - - // Try to open image file - const int backing_file = open(path, oflag); - if (backing_file < 0) { - throw std::system_error{errno, std::generic_category(), "could not open image file '"s + path + "'"s}; - } - - // Try to get file size - struct stat statbuf {}; - if (fstat(backing_file, &statbuf) < 0) { - close(backing_file); + if (plat_mkdir(path, mode) != 0) { throw std::system_error{errno, std::generic_category(), - "unable to obtain length of image file '"s + path + "'"s}; - } - - // Check that it matches range length - if (static_cast(statbuf.st_size) != length) { - close(backing_file); - throw std::invalid_argument{"image file '"s + path + "' size ("s + - std::to_string(static_cast(statbuf.st_size)) + ") does not match range length ("s + - std::to_string(length) + ")"s}; - } - - // Try to map image file to host memory - const int mflag = shared ? MAP_SHARED : MAP_PRIVATE; - auto *host_memory = - static_cast(mmap(nullptr, length, PROT_READ | PROT_WRITE, mflag, backing_file, 0)); - if (host_memory == MAP_FAILED) { // NOLINT(cppcoreguidelines-pro-type-cstyle-cast,performance-no-int-to-ptr) - close(backing_file); - throw std::system_error{errno, std::generic_category(), "could not map image file '"s + path + "' to memory"s}; - } - - // We can close the file after mapping it, because the OS will retain a reference of the file on its own - close(backing_file); - return host_memory; - -#elif defined(_WIN32) - const int oflag = (shared ? _O_RDWR : _O_RDONLY) | _O_BINARY; - - // Try to open image file - const int backing_file = _open(path, oflag); - if (backing_file < 0) { - throw std::system_error{errno, std::generic_category(), "could not open image file '"s + path + "'"s}; - } - - // Try to get file size - struct __stat64 statbuf {}; - if (_fstat64(backing_file, &statbuf) < 0) { - _close(backing_file); - throw std::system_error{errno, std::generic_category(), - "unable to obtain length of image file '"s + path + "'"s}; - } - - // Check that it matches range length - if (static_cast(statbuf.st_size) != length) { - _close(backing_file); - throw std::invalid_argument{"image file '"s + path + "' size ("s + - std::to_string(static_cast(statbuf.st_size)) + ") does not match range length ("s + - std::to_string(length) + ")"s}; - } - - // Try to map image file to host memory - DWORD flProtect = shared ? PAGE_READWRITE : PAGE_READONLY; - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - HANDLE hFile = reinterpret_cast(_get_osfhandle(backing_file)); - HANDLE hFileMappingObject = CreateFileMapping(hFile, NULL, flProtect, length >> 32, length & 0xffffffff, NULL); - if (!hFileMappingObject) { - _close(backing_file); - throw std::system_error{errno, std::generic_category(), "could not map image file '"s + path + "' to memory"s}; + "error creating directory '"s + std::string(path) + "'"s}; } - - DWORD dwDesiredAccess = shared ? FILE_MAP_WRITE : FILE_MAP_COPY; - auto *host_memory = static_cast(MapViewOfFile(hFileMappingObject, dwDesiredAccess, 0, 0, length)); - if (!host_memory) { - _close(backing_file); - throw std::system_error{errno, std::generic_category(), "could not map image file '"s + path + "' to memory"s}; - } - - // We can close the file after mapping it, because the OS will retain a reference of the file on its own - _close(backing_file); - return host_memory; - #else - if (shared) { - throw std::runtime_error{"shared image file mapping is unsupported"s}; - } - - auto fp = unique_fopen(path, "rb", std::nothrow_t{}); - if (!fp) { - throw std::system_error{errno, std::generic_category(), "error opening image file '"s + path + "'"s}; - } - // Get file size - if (fseek(fp.get(), 0, SEEK_END)) { - throw std::system_error{errno, std::generic_category(), - "error obtaining length of image file '"s + path + "'"s}; - } - auto file_length = ftell(fp.get()); - if (fseek(fp.get(), 0, SEEK_SET)) { - throw std::system_error{errno, std::generic_category(), - "error obtaining length of image file '"s + path + "'"s}; - } - // Check against PMA range size - if (static_cast(file_length) > length) { - throw std::runtime_error{"image file '"s + path + "' of "s + " is too large for range"s}; - } - - // use calloc to improve performance - // NOLINTNEXTLINE(cppcoreguidelines-no-malloc, cppcoreguidelines-prefer-member-initializer) - auto host_memory = static_cast(std::calloc(1, length)); - if (!host_memory) { - throw std::runtime_error{"error allocating memory"s}; - } - - // Read to host memory - auto read = fread(host_memory, 1, length, fp.get()); - (void) read; - if (ferror(fp.get())) { - throw std::system_error{errno, std::generic_category(), "error reading from image file '"s + path + "'"s}; - } - return host_memory; - -#endif // HAVE_MMAP -} - -void os_unmap_file(unsigned char *host_memory, uint64_t length) { -#ifdef HAVE_MMAP - munmap(host_memory, length); - -#elif defined(_WIN32) - (void) length; - UnmapViewOfFile(host_memory); - -#else - (void) length; - std::free(host_memory); - -#endif // HAVE_MMAP + throw std::runtime_error("mkdir() is not supported"); +#endif // HAVE_MKDIR } int64_t os_now_us() { @@ -786,4 +647,172 @@ void os_sleep_us(uint64_t timeout_us) { Sleep(timeout_us / 1000); #endif } + +void os_copy_file(const char *dest_path, const char *src_path) { + // TODO(edubart): copy read-only files with hardlinks ? + // TODO(edubart): copy using COW + +#ifdef HAVE_POSIX_FS + int src_fd = -1; + int dest_fd = -1; + try { + // Open source file + src_fd = open(src_path, O_RDONLY); + if (src_fd < 0) { + throw std::system_error{errno, std::generic_category(), + "unable to open file '"s + src_path + "' for read"s}; + } +#ifdef HAVE_FLOCK + // Lock source file + if (flock(src_fd, LOCK_SH | LOCK_NB) < 0) { + throw std::system_error{errno, std::generic_category(), + "unable to lock file '"s + src_path + "' for read"s}; + } +#endif + // Get source file length + struct stat src_statbuf {}; + if (fstat(src_fd, &src_statbuf) < 0) { + throw std::system_error{errno, std::generic_category(), + "unable to obtain length of file '"s + src_path + "'"s}; + } + const uint64_t src_length = static_cast(src_statbuf.st_size); + + // Open destination file + const int mode = (S_IRUSR | S_IWUSR) | S_IRGRP | S_IROTH; + dest_fd = open(dest_path, O_RDWR | O_CREAT | O_EXCL, mode); + if (dest_fd < 0) { + throw std::system_error{errno, std::generic_category(), + "unable to open file '"s + dest_path + "' for write"s}; + } +#ifdef HAVE_FLOCK + // Lock destination file + if (flock(dest_fd, LOCK_SH | LOCK_NB) < 0) { + throw std::system_error{errno, std::generic_category(), + "unable to lock file '"s + dest_path + "' for write"s}; + } +#endif + // Truncate destination file to a sparse file + if (ftruncate(dest_fd, static_cast(src_length)) < 0) { + throw std::system_error{errno, std::generic_category(), "unable to truncate file '"s + dest_path + "'"s}; + } + + // Copy in chunks of 4096 bytes + uint8_t buf[4096]; + for (uint64_t off = 0; off < src_length;) { + const size_t len = std::min(static_cast(src_length - off), sizeof(buf)); + const ssize_t read_len = pread(src_fd, buf, len, static_cast(off)); + if (read_len < 0) { + throw std::system_error{errno, std::generic_category(), "unable to read file '"s + src_path + "'"s}; + } else if (static_cast(read_len) != len) { + throw std::runtime_error{"unable to read file '"s + src_path + "'"s}; + } + // Check if is all non zeros + bool only_zeros = true; + for (size_t i = 0; i < len; ++i) { + if (buf[i] != 0) { + only_zeros = false; + break; + } + } + // Write only non zeros chunks (to keep file sparse) + if (!only_zeros) { + const ssize_t written_len = pwrite(dest_fd, buf, len, static_cast(off)); + if (written_len < 0) { + throw std::system_error{errno, std::generic_category(), + "unable to write file '"s + dest_path + "'"s}; + } else if (static_cast(read_len) != len) { + throw std::runtime_error{"unable to write file '"s + dest_path + "'"s}; + } + } + off += len; + } + + // Close source file + if (close(src_fd) < 0) { + throw std::system_error{errno, std::generic_category(), "unable to close file '"s + src_path + "'"s}; + } + src_fd = -1; + + // Close destination file + if (close(dest_fd) < 0) { + throw std::system_error{errno, std::generic_category(), "unable to close file '"s + dest_path + "'"s}; + } + dest_fd = -1; + } catch (std::exception &e) { + if (src_fd != -1) { + close(src_fd); + } + if (dest_fd != -1) { + close(dest_fd); + unlink(dest_path); // revert file creation + } + throw; + } + +#else + // TODO(edubart): remove file on failure + auto src_fp = unique_fopen(src_path, "rb"); + auto dest_fp = unique_fopen(dest_path, "wb"); + + char buf[BUFSIZ]; + while (true) { + const size_t size = fread(buf, 1, BUFSIZ, src_fp.get()); + if (size == 0) { + if (feof(src_fp.get()) != 0) { // end of file + break; + } else { + throw std::system_error{errno, std::generic_category(), + "could not read file '"s + std::string(src_path) + "'"s}; + } + } + const size_t written = fwrite(buf, 1, size, dest_fp.get()); + if (written != size) { + throw std::system_error{errno, std::generic_category(), + "could not write file '"s + std::string(dest_path) + "'"s}; + } + } + +#endif +} + +void os_grow_file(const char *path, uint64_t length, bool create) { + // TODO(edubart): fallback implementation + + int fd = -1; + try { + int oflags = O_RDWR; + int omode = 0; + if (create) { + oflags |= O_CREAT | O_EXCL; + omode = (S_IRUSR | S_IWUSR) | S_IRGRP | S_IROTH; + } + fd = open(path, oflags, omode); + if (fd < 0) { + throw std::system_error{errno, std::generic_category(), "unable to create file '"s + path + "'"s}; + } + struct stat statbuf {}; + if (fstat(fd, &statbuf) < 0) { + throw std::system_error{errno, std::generic_category(), "unable to obtain length of file '"s + path + "'"s}; + } + const uint64_t file_length = static_cast(statbuf.st_size); + if (length < file_length) { + throw std::system_error{errno, std::generic_category(), "shrinking file '"s + path + "' is not allowed"s}; + } + if (ftruncate(fd, static_cast(length)) < 0) { + throw std::system_error{errno, std::generic_category(), "unable to truncate file '"s + path + "'"s}; + } + if (close(fd) < 0) { + throw std::system_error{errno, std::generic_category(), "unable to close file '"s + path + "'"s}; + } + } catch (std::exception &e) { + if (fd != -1) { + close(fd); + if (create) { // revert file creation + unlink(path); + } + } + throw; + } +} + } // namespace cartesi diff --git a/src/os.h b/src/os.h index c8178e525..e42d30335 100644 --- a/src/os.h +++ b/src/os.h @@ -86,13 +86,7 @@ void os_putchar(uint8_t ch); void os_putchars(const uint8_t *data, size_t len); /// \brief Creates a new directory -int os_mkdir(const char *path, int mode); - -/// \brief Maps a file to memory -unsigned char *os_map_file(const char *path, uint64_t length, bool shared); - -/// \brief Unmaps a file from memory -void os_unmap_file(unsigned char *host_memory, uint64_t length); +void os_mkdir(const char *path, int mode); /// \brief Get time elapsed since its first call with microsecond precision int64_t os_now_us(); @@ -147,6 +141,9 @@ void os_disable_sigpipe(); /// \brief Sleep until timeout_us microseconds elapsed void os_sleep_us(uint64_t timeout_us); +void os_copy_file(const char *dest_path, const char *src_path); +void os_grow_file(const char *path, uint64_t length, bool create); + } // namespace cartesi #endif diff --git a/src/pma.cpp b/src/pma.cpp index 2a0481f19..d27669040 100644 --- a/src/pma.cpp +++ b/src/pma.cpp @@ -21,6 +21,7 @@ #include #include +#include "machine-config.h" #include "os.h" #include "unique-c-ptr.h" @@ -29,91 +30,29 @@ namespace cartesi { using namespace std::string_literals; void pma_memory::release(void) { - if (m_mmapped) { - os_unmap_file(m_host_memory, m_length); - m_mmapped = false; - } else { - std::free(m_host_memory); // NOLINT(cppcoreguidelines-no-malloc) - } - m_host_memory = nullptr; - m_length = 0; + os_munmap(m_mmaped); + m_mmaped = os_mmapd{}; } pma_memory::~pma_memory() { release(); } -pma_memory::pma_memory(pma_memory &&other) noexcept : - m_length{std::move(other.m_length)}, - m_host_memory{std::move(other.m_host_memory)}, - m_mmapped{std::move(other.m_mmapped)} { +pma_memory::pma_memory(pma_memory &&other) noexcept : m_mmaped{std::move(other.m_mmaped)} { // set other to safe state - other.m_host_memory = nullptr; - other.m_mmapped = false; - other.m_length = 0; -} - -pma_memory::pma_memory(const std::string &description, uint64_t length, const callocd &c) : - m_length{length}, - m_host_memory{nullptr}, - m_mmapped{false} { - (void) c; - // use calloc to improve performance - // NOLINTNEXTLINE(cppcoreguidelines-no-malloc, cppcoreguidelines-prefer-member-initializer) - m_host_memory = static_cast(std::calloc(1, length)); - if (!m_host_memory) { - throw std::runtime_error{"error allocating memory for "s + description}; - } -} - -pma_memory::pma_memory(const std::string &description, uint64_t length, const mockd &m) : - m_length{length}, - m_host_memory{nullptr}, - m_mmapped{false} { - (void) m; - (void) description; + other.m_mmaped = os_mmapd{}; } -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 = 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)) { - throw std::system_error{errno, std::generic_category(), - "error obtaining length of image file '"s + path + "' when initializing "s + description}; - } - auto file_length = ftell(fp.get()); - if (fseek(fp.get(), 0, SEEK_SET)) { - 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 (static_cast(file_length) > length) { - throw std::runtime_error{"image file '"s + path + "' of "s + description + " is too large for range"s}; +pma_memory::pma_memory(const std::string &description, uint64_t length, const std::string &path, bool shared) { + try { + int flags = 0; + if (shared) { + flags |= OS_MMAP_SHARED; } - // Read to host memory - auto read = fread(m_host_memory, 1, length, fp.get()); - (void) read; - if (ferror(fp.get())) { - throw std::system_error{errno, std::generic_category(), - "error reading from image file '"s + path + "' when initializing "s + description}; + if (!path.empty()) { + flags |= OS_MMAP_LOCK_BACKING; } - } -} - -pma_memory::pma_memory(const std::string &description, uint64_t length, const std::string &path, const mmapd &m) : - m_length{length}, - m_host_memory{nullptr}, - m_mmapped{false} { - try { - m_host_memory = os_map_file(path.c_str(), length, m.shared); - m_mmapped = true; + m_mmaped = os_mmap(length, flags, path); } catch (std::exception &e) { throw std::runtime_error{e.what() + " when initializing "s + description}; } @@ -122,13 +61,9 @@ pma_memory::pma_memory(const std::string &description, uint64_t length, const st pma_memory &pma_memory::operator=(pma_memory &&other) noexcept { release(); // copy from other - m_host_memory = std::move(other.m_host_memory); - m_mmapped = std::move(other.m_mmapped); - m_length = std::move(other.m_length); + m_mmaped = std::move(other.m_mmaped); // set other to safe state - other.m_host_memory = nullptr; - other.m_mmapped = false; - other.m_length = 0; + other.m_mmaped = os_mmapd{}; return *this; } @@ -206,31 +141,29 @@ pma_entry make_mmapd_memory_pma_entry(const std::string &description, uint64_t s if (length == 0) { throw std::invalid_argument{description + " length cannot be zero"s}; } - return pma_entry{description, start, length, pma_memory{description, length, path, pma_memory::mmapd{shared}}, - memory_peek}; -} - -pma_entry make_callocd_memory_pma_entry(const std::string &description, uint64_t start, uint64_t length) { - if (length == 0) { - throw std::invalid_argument{description + " length cannot be zero"s}; - } - return pma_entry{description, start, length, pma_memory{description, length, pma_memory::callocd{}}, memory_peek}; -} - -pma_entry make_callocd_memory_pma_entry(const std::string &description, uint64_t start, uint64_t length, - const std::string &path) { - if (length == 0) { - throw std::invalid_argument{description + " length cannot be zero"s}; - } - return pma_entry{description, start, length, pma_memory{description, length, path, pma_memory::callocd{}}, - memory_peek}; -} - -pma_entry make_mockd_memory_pma_entry(const std::string &description, uint64_t start, uint64_t length) { - if (length == 0) { - throw std::invalid_argument{description + " length cannot be zero"s}; + return pma_entry{description, start, length, pma_memory{description, length, path, shared}, memory_peek}; +} + +pma_entry make_memory_range_pma_entry(const std::string &description, pma_entry::flags flags, uint64_t start, + uint64_t length, const std::string &image_filename, bool shared, const machine_runtime_config &r) { + std::string backing_filename = image_filename; + if (!r.backing_storage.empty()) { + backing_filename = machine_config::get_image_filename(r.backing_storage, start, length); + if (!image_filename.empty()) { + if (shared && backing_filename != image_filename) { + throw std::runtime_error( + "PMA "s + description + " cannot be shared simultaneously with backing storage runtime option"s); + } + if (backing_filename != image_filename) { + os_copy_file(backing_filename.c_str(), image_filename.c_str()); + os_grow_file(backing_filename.c_str(), length, false); + } + } else { + os_grow_file(backing_filename.c_str(), length, true); + } + shared = true; } - return pma_entry{description, start, length, pma_memory{description, length, pma_memory::mockd{}}, pma_peek_error}; + return make_mmapd_memory_pma_entry(description, start, length, backing_filename, shared).set_flags(flags); } pma_entry make_device_pma_entry(const std::string &description, uint64_t start, uint64_t length, pma_peek peek, diff --git a/src/pma.h b/src/pma.h index 4fe71d210..90a5a287c 100644 --- a/src/pma.h +++ b/src/pma.h @@ -24,6 +24,8 @@ #include #include +#include "machine-runtime-config.h" +#include "os-mmap.h" #include "pma-constants.h" #include "pma-driver.h" @@ -91,51 +93,18 @@ class pma_device final { /// \brief Data for memory ranges. class pma_memory final { - - uint64_t m_length; ///< Length of memory range (copy of PMA length field). - unsigned char *m_host_memory; ///< Start of associated memory region in host. - bool m_mmapped; ///< True if memory was mapped from a file. + os_mmapd m_mmaped; ///< Memory map entry /// \brief Close file and/or release memory. void release(void); public: - /// \brief Mmap'd range data (shared or not). - struct mmapd { - bool shared; - }; - /// \brief Constructor for mmap'd ranges. /// \param description Informative description of PMA entry for use in error messages /// \param length Length of range. /// \param path Path for backing file. /// \param m Mmap'd range data (shared or not). - pma_memory(const std::string &description, uint64_t length, const std::string &path, const mmapd &m); - - /// \brief Calloc'd range data (just a tag). - struct callocd {}; - - /// \brief Mock'd range data (just a tag). - struct mockd {}; - - /// \brief Constructor for calloc'd ranges. - /// \param description Informative description of PMA entry for use in error messages - /// \param length Length of range. - /// \param path Path for backing file. - /// \param c Calloc'd range data (just a tag). - pma_memory(const std::string &description, uint64_t length, const std::string &path, const callocd &c); - - /// \brief Constructor for calloc'd ranges. - /// \param description Informative description of PMA entry for use in error messages - /// \param length Length of range. - /// \param c Calloc'd range data (just a tag). - pma_memory(const std::string &description, uint64_t length, const callocd &c); - - /// \brief Constructor for mock ranges. - /// \param description Informative description of PMA entry for use in error messages - /// \param length Length of range. - /// \param m Mock'd range data (just a tag). - pma_memory(const std::string &description, uint64_t length, const mockd &m); + pma_memory(const std::string &description, uint64_t length, const std::string &path = "", bool shared = false); /// \brief No copy constructor pma_memory(const pma_memory &) = delete; @@ -153,18 +122,13 @@ class pma_memory final { ~pma_memory(void); /// \brief Returns start of associated memory region in host - unsigned char *get_host_memory(void) { - return m_host_memory; - } - - /// \brief Returns start of associated memory region in host - const unsigned char *get_host_memory(void) const { - return m_host_memory; + unsigned char *get_host_memory(void) const { + return m_mmaped.host_memory; } /// \brief Returns copy of PMA length field (needed for munmap). uint64_t get_length(void) const { - return m_length; + return m_mmaped.length; } }; @@ -511,22 +475,6 @@ class pma_entry final { void fill_memory(uint64_t paddr, unsigned char value, uint64_t size); }; -/// \brief Creates a PMA entry for a new memory range initially filled with zeros. -/// \param description Informative description of PMA entry for use in error messages -/// \param start Start of PMA range. -/// \param length Length of PMA range. -/// \returns Corresponding PMA entry -pma_entry make_callocd_memory_pma_entry(const std::string &description, uint64_t start, uint64_t length); - -/// \brief Creates a PMA entry for a new memory range initially filled with the contents of a backing file. -/// \param description Informative description of PMA entry for use in error messages -/// \param start Start of PMA range. -/// \param length Length of PMA range. -/// \param path Path to backing file. -/// \returns Corresponding PMA entry -pma_entry make_callocd_memory_pma_entry(const std::string &description, uint64_t start, uint64_t length, - const std::string &path); - /// \brief Creates a PMA entry for a new memory region using the host's /// mmap functionality. /// \param description Informative description of PMA entry for use in error messages @@ -542,16 +490,14 @@ pma_entry make_callocd_memory_pma_entry(const std::string &description, uint64_t /// \details \p length must match the size of the backing file. /// This function is typically used to map flash drives. pma_entry make_mmapd_memory_pma_entry(const std::string &description, uint64_t start, uint64_t length, - const std::string &path, bool shared); + const std::string &path = "", bool shared = false); -/// \brief Creates a PMA entry for a new mock memory region (no allocation). +/// \brief Creates a new PMA entry reflecting a memory range configuration. /// \param description Informative description of PMA entry for use in error messages -/// \param start Start of physical memory range in the target address -/// space on which to map the memory region. -/// \param length Length of physical memory range in the -/// target address space on which to map the memory region. -/// \returns Corresponding PMA entry -pma_entry make_mockd_memory_pma_entry(const std::string &description, uint64_t start, uint64_t length); +/// \param c Memory range configuration. +/// \returns New PMA entry (with default flags). +pma_entry make_memory_range_pma_entry(const std::string &description, pma_entry::flags flags, uint64_t start, + uint64_t length, const std::string &image_filename, bool shared, const machine_runtime_config &r); /// \brief Creates a PMA entry for a new memory-mapped IO device. /// \param description Informative description of PMA entry for use in error messages diff --git a/src/uarch-machine.cpp b/src/uarch-machine.cpp index c6c4e13e5..94cceaf68 100644 --- a/src/uarch-machine.cpp +++ b/src/uarch-machine.cpp @@ -34,7 +34,7 @@ const pma_entry::flags ram_flags{ PMA_ISTART_DID::memory // DID }; -uarch_machine::uarch_machine(uarch_config c) : m_s{}, m_c{c} { +uarch_machine::uarch_machine(const uarch_config &c) : m_s{}, m_c{c} { m_s.pc = c.processor.pc; m_s.cycle = c.processor.cycle; m_s.halt_flag = c.processor.halt_flag; @@ -42,19 +42,16 @@ uarch_machine::uarch_machine(uarch_config c) : m_s{}, m_c{c} { for (int i = 1; i < UARCH_X_REG_COUNT; i++) { m_s.x[i] = c.processor.x[i]; } +} + +void uarch_machine::register_pmas(const machine_runtime_config &r) { // Register shadow state m_s.shadow_state = make_shadow_uarch_state_pma_entry(PMA_SHADOW_UARCH_STATE_START, PMA_SHADOW_UARCH_STATE_LENGTH); - // Register RAM - constexpr auto ram_description = "uarch RAM"; - if (!c.ram.image_filename.empty()) { - // Load RAM image from file - m_s.ram = - make_callocd_memory_pma_entry(ram_description, PMA_UARCH_RAM_START, UARCH_RAM_LENGTH, c.ram.image_filename) - .set_flags(ram_flags); - } else { - // Load embedded pristine RAM image - m_s.ram = make_callocd_memory_pma_entry(ram_description, PMA_UARCH_RAM_START, PMA_UARCH_RAM_LENGTH) - .set_flags(ram_flags); + // Register RAM image from file + m_s.ram = make_memory_range_pma_entry("uarch RAM", ram_flags, PMA_UARCH_RAM_START, PMA_UARCH_RAM_LENGTH, + m_c.ram.image_filename, false, r); + // Load embedded pristine RAM image + if (m_c.ram.image_filename.empty()) { if (uarch_pristine_ram_len > m_s.ram.get_length()) { throw std::runtime_error("embedded uarch ram image does not fit in uarch ram pma"); } diff --git a/src/uarch-machine.h b/src/uarch-machine.h index 4de0f19fe..373938034 100644 --- a/src/uarch-machine.h +++ b/src/uarch-machine.h @@ -20,6 +20,7 @@ /// \file /// \brief Cartesi microarchitecture machine +#include "machine-runtime-config.h" #include "uarch-config.h" #include "uarch-state.h" @@ -34,7 +35,7 @@ class uarch_machine final { public: /// \brief Constructor from machine configuration // I will deal with clang-tidy later. - explicit uarch_machine(uarch_config c); + explicit uarch_machine(const uarch_config &c); /// \brief Destructor. ~uarch_machine() = default; @@ -59,6 +60,8 @@ class uarch_machine final { return m_s; } + void register_pmas(const machine_runtime_config &r); + /// \brief Obtain PMA entry that covers a given physical memory region /// \param s Pointer to machine state. /// \param paddr Start of physical memory region. diff --git a/tests/lua/create-machines.lua b/tests/lua/create-machines.lua index 58c155193..e67d7c2c1 100755 --- a/tests/lua/create-machines.lua +++ b/tests/lua/create-machines.lua @@ -211,11 +211,3 @@ chmod +x /home/dapp/s.sh; rollup-init bash /home/dapp/s.sh ]] ) - --- Should not work with shared buffers -create_machine("shared-rx-buffer-machine", "rollup accept", function(config) - config.cmio.rx_buffer.shared = true -end) -create_machine("shared-tx-buffer-machine", "rollup accept", function(config) - config.cmio.tx_buffer.shared = true -end) diff --git a/tests/misc/test-machine-c-api.cpp b/tests/misc/test-machine-c-api.cpp index 468650066..1cbb01490 100644 --- a/tests/misc/test-machine-c-api.cpp +++ b/tests/misc/test-machine-c-api.cpp @@ -147,9 +147,9 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(create_machine_null_machine_test, incomplete_mach BOOST_FIXTURE_TEST_CASE_NOLINT(replace_memory_range_pma_overlapping_test, incomplete_machine_fixture) { _machine_config["flash_drive"] = nlohmann::json{nlohmann::json{nlohmann::json{"start", 0x80000000000000}, nlohmann::json{"length", 0x3c00000}, - nlohmann::json{"shared", true}}, + nlohmann::json{"shared", false}}, nlohmann::json{nlohmann::json{"start", 0x7ffffffffff000}, nlohmann::json{"length", 0x2000}, - nlohmann::json{"shared", true}}}; + nlohmann::json{"shared", false}}}; cm_error error_code = cm_create(_machine_config.dump().c_str(), nullptr, &_machine); BOOST_CHECK_EQUAL(error_code, CM_ERROR_INVALID_ARGUMENT); @@ -161,8 +161,9 @@ BOOST_FIXTURE_TEST_CASE_NOLINT(replace_memory_range_pma_overlapping_test, incomp class machine_flash_simple_fixture : public incomplete_machine_fixture { public: machine_flash_simple_fixture() { - _machine_config["flash_drive"] = nlohmann::json{nlohmann::json{nlohmann::json{"start", 0x80000000000000}, - nlohmann::json{"length", 0x3c00000}, nlohmann::json{"shared", true}, nlohmann::json{"image_filename", ""}}}; + _machine_config["flash_drive"] = nlohmann::json{ + nlohmann::json{nlohmann::json{"start", 0x80000000000000}, nlohmann::json{"length", 0x3c00000}, + nlohmann::json{"shared", false}, nlohmann::json{"image_filename", ""}}}; } machine_flash_simple_fixture(const machine_flash_simple_fixture &other) = delete;