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/machine.cpp b/src/machine.cpp index fa184d2ad..4171000ae 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -109,9 +109,6 @@ const pma_entry::flags machine::m_cmio_tx_buffer_flags{ }; 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); } @@ -120,24 +117,14 @@ pma_entry machine::make_flash_drive_pma_entry(const std::string &description, co } 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) + return make_mmapd_memory_pma_entry("cmio rx buffer memory range"s, 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); } 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) + return make_mmapd_memory_pma_entry("cmio tx buffer memory range"s, 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); } @@ -323,18 +310,13 @@ machine::machine(const machine_config &c, const machine_runtime_config &r) : 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)); - } + register_pma_entry(make_mmapd_memory_pma_entry("RAM"s, PMA_RAM_START, m_c.ram.length, m_c.ram.image_filename) + .set_flags(m_ram_flags)); // 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_mmapd_memory_pma_entry("DTB"s, PMA_DTB_START, PMA_DTB_LENGTH, m_c.dtb.image_filename)) + .set_flags(m_dtb_flags); // Register all flash drives int i = 0; @@ -485,7 +467,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); 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..772731133 --- /dev/null +++ b/src/os-mmap.cpp @@ -0,0 +1,306 @@ +// 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_FILE | OS_MMAP_SHARED | OS_MMAP_SHARED_TRUNCATE | 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; + int omode = 0; + if (flags & OS_MMAP_SHARED_TRUNCATE) { + // In case of truncate is specified we actually want to create a new file + oflags = O_CREAT | O_EXCL | O_RDWR; + omode |= (S_IRUSR | S_IWUSR) | S_IRGRP | S_IROTH; + } + + // Try to open backing file + backing_fd = open(backing_path.c_str(), oflags, omode); + if (backing_fd < 0) { + throw std::system_error{errno, std::generic_category(), + "could not open backing file '"s + backing_path + "'"s}; + } + + // Truncate a new file with desired length if needed + if (flags & OS_MMAP_SHARED_TRUNCATE) { + if (ftruncate(backing_fd, static_cast(length)) < 0) { + throw std::system_error{errno, std::generic_category(), + "could not lock backing file '"s + backing_path + "'"s}; + } + backing_length = length; + } else { // 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_FILE) { + 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); + // Remove created backing file + if (flags & OS_MMAP_SHARED_TRUNCATE) { + unlink(backing_path.c_str()); + } + } + // 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..5cc1fbc07 --- /dev/null +++ b/src/os-mmap.h @@ -0,0 +1,50 @@ +// 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_SHARED_TRUNCATE = 1 << 1, ///< Create a sparse backing file truncated with zeros + OS_MMAP_READONLY = 1 << 2, ///< Mark memory as read-only + OS_MMAP_LOCK_BACKING_FILE = 1 << 3, ///< Lock backing file for read (in case of READONLY) or exclusive writing + OS_MMAP_NO_RESERVE = 1 << 4, ///< 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..175bd28cc 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -524,152 +524,6 @@ int os_mkdir(const char *path, int mode) { #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); - 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}; - } - - 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 -} - int64_t os_now_us() { std::chrono::time_point start{}; static bool started = false; diff --git a/src/os.h b/src/os.h index c8178e525..124c80ace 100644 --- a/src/os.h +++ b/src/os.h @@ -88,12 +88,6 @@ 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); - /// \brief Get time elapsed since its first call with microsecond precision int64_t os_now_us(); diff --git a/src/pma.cpp b/src/pma.cpp index 2a0481f19..25f40afb6 100644 --- a/src/pma.cpp +++ b/src/pma.cpp @@ -21,7 +21,6 @@ #include #include -#include "os.h" #include "unique-c-ptr.h" namespace cartesi { @@ -29,91 +28,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_FILE; } - } -} - -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 +59,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 +139,7 @@ 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, pma_memory::mockd{}}, pma_peek_error}; + return pma_entry{description, start, length, pma_memory{description, length, path, shared}, memory_peek}; } 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..772dfa7f4 100644 --- a/src/pma.h +++ b/src/pma.h @@ -24,6 +24,7 @@ #include #include +#include "os-mmap.h" #include "pma-constants.h" #include "pma-driver.h" @@ -91,51 +92,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 +121,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 +474,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 +489,7 @@ 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); - -/// \brief Creates a PMA entry for a new mock memory region (no allocation). -/// \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); + const std::string &path = "", bool shared = false); /// \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..a06642a3a 100644 --- a/src/uarch-machine.cpp +++ b/src/uarch-machine.cpp @@ -44,17 +44,11 @@ uarch_machine::uarch_machine(uarch_config c) : m_s{}, m_c{c} { } // 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_mmapd_memory_pma_entry("uarch RAM", PMA_UARCH_RAM_START, PMA_UARCH_RAM_LENGTH, c.ram.image_filename) + .set_flags(ram_flags); + // Load embedded pristine RAM image + if (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/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;