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/compiler-defines.h b/src/compiler-defines.h
index ff1adfc53..0b160d7c2 100644
--- a/src/compiler-defines.h
+++ b/src/compiler-defines.h
@@ -27,6 +27,12 @@
#define NO_INLINE __attribute__((noinline))
+#if defined(__GNUC__)
+#define FORCE_OPTIMIZE_O3 __attribute__((optimize("-O3")))
+#else
+#define FORCE_OPTIMIZE_O3
+#endif
+
#define NO_RETURN [[noreturn]]
// These macros are used only in very hot code paths (such as TLB hit checks).
diff --git a/src/is-pristine.h b/src/is-pristine.h
new file mode 100644
index 000000000..f8ecdd3fd
--- /dev/null
+++ b/src/is-pristine.h
@@ -0,0 +1,37 @@
+// 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 IS_PRISTINE_H
+#define IS_PRISTINE_H
+
+#include "compiler-defines.h"
+#include
+#include
+
+namespace cartesi {
+
+// NOLINTNEXTLINE(clang-diagnostic-unknown-attributes)
+static inline bool FORCE_OPTIMIZE_O3 is_pristine(const unsigned char *data, size_t length) {
+ unsigned char bits = 0;
+ for (size_t i = 0; i < length; ++i) {
+ bits |= data[i];
+ }
+ return bits == 0;
+}
+
+} // namespace cartesi
+
+#endif
diff --git a/src/json-util.cpp b/src/json-util.cpp
index 7e0ae792a..e2f82dadf 100644
--- a/src/json-util.cpp
+++ b/src/json-util.cpp
@@ -789,6 +789,8 @@ 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], "copy_reflink"s, value.copy_reflink, 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 +1973,8 @@ 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},
+ {"copy_reflink", runtime.copy_reflink},
+ {"backing_storage", runtime.backing_storage},
};
}
diff --git a/src/machine-config.cpp b/src/machine-config.cpp
index 4d89fe409..e6d8600a9 100644
--- a/src/machine-config.cpp
+++ b/src/machine-config.cpp
@@ -77,6 +77,7 @@ machine_config machine_config::load(const std::string &dir) {
}
ju_get_field(j, std::string("config"), c, "");
adjust_image_filenames(c, dir);
+ c.load_dir = dir;
} catch (std::exception &e) {
throw std::runtime_error{e.what()};
}
diff --git a/src/machine-config.h b/src/machine-config.h
index fc47eb1e7..363dd812d 100644
--- a/src/machine-config.h
+++ b/src/machine-config.h
@@ -211,6 +211,8 @@ struct machine_config final {
/// \brief Stores the machine config to a directory
/// \param dir Directory where "config" will be stored
void store(const std::string &dir) const;
+
+ std::string load_dir{}; ///< Directory this machine config was loaded from
};
} // namespace cartesi
diff --git a/src/machine-runtime-config.h b/src/machine-runtime-config.h
index 649ea4aaf..2d6abec53 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,8 @@ struct machine_runtime_config {
bool skip_root_hash_store{};
bool skip_version_check{};
bool soft_yield{};
+ bool copy_reflink{};
+ std::string backing_storage{};
};
/// \brief CONCURRENCY constants
diff --git a/src/machine.cpp b/src/machine.cpp
index fa184d2ad..644cbc1a6 100644
--- a/src/machine.cpp
+++ b/src/machine.cpp
@@ -27,6 +27,7 @@
#include "htif-factory.h"
#include "htif.h"
#include "interpret.h"
+#include "is-pristine.h"
#include "plic-factory.h"
#include "record-state-access.h"
#include "replay-state-access.h"
@@ -108,39 +109,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 +167,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;
}
}
@@ -250,6 +220,14 @@ static void init_tlb_entry(machine &m, uint64_t eidx) {
tlbce.pma_index = TLB_INVALID_PMA;
}
+static void load_hash(const std::string &dir, machine::hash_type &h) {
+ auto name = dir + "/hash";
+ auto fp = unique_fopen(name.c_str(), "rb");
+ if (fread(h.data(), 1, h.size(), fp.get()) != h.size()) {
+ throw std::runtime_error{"error reading from '" + name + "'"};
+ }
+}
+
machine::machine(const machine_config &c, const machine_runtime_config &r) :
m_s{},
m_t{},
@@ -322,19 +300,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() && m_r.backing_storage != m_c.load_dir) {
+ 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 +341,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));
@@ -396,7 +382,7 @@ machine::machine(const machine_config &c, const machine_runtime_config &r) :
write_reg(reg::plic_girqsrvd, m_c.plic.girqsrvd);
// Register TLB device
- register_pma_entry(make_shadow_tlb_pma_entry(PMA_SHADOW_TLB_START, PMA_SHADOW_TLB_LENGTH));
+ const pma_entry &tlb = register_pma_entry(make_shadow_tlb_pma_entry(PMA_SHADOW_TLB_START, PMA_SHADOW_TLB_LENGTH));
// Register state shadow device
register_pma_entry(make_shadow_state_pma_entry(PMA_SHADOW_STATE_START, PMA_SHADOW_STATE_LENGTH));
@@ -485,7 +471,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);
@@ -518,35 +504,35 @@ machine::machine(const machine_config &c, const machine_runtime_config &r) :
std::sort(m_mrds.begin(), m_mrds.end(),
[](const machine_memory_range_descr &a, const machine_memory_range_descr &b) { return a.start < b.start; });
+ // Check root hash
+ if (!m_c.load_dir.empty() && !r.skip_root_hash_check) {
+ hash_type hstored;
+ hash_type hrestored;
+ load_hash(m_c.load_dir, hstored);
+ if (!update_merkle_tree()) {
+ throw std::runtime_error{"error updating Merkle tree"};
+ }
+ m_t.get_root_hash(hrestored);
+ if (hstored != hrestored) {
+ throw std::runtime_error{"stored and restored hashes do not match"};
+ }
+ }
+
+ // Remove backing files that are not mapped to memory
+ if (!m_r.backing_storage.empty()) {
+ os_unlink(machine_config::get_image_filename(m_r.backing_storage, tlb.get_start(), tlb.get_length()).c_str(),
+ true);
+ os_unlink((m_r.backing_storage + "/config.json"s).c_str(), true);
+ os_unlink((m_r.backing_storage + "/hash"s).c_str(), true);
+ }
+
// Disable SIGPIPE handler, because this signal can be raised and terminate the emulator process
// when calling write() on closed file descriptors.
// This can happen with the stdout console file descriptors or network file descriptors.
os_disable_sigpipe();
}
-static void load_hash(const std::string &dir, machine::hash_type &h) {
- auto name = dir + "/hash";
- auto fp = unique_fopen(name.c_str(), "rb");
- if (fread(h.data(), 1, h.size(), fp.get()) != h.size()) {
- throw std::runtime_error{"error reading from '" + name + "'"};
- }
-}
-
-machine::machine(const std::string &dir, const machine_runtime_config &r) : machine{machine_config::load(dir), r} {
- if (r.skip_root_hash_check) {
- return;
- }
- hash_type hstored;
- hash_type hrestored;
- load_hash(dir, hstored);
- if (!update_merkle_tree()) {
- throw std::runtime_error{"error updating Merkle tree"};
- }
- m_t.get_root_hash(hrestored);
- if (hstored != hrestored) {
- throw std::runtime_error{"stored and restored hashes do not match"};
- }
-}
+machine::machine(const std::string &dir, const machine_runtime_config &r) : machine{machine_config::load(dir), r} {}
void machine::prepare_virtio_devices_select(select_fd_sets *fds, uint64_t *timeout_us) {
for (auto &vdev : m_vdevs) {
@@ -687,15 +673,23 @@ static void store_device_pma(const machine &m, const pma_entry &pma, const std::
}
}
-static void store_memory_pma(const pma_entry &pma, const std::string &dir) {
+static void store_memory_pma(const pma_entry &pma, const std::string &dir, const machine_runtime_config &r) {
if (!pma.get_istart_M()) {
throw std::runtime_error{"attempt to save non-memory PMA"};
}
auto name = machine_config::get_image_filename(dir, pma.get_start(), pma.get_length());
- auto fp = unique_fopen(name.c_str(), "wb");
const pma_memory &mem = pma.get_memory();
- if (fwrite(mem.get_host_memory(), 1, pma.get_length(), fp.get()) != pma.get_length()) {
- throw std::runtime_error{"error writing to '" + name + "'"};
+ if (!r.backing_storage.empty() && name == mem.get_backing_filename()) {
+ if (!os_exists(name.c_str())) {
+ throw std::runtime_error{"PMA backing file '" + name + "' was unexpectedly removed"};
+ }
+ } else if (!r.backing_storage.empty() && r.copy_reflink) {
+ os_unlock_fd(mem.get_backing_fd(), mem.get_backing_filename().c_str());
+ os_copy_reflink(mem.get_backing_filename().c_str(), name.c_str());
+ os_lock_fd(mem.get_backing_fd(), mem.get_backing_filename().c_str(), true);
+ } else {
+ // TODO(edubart): could use pread/pwrite to avoid page faults
+ os_write_file(name.c_str(), mem.get_host_memory(), pma.get_length());
}
}
@@ -747,32 +741,24 @@ void machine::store_pmas(const machine_config &c, const std::string &dir) const
if (read_reg(reg::iunrep)) {
throw std::runtime_error{"cannot store PMAs of unreproducible machines"};
}
- store_memory_pma(find_pma_entry(PMA_DTB_START), dir);
- store_memory_pma(find_pma_entry(PMA_RAM_START), dir);
+ store_memory_pma(find_pma_entry(PMA_DTB_START), dir, m_r);
+ store_memory_pma(find_pma_entry(PMA_RAM_START), dir, m_r);
store_device_pma(*this, find_pma_entry(PMA_SHADOW_TLB_START), dir);
// Could iterate over PMAs checking for those with a drive DID
// but this is easier
for (const auto &f : c.flash_drive) {
- store_memory_pma(find_pma_entry(f.start), dir);
+ store_memory_pma(find_pma_entry(f.start), dir, m_r);
}
- store_memory_pma(find_pma_entry(PMA_CMIO_RX_BUFFER_START), dir);
- store_memory_pma(find_pma_entry(PMA_CMIO_TX_BUFFER_START), dir);
+ store_memory_pma(find_pma_entry(PMA_CMIO_RX_BUFFER_START), dir, m_r);
+ store_memory_pma(find_pma_entry(PMA_CMIO_TX_BUFFER_START), dir, m_r);
if (!m_uarch.get_state().ram.get_istart_E()) {
- store_memory_pma(m_uarch.get_state().ram, dir);
- }
-}
-
-static void store_hash(const machine::hash_type &h, const std::string &dir) {
- auto name = dir + "/hash";
- auto fp = unique_fopen(name.c_str(), "wb");
- if (fwrite(h.data(), 1, h.size(), fp.get()) != h.size()) {
- throw std::runtime_error{"error writing to '" + name + "'"};
+ store_memory_pma(m_uarch.get_state().ram, dir, m_r);
}
}
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};
+ if (m_r.backing_storage.empty() || dir != m_r.backing_storage) {
+ os_mkdir(dir.c_str(), 0700);
}
if (!m_r.skip_root_hash_store) {
if (!update_merkle_tree()) {
@@ -780,7 +766,7 @@ void machine::store(const std::string &dir) const {
}
hash_type h;
m_t.get_root_hash(h);
- store_hash(h, dir);
+ os_write_file((dir + "/hash").c_str(), h.data(), h.size());
}
auto c = get_serialization_config();
c.store(dir);
@@ -789,6 +775,27 @@ void machine::store(const std::string &dir) const {
// NOLINTNEXTLINE(modernize-use-equals-default)
machine::~machine() {
+ if (!m_r.backing_storage.empty()) {
+ try {
+ // If the machine as not committed with an explicit store method, remove it
+ if (!os_exists((m_r.backing_storage + "/config.json"s).c_str())) {
+ // Remove backing files for PMAs
+ for (const auto &pma : m_pmas) {
+ if (pma->get_istart_M()) {
+ const std::string backing_filename = pma->get_memory().get_backing_filename();
+ *pma = make_empty_pma_entry("removed"s, 0, 0);
+ os_unlink(backing_filename.c_str());
+ }
+ }
+ // Remove backing storage directory
+ os_rmdir(m_r.backing_storage.c_str());
+ }
+ } catch (std::exception &e) {
+ // Silently fail
+ (void) fprintf(stderr, "unable to cleanup machine backing storage: %s\n", e.what());
+ }
+ }
+
// Cleanup TTY if console input was enabled
if (m_c.htif.console_getchar || has_virtio_console()) {
os_close_tty();
@@ -1989,10 +1996,7 @@ bool machine::update_merkle_tree(void) const {
return false;
}
if (page_data) {
- const bool is_pristine = std::all_of(page_data, page_data + PMA_PAGE_SIZE,
- [](unsigned char pp) -> bool { return pp == '\0'; });
-
- if (is_pristine) {
+ if (is_pristine(page_data, PMA_PAGE_SIZE)) {
// The update_page_node_hash function in the machine_merkle_tree is not thread
// safe, so we protect it with a mutex
const parallel_for_mutex_guard lock(mutex);
@@ -2138,6 +2142,7 @@ void machine::read_memory(uint64_t address, unsigned char *data, uint64_t length
}
const pma_entry &pma = find_pma_entry(m_pmas, address, length);
if (pma.get_istart_M()) {
+ // TODO(edubart): use pread to avoid faulting page
memcpy(data, pma.get_memory().get_host_memory() + (address - pma.get_start()), length);
return;
}
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..4220d71f8 100644
--- a/src/os-features.h
+++ b/src/os-features.h
@@ -40,6 +40,14 @@
#define HAVE_MMAP
#endif
+#if !defined(NO_FLOCK) && !defined(_WIN32) && !defined(__wasi__)
+#define HAVE_FLOCK
+#endif
+
+#if !defined(NO_FICLONE) && defined(__linux__)
+#define HAVE_FICLONE
+#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..3be4e3c12
--- /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_filename) {
+ if (backing_filename.empty() && (flags & (OS_MMAP_LOCKBACKING | 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_filename.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_filename.c_str(), oflags);
+ if (backing_fd < 0) {
+ throw std::system_error{errno, std::generic_category(),
+ "could not open backing file '"s + backing_filename + "'"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_filename + "'"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_filename + "' 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_LOCKBACKING) {
+ 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_filename + "'"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_NORESERVE) {
+ 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_filename.empty()) {
+ throw std::system_error{errno, std::generic_category(),
+ "could not map backing file '"s + backing_filename + "' 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_NORESERVE) {
+ 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_filename};
+ } 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..f687cf03b
--- /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_LOCKBACKING = 1 << 1, ///< Lock backing file (for shared read or exclusive writing)
+ OS_MMAP_READONLY = 1 << 2, ///< Mark memory as read-only
+ OS_MMAP_NORESERVE = 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_filename{};
+};
+
+/// \brief Maps OS memory
+os_mmapd os_mmap(uint64_t length, int flags = 0, const std::string &backing_filename = "");
+
+/// \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..27724ca34 100644
--- a/src/os.cpp
+++ b/src/os.cpp
@@ -14,6 +14,7 @@
// with this program (see COPYING). If not, see .
//
+#include
#include
#include
#include
@@ -24,6 +25,7 @@
#include
#include
+#include "is-pristine.h"
#include "os-features.h"
#include "os.h"
#include "unique-c-ptr.h"
@@ -57,6 +59,16 @@
#include // fstat/mkdir
#endif
+#if defined(HAVE_FLOCK)
+#include // flock
+#endif
+
+#ifdef HAVE_FICLONE
+#ifndef FICLONE
+#define FICLONE _IOW(0x94, 9, int)
+#endif
+#endif
+
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
@@ -516,158 +528,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);
- 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)) {
+ if (plat_mkdir(path, mode) != 0) {
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};
+ "error creating directory '"s + std::string(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 +655,316 @@ void os_sleep_us(uint64_t timeout_us) {
Sleep(timeout_us / 1000);
#endif
}
+
+void os_copy_reflink(const char *oldpath, const char *newpath) {
+#ifdef HAVE_POSIX_FS
+ int src_fd = -1;
+ int dest_fd = -1;
+ try {
+ // Open source file
+ src_fd = open(oldpath, O_RDONLY);
+ if (src_fd < 0) {
+ throw std::system_error{errno, std::generic_category(), "unable to open file '"s + oldpath + "' 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 + oldpath + "' for read"s};
+ }
+#endif
+
+ // Open destination file
+ const int mode = (S_IRUSR | S_IWUSR) | S_IRGRP | S_IROTH;
+ dest_fd = open(newpath, O_WRONLY | O_CREAT | O_EXCL, mode);
+ if (dest_fd < 0) {
+ throw std::system_error{errno, std::generic_category(),
+ "unable to open file '"s + newpath + "' for write"s};
+ }
+#ifdef HAVE_FLOCK
+ // Lock destination file
+ if (flock(dest_fd, LOCK_EX | LOCK_NB) < 0) {
+ throw std::system_error{errno, std::generic_category(),
+ "unable to lock file '"s + newpath + "' for write"s};
+ }
+#endif
+
+ // Clone file
+ if (ioctl(dest_fd, FICLONE, src_fd) < 0) {
+ throw std::system_error{errno, std::generic_category(), "unable to clone file '"s + newpath + "'"s};
+ }
+
+ // Close source file
+ if (close(src_fd) < 0) {
+ throw std::system_error{errno, std::generic_category(), "unable to close file '"s + oldpath + "'"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 + newpath + "'"s};
+ }
+ dest_fd = -1;
+ } catch (std::exception &e) {
+ if (src_fd != -1) {
+ close(src_fd);
+ }
+ if (dest_fd != -1) {
+ close(dest_fd);
+ unlink(newpath); // revert file creation
+ }
+ throw;
+ }
+#else
+ throw std::runtime_error{"copy reflink is unsupported in this platform"};
+#endif
+}
+
+void os_copy_file(const char *oldpath, const char *newpath) {
+ // 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(oldpath, O_RDONLY);
+ if (src_fd < 0) {
+ throw std::system_error{errno, std::generic_category(), "unable to open file '"s + oldpath + "' 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 + oldpath + "' 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 + oldpath + "'"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(newpath, O_RDWR | O_CREAT | O_EXCL, mode);
+ if (dest_fd < 0) {
+ throw std::system_error{errno, std::generic_category(),
+ "unable to open file '"s + newpath + "' for write"s};
+ }
+#ifdef HAVE_FLOCK
+ // Lock destination file
+ if (flock(dest_fd, LOCK_EX | LOCK_NB) < 0) {
+ throw std::system_error{errno, std::generic_category(),
+ "unable to lock file '"s + newpath + "' 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 + newpath + "'"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 + oldpath + "'"s};
+ } else if (static_cast(read_len) != len) {
+ throw std::runtime_error{"unable to read file '"s + oldpath + "'"s};
+ }
+ // Write only non zeros chunks (to keep file sparse)
+ if (!is_pristine(buf, len)) {
+ 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 + newpath + "'"s};
+ } else if (static_cast(written_len) != len) {
+ throw std::runtime_error{"unable to write file '"s + newpath + "'"s};
+ }
+ }
+ off += len;
+ }
+
+ // Close source file
+ if (close(src_fd) < 0) {
+ throw std::system_error{errno, std::generic_category(), "unable to close file '"s + oldpath + "'"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 + newpath + "'"s};
+ }
+ dest_fd = -1;
+ } catch (std::exception &e) {
+ if (src_fd != -1) {
+ close(src_fd);
+ }
+ if (dest_fd != -1) {
+ close(dest_fd);
+ unlink(newpath); // revert file creation
+ }
+ throw;
+ }
+
+#else
+ // TODO(edubart): remove file on failure
+ auto src_fp = unique_fopen(oldpath, "rb");
+ auto dest_fp = unique_fopen(newpath, "wb");
+
+ char buf[4096];
+ while (true) {
+ const size_t size = fread(buf, 1, sizeof(buf), 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(oldpath) + "'"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(newpath) + "'"s};
+ }
+ }
+
+#endif
+}
+
+void os_write_file(const char *path, const unsigned char *data, size_t length) {
+
+#ifdef HAVE_POSIX_FS
+ int fd = -1;
+ try {
+ // Open destination file
+ const int mode = (S_IRUSR | S_IWUSR) | S_IRGRP | S_IROTH;
+ fd = open(path, O_RDWR | O_CREAT | O_EXCL, mode);
+ if (fd < 0) {
+ throw std::system_error{errno, std::generic_category(), "unable to open file '"s + path + "' for write"s};
+ }
+#ifdef HAVE_FLOCK
+ // Lock destination file
+ if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
+ throw std::system_error{errno, std::generic_category(), "unable to lock file '"s + path + "' for write"s};
+ }
+#endif
+ // Truncate destination file to a sparse file
+ if (ftruncate(fd, static_cast(length)) < 0) {
+ throw std::system_error{errno, std::generic_category(), "unable to truncate file '"s + path + "'"s};
+ }
+
+ // Copy in chunks of 4096 bytes
+ for (size_t off = 0; off < length;) {
+ const size_t len = std::min(static_cast(length - off), 4096);
+ const unsigned char *buf = &data[off];
+ // Write only non zeros chunks (to keep file sparse)
+ if (!is_pristine(buf, len)) {
+ const ssize_t written_len = pwrite(fd, buf, len, static_cast(off));
+ if (written_len < 0) {
+ throw std::system_error{errno, std::generic_category(), "unable to write file '"s + path + "'"s};
+ } else if (static_cast(written_len) != len) {
+ throw std::runtime_error{"unable to write file '"s + path + "'"s};
+ }
+ }
+ off += len;
+ }
+
+ // Close destination file
+ if (close(fd) < 0) {
+ throw std::system_error{errno, std::generic_category(), "unable to close file '"s + path + "'"s};
+ }
+ fd = -1;
+ } catch (std::exception &e) {
+ if (fd != -1) {
+ close(fd);
+ unlink(path); // revert file creation
+ }
+ throw;
+ }
+#else
+ auto fp = unique_fopen(name.c_str(), "wb");
+ if (fwrite(data, 1, length, fp.get()) != pma.get_length()) {
+ throw std::runtime_error{"error writing to '" + name + "'"};
+ }
+
+#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;
+ }
+}
+
+bool os_exists(const char *path) {
+ const int fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ return false;
+ }
+ close(fd);
+ return true;
+}
+
+void os_unlink(const char *path, bool force) {
+ if (unlink(path) < 0 && !force) {
+ throw std::system_error{errno, std::generic_category(), "unable to unlink file '"s + path + "'"s};
+ }
+}
+
+void os_rmdir(const char *path, bool force) {
+ if (rmdir(path) < 0 && force) {
+ throw std::system_error{errno, std::generic_category(), "unable to remove directory '"s + path + "'"s};
+ }
+}
+
+void os_unlock_fd(int fd, const char *path) {
+ if (flock(fd, LOCK_UN | LOCK_NB) < 0) {
+ throw std::system_error{errno, std::generic_category(), "unable to unlock file'"s + path + "' for write"s};
+ }
+}
+
+void os_lock_fd(int fd, const char *path, bool write) {
+ if (flock(fd, (write ? LOCK_EX : LOCK_SH) | LOCK_NB) < 0) {
+ throw std::system_error{errno, std::generic_category(), "unable to lock file'"s + path + "' for write"s};
+ }
+}
+
} // namespace cartesi
diff --git a/src/os.h b/src/os.h
index c8178e525..21ef485df 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,17 @@ void os_disable_sigpipe();
/// \brief Sleep until timeout_us microseconds elapsed
void os_sleep_us(uint64_t timeout_us);
+void os_copy_reflink(const char *oldpath, const char *newpath);
+void os_copy_file(const char *oldpath, const char *newpath);
+void os_write_file(const char *path, const unsigned char *data, size_t length);
+void os_grow_file(const char *path, uint64_t length, bool create);
+bool os_exists(const char *path);
+void os_unlink(const char *path, bool force = false);
+void os_rmdir(const char *path, bool force = false);
+
+void os_unlock_fd(int fd, const char *path);
+void os_lock_fd(int fd, const char *path, bool write);
+
} // namespace cartesi
#endif
diff --git a/src/pma.cpp b/src/pma.cpp
index 2a0481f19..e9a6cd1a9 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_LOCKBACKING;
}
- }
-}
-
-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;
}
@@ -182,6 +117,7 @@ bool pma_peek_error(const pma_entry &, const machine &, uint64_t, const unsigned
/// \brief Memory range peek callback. See pma_peek.
static bool memory_peek(const pma_entry &pma, const machine &m, uint64_t page_address, const unsigned char **page_data,
unsigned char *scratch) {
+ // TODO(edubart): use pread to avoid faulting page
(void) m;
// If page_address is not aligned, or if it is out of range, return error
if ((page_address & (PMA_PAGE_SIZE - 1)) != 0 || page_address > pma.get_length()) {
@@ -206,31 +142,33 @@ 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) {
+ if (r.copy_reflink) {
+ os_copy_reflink(image_filename.c_str(), backing_filename.c_str());
+ } else {
+ os_copy_file(image_filename.c_str(), backing_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..fd31cdf61 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,21 @@ 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;
+ }
+
+ const std::string &get_backing_filename() const {
+ return m_mmaped.backing_filename;
+ }
+
+ int get_backing_fd() const {
+ return m_mmaped.backing_fd;
}
};
@@ -511,22 +483,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 +498,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;