Skip to content

Commit 9419d52

Browse files
committed
feat: support truncate and read-only for backing store mmap
1 parent 6785515 commit 9419d52

21 files changed

+476
-316
lines changed

src/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,7 @@ LIBCARTESI_OBJS:= \
358358
machine.o \
359359
memory-address-range.o \
360360
os.o \
361+
os-mmap.o \
361362
plic-address-range.o \
362363
pristine-merkle-tree.o \
363364
replay-step-state-access-interop.o \

src/address-range.h

+10
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,12 @@ class address_range {
327327
return do_is_page_marked_dirty(offset);
328328
}
329329

330+
/// \brief Returns true the mapped memory is read-only on the host
331+
/// \returns True if the memory is read-only in the host
332+
bool is_host_read_only() const noexcept {
333+
return do_is_host_read_only();
334+
}
335+
330336
private:
331337
// Default implementation of peek() always fails
332338
virtual bool do_peek(const machine & /*m*/, uint64_t /*offset*/, uint64_t /*length*/,
@@ -371,6 +377,10 @@ class address_range {
371377
virtual bool do_is_page_marked_dirty(uint64_t /*offset*/) const noexcept {
372378
return true;
373379
}
380+
381+
virtual bool do_is_host_read_only() const noexcept {
382+
return false;
383+
}
374384
};
375385

376386
template <size_t N>

src/cartesi-machine.lua

+1-1
Original file line numberDiff line numberDiff line change
@@ -628,7 +628,7 @@ local has_sync_init_date = false
628628
local memory_range_replace = {}
629629
local ram = {
630630
length = 128 << 20, -- 128MB
631-
backing_store = { data_filename = images_path .. "linux.bin" },
631+
backing_store = { data_filename = images_path .. "linux.bin", truncate = true },
632632
}
633633
local init_splash = true
634634
local append_bootargs = ""

src/machine.cpp

+33-48
Original file line numberDiff line numberDiff line change
@@ -93,27 +93,6 @@ using namespace std::string_literals;
9393

9494
static const auto throw_invalid_argument = [](const char *err) { throw std::invalid_argument{err}; };
9595

96-
/// \brief Creates a memory address range.
97-
/// \param d Description of address range for use in error messages.
98-
/// \param start Target physical address where range starts.
99-
/// \param length Length of range, in bytes.
100-
/// \param f Flags for address range.
101-
/// \param backing_store Backing store configuration for range.
102-
/// \returns New address range with flags already set.
103-
/// \details If \p backing_store.data_filename is non-empty and file is large enough to back entire address range,
104-
/// return a memory-mapped range, otherwise use calloc.
105-
static inline auto make_memory_address_range(const std::string &d, uint64_t start, uint64_t length, pmas_flags flags,
106-
const backing_store_config &backing_store) {
107-
if (backing_store.data_filename.empty() && backing_store.shared) {
108-
throw std::invalid_argument{"shared address range requires non-empty memory filename when initializing " + d};
109-
}
110-
if (backing_store.data_filename.empty() ||
111-
length > static_cast<uint64_t>(os_get_file_length(backing_store.data_filename.c_str()))) {
112-
return make_callocd_memory_address_range(d, start, length, flags, backing_store.data_filename);
113-
}
114-
return make_mmapd_memory_address_range(d, start, length, flags, backing_store.data_filename, backing_store.shared);
115-
}
116-
11796
void machine::check_address_range(const address_range &ar, register_where where) {
11897
if (!where.interpret && !where.merkle) {
11998
throw std::runtime_error{"address range "s + ar.get_description() + " must be registered somwhere"s};
@@ -158,8 +137,8 @@ void machine::replace_memory_range(const memory_range_config &config) {
158137
}
159138
// Replace range, preserving original flags.
160139
// This will automatically start with all pages dirty.
161-
existing = make_moved_unique(make_memory_address_range(existing->get_description(), existing->get_start(),
162-
existing->get_length(), existing->get_flags(), config.backing_store));
140+
existing = make_moved_unique(memory_address_range{existing->get_description(), existing->get_start(),
141+
existing->get_length(), existing->get_flags(), config.backing_store, config.read_only});
163142
return;
164143
}
165144
}
@@ -196,12 +175,12 @@ void machine::init_uarch(const uarch_config &c) {
196175
constexpr auto ram_description = "uarch RAM";
197176
if (c.ram.backing_store.data_filename.empty()) {
198177
m_us.ram = &register_address_range(
199-
make_callocd_memory_address_range(ram_description, AR_UARCH_RAM_START, UARCH_RAM_LENGTH, uram_flags),
178+
memory_address_range{ram_description, AR_UARCH_RAM_START, UARCH_RAM_LENGTH, uram_flags},
200179
register_where{.merkle = true, .interpret = false});
201180
memcpy(m_us.ram->get_host_memory(), uarch_pristine_ram, uarch_pristine_ram_len);
202181
} else {
203-
m_us.ram = &register_address_range(make_memory_address_range(ram_description, AR_UARCH_RAM_START,
204-
UARCH_RAM_LENGTH, uram_flags, c.ram.backing_store),
182+
m_us.ram = &register_address_range(memory_address_range{ram_description, AR_UARCH_RAM_START, UARCH_RAM_LENGTH,
183+
uram_flags, c.ram.backing_store},
205184
register_where{.merkle = true, .interpret = false});
206185
}
207186
}
@@ -291,25 +270,14 @@ void machine::init_ram_ar(const ram_config &ram) {
291270
if (ram.length == 0) {
292271
throw std::invalid_argument("RAM length cannot be zero");
293272
}
294-
register_address_range(make_memory_address_range("RAM"s, AR_RAM_START, ram.length, ram_flags, ram.backing_store),
273+
register_address_range(memory_address_range{"RAM"s, AR_RAM_START, ram.length, ram_flags, ram.backing_store},
295274
register_where{.merkle = true, .interpret = true});
296275
}
297276

298277
void machine::init_flash_drive_ars(flash_drive_configs &flash_drive) {
299278
if (flash_drive.size() > FLASH_DRIVE_MAX) {
300279
throw std::invalid_argument{"too many flash drives"};
301280
}
302-
// Flags for flash drives
303-
static const pmas_flags flash_flags{
304-
.M = true,
305-
.IO = false,
306-
.R = true,
307-
.W = true,
308-
.X = false,
309-
.IR = true,
310-
.IW = true,
311-
.DID = PMA_ISTART_DID::flash_drive,
312-
};
313281
// Register all flash drives
314282
int i = 0; // NOLINT(misc-const-correctness)
315283
for (auto &f : flash_drive) {
@@ -341,8 +309,19 @@ void machine::init_flash_drive_ars(flash_drive_configs &flash_drive) {
341309
}
342310
f.length = length;
343311
}
312+
// Flags for flash drives
313+
static const pmas_flags flash_flags{
314+
.M = true,
315+
.IO = false,
316+
.R = true,
317+
.W = !f.read_only,
318+
.X = false,
319+
.IR = true,
320+
.IW = !f.read_only,
321+
.DID = PMA_ISTART_DID::flash_drive,
322+
};
344323
register_address_range(
345-
make_memory_address_range(flash_description, f.start, f.length, flash_flags, f.backing_store),
324+
memory_address_range{flash_description, f.start, f.length, flash_flags, f.backing_store, f.read_only},
346325
register_where{.merkle = true, .interpret = true});
347326
i++;
348327
}
@@ -456,11 +435,11 @@ void machine::init_cmio_ars(const cmio_config &c) {
456435
.IW = true,
457436
.DID = PMA_ISTART_DID::cmio_rx_buffer,
458437
};
459-
register_address_range(make_memory_address_range("CMIO tx buffer memory range"s, AR_CMIO_TX_BUFFER_START,
460-
AR_CMIO_TX_BUFFER_LENGTH, tx_flags, c.tx_buffer.backing_store),
438+
register_address_range(memory_address_range{"CMIO tx buffer memory range"s, AR_CMIO_TX_BUFFER_START,
439+
AR_CMIO_TX_BUFFER_LENGTH, tx_flags, c.tx_buffer.backing_store},
461440
register_where{.merkle = true, .interpret = true});
462-
register_address_range(make_memory_address_range("CMIO rx buffer memory range"s, AR_CMIO_RX_BUFFER_START,
463-
AR_CMIO_RX_BUFFER_LENGTH, rx_flags, c.rx_buffer.backing_store),
441+
register_address_range(memory_address_range{"CMIO rx buffer memory range"s, AR_CMIO_RX_BUFFER_START,
442+
AR_CMIO_RX_BUFFER_LENGTH, rx_flags, c.rx_buffer.backing_store},
464443
register_where{.merkle = true, .interpret = true});
465444
}
466445

@@ -517,7 +496,7 @@ void machine::init_pmas_contents(const pmas_config &config, memory_address_range
517496

518497
void machine::init_tlb_contents(const tlb_config &config) {
519498
if (const auto &image_filename = config.backing_store.data_filename; !image_filename.empty()) {
520-
auto shadow_tlb_ptr = make_unique_mmap<shadow_tlb_state>(image_filename.c_str(), 1, false /* not shared */);
499+
auto shadow_tlb_ptr = make_unique_mmap<shadow_tlb_state>(1, os_mmap_flags{}, image_filename);
521500
auto &shadow_tlb = *shadow_tlb_ptr;
522501
for (auto set_index : {TLB_CODE, TLB_READ, TLB_WRITE}) {
523502
for (uint64_t slot_index = 0; slot_index < TLB_SET_SIZE; ++slot_index) {
@@ -550,7 +529,7 @@ static inline auto make_dtb_address_range(const dtb_config &config) {
550529
.IW = true,
551530
.DID = PMA_ISTART_DID::memory,
552531
};
553-
return make_memory_address_range("DTB"s, AR_DTB_START, AR_DTB_LENGTH, dtb_flags, config.backing_store);
532+
return memory_address_range{"DTB"s, AR_DTB_START, AR_DTB_LENGTH, dtb_flags, config.backing_store};
554533
}
555534

556535
void machine::init_dtb_contents(const machine_config &config, memory_address_range &dtb) {
@@ -570,7 +549,7 @@ static inline auto make_pmas_address_range(const pmas_config &config) {
570549
.IW = false,
571550
.DID = PMA_ISTART_DID::memory,
572551
};
573-
return make_memory_address_range("PMAs", AR_PMAS_START, AR_PMAS_LENGTH, m_pmas_flags, config.backing_store);
552+
return memory_address_range{"PMAs", AR_PMAS_START, AR_PMAS_LENGTH, m_pmas_flags, config.backing_store};
574553
}
575554

576555
// ??D It is best to leave the std::move() on r because it may one day be necessary!
@@ -2190,6 +2169,9 @@ void machine::write_memory(uint64_t paddr, const unsigned char *data, uint64_t l
21902169
if (!ar.is_memory()) {
21912170
throw std::invalid_argument{"address range to write is not entirely in single memory range"};
21922171
}
2172+
if (ar.is_host_read_only()) {
2173+
throw std::invalid_argument{"address range is read-only in the host"};
2174+
}
21932175
if (is_protected(ar.get_driver_id())) {
21942176
throw std::invalid_argument{"attempt to write to protected memory range"};
21952177
}
@@ -2216,6 +2198,9 @@ void machine::fill_memory(uint64_t paddr, uint8_t val, uint64_t length) {
22162198
if (!ar.is_memory()) {
22172199
throw std::invalid_argument{"address range to fill is not entirely in memory PMA"};
22182200
}
2201+
if (ar.is_host_read_only()) {
2202+
throw std::invalid_argument{"address range is read-only in the host"};
2203+
}
22192204
if (is_protected(ar.get_driver_id())) {
22202205
throw std::invalid_argument{"attempt fill to protected memory range"};
22212206
}
@@ -2346,7 +2331,7 @@ void machine::write_word(uint64_t paddr, uint64_t val) {
23462331
<< std::dec << paddr << ")";
23472332
throw std::runtime_error{err.str()};
23482333
}
2349-
if (!ar.is_writeable()) {
2334+
if (!ar.is_writeable() || ar.is_host_read_only()) {
23502335
std::ostringstream err;
23512336
err << "attempted memory write to read-only " << ar.get_description() << " at address 0x" << std::hex << paddr
23522337
<< "(" << std::dec << paddr << ")";
@@ -2531,7 +2516,7 @@ interpreter_break_reason machine::log_step(uint64_t mcycle_count, const std::str
25312516
interpreter_break_reason machine::verify_step(const hash_type &root_hash_before, const std::string &filename,
25322517
uint64_t mcycle_count, const hash_type &root_hash_after) {
25332518
auto data_length = os_get_file_length(filename.c_str(), "step log file");
2534-
auto data = make_unique_mmap<unsigned char>(filename.c_str(), data_length, false /* not shared */);
2519+
auto data = make_unique_mmap<unsigned char>(data_length, os_mmap_flags{}, filename);
25352520
replay_step_state_access::context context;
25362521
replay_step_state_access a(context, data.get(), data_length, root_hash_before);
25372522
uint64_t mcycle_end{};

src/memory-address-range.cpp

+12-46
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,19 @@ class base_error : public std::invalid_argument {
2828
};
2929

3030
memory_address_range::memory_address_range(const std::string &description, uint64_t start, uint64_t length,
31-
const pmas_flags &flags, const std::string &image_filename, const mmapd &m) try :
31+
const pmas_flags &flags, const backing_store_config &backing_store, bool read_only) try :
3232
address_range(description.c_str(), start, length, flags, [](const char *err) { throw base_error{err}; }),
33-
m_ptr{make_unique_mmap<unsigned char>(image_filename.c_str(), length, m.shared)},
34-
m_host_memory{std::get<mmapd_ptr>(m_ptr).get()} {
33+
m_ptr{make_unique_mmap<unsigned char>(length,
34+
os_mmap_flags{
35+
.shared = backing_store.shared,
36+
.read_only = read_only,
37+
.truncate = backing_store.truncate,
38+
// Lock backing file to prevent other processes causing memory corruptions
39+
.lock_backing = !backing_store.data_filename.empty(),
40+
},
41+
backing_store.data_filename)},
42+
m_host_memory{m_ptr.get()},
43+
m_read_only{read_only} {
3544
if (!is_memory()) {
3645
throw std::invalid_argument{"memory range must be flagged memory when initializing "s + description};
3746
}
@@ -44,47 +53,4 @@ memory_address_range::memory_address_range(const std::string &description, uint6
4453
throw std::invalid_argument{"unknown exception when initializing "s + description};
4554
}
4655

47-
memory_address_range::memory_address_range(const std::string &description, uint64_t start, uint64_t length,
48-
const pmas_flags &flags, const std::string &image_filename, const callocd & /*c*/) try :
49-
address_range(description.c_str(), start, length, flags, [](const char *err) { throw base_error{err}; }),
50-
m_ptr{make_unique_calloc<unsigned char>(length)},
51-
m_host_memory{std::get<callocd_ptr>(m_ptr).get()} {
52-
if (!is_memory()) {
53-
throw std::invalid_argument{"memory range must be flagged memory when initializing "s + description};
54-
}
55-
m_dirty_page_map.resize((length / (8 * AR_PAGE_SIZE)) + 1, 0xff);
56-
// Try to load image file, if any
57-
if (!image_filename.empty()) {
58-
auto fp = make_unique_fopen(image_filename.c_str(), "rb", std::nothrow_t{});
59-
if (!fp) {
60-
throw std::system_error{errno, std::generic_category(), "error opening image file '"s + image_filename};
61-
}
62-
// Get file size
63-
if (fseek(fp.get(), 0, SEEK_END) != 0) {
64-
throw std::system_error{errno, std::generic_category(),
65-
"error obtaining length of image file '"s + image_filename};
66-
}
67-
const auto file_length = static_cast<uint64_t>(ftello(fp.get()));
68-
if (fseek(fp.get(), 0, SEEK_SET) != 0) {
69-
throw std::system_error{errno, std::generic_category(),
70-
"error obtaining length of image file '"s + image_filename};
71-
}
72-
// Check against PMA range size
73-
if (file_length > length) {
74-
throw std::runtime_error{"image file '"s + image_filename + "' is too large for range"s};
75-
}
76-
// Read to host memory
77-
const auto read_length = static_cast<uint64_t>(fread(m_host_memory, 1, file_length, fp.get()));
78-
if (read_length != file_length) {
79-
throw std::runtime_error{"error reading from image file '"s + image_filename};
80-
}
81-
}
82-
} catch (base_error &b) {
83-
throw; // already contains the description
84-
} catch (std::exception &e) {
85-
throw std::invalid_argument{e.what() + " when initializing "s + description};
86-
} catch (...) {
87-
throw std::invalid_argument{"unknown exception when initializing "s + description};
88-
}
89-
9056
} // namespace cartesi

src/memory-address-range.h

+9-40
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
#include <variant>
2222

2323
#include "address-range.h"
24+
#include "machine-config.h"
25+
#include "os-mmap.h"
2426
#include "unique-c-ptr.h"
2527

2628
namespace cartesi {
@@ -32,49 +34,22 @@ class machine;
3234
/// \brief An address range occupied by memory
3335

3436
class memory_address_range : public address_range {
35-
36-
using callocd_ptr = unique_calloc_ptr<unsigned char>;
37-
using mmapd_ptr = unique_mmap_ptr<unsigned char>;
38-
39-
std::variant<std::monostate, ///< Before initialization
40-
callocd_ptr, ///< Automatic pointer for calloced memory
41-
mmapd_ptr ///< Automatic pointer for mmapped memory
42-
>
43-
m_ptr;
44-
37+
unique_mmap_ptr<unsigned char> m_ptr; ///< Pointer to mmaped memory
4538
unsigned char *m_host_memory; ///< Start of associated memory region in host.
39+
bool m_read_only; ///< Whether the mapped memory is read-only on the host
4640
std::vector<uint8_t> m_dirty_page_map; ///< Map of dirty pages.
4741

4842
public:
4943
using ptr_type = std::unique_ptr<memory_address_range>;
5044

51-
/// \brief Mmap'd range data (shared or not).
52-
struct mmapd {
53-
bool shared;
54-
};
55-
5645
/// \brief Constructor for mmap'd ranges.
5746
/// \param description Description of address range for use in error messages
5847
/// \param start Start of address range
5948
/// \param length Length of address range
6049
/// \param flags Range flags
6150
/// \param image_filename Path to backing file.
62-
/// \param m Mmap'd range data (shared or not).
63-
memory_address_range(const std::string &description, uint64_t start, uint64_t length, const pmas_flags &flags,
64-
const std::string &image_filename, const mmapd &m);
65-
66-
/// \brief Calloc'd range data (just a tag).
67-
struct callocd {};
68-
69-
/// \brief Constructor for calloc'd ranges.
70-
/// \param description Description of address range for use in error messages
71-
/// \param start Start of address range
72-
/// \param length Length of address range
73-
/// \param flags Range flags
74-
/// \param image_filename Path to backing file.
75-
/// \param c Calloc'd range data (just a tag).
7651
memory_address_range(const std::string &description, uint64_t start, uint64_t length, const pmas_flags &flags,
77-
const std::string &image_filename, const callocd & /*c*/);
52+
const backing_store_config &backing_store = {}, bool read_only = false);
7853

7954
memory_address_range(const memory_address_range &) = delete;
8055
memory_address_range &operator=(const memory_address_range &) = delete;
@@ -117,6 +92,10 @@ class memory_address_range : public address_range {
11792
return (m_dirty_page_map[map_index] & (1 << (page_index & 7))) != 0;
11893
}
11994

95+
bool do_is_host_read_only() const noexcept override {
96+
return m_read_only;
97+
}
98+
12099
bool do_peek(const machine & /*m*/, uint64_t offset, uint64_t length, const unsigned char **data,
121100
unsigned char * /*scratch*/) const noexcept override {
122101
if (contains_relative(offset, length)) {
@@ -128,16 +107,6 @@ class memory_address_range : public address_range {
128107
}
129108
};
130109

131-
static inline auto make_callocd_memory_address_range(const std::string &description, uint64_t start, uint64_t length,
132-
pmas_flags flags, const std::string &image_filename = {}) {
133-
return memory_address_range{description, start, length, flags, image_filename, memory_address_range::callocd{}};
134-
}
135-
136-
static inline auto make_mmapd_memory_address_range(const std::string &description, uint64_t start, uint64_t length,
137-
pmas_flags flags, const std::string &image_filename, bool shared) {
138-
return memory_address_range{description, start, length, flags, image_filename, memory_address_range::mmapd{shared}};
139-
}
140-
141110
} // namespace cartesi
142111

143112
#endif

0 commit comments

Comments
 (0)