Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
VirtualMemorySpace.hxx
Go to the documentation of this file.
1
36#ifndef _OPENLCB_VIRTUALMEMORYSPACE_HXX
37#define _OPENLCB_VIRTUALMEMORYSPACE_HXX
38
43
44namespace openlcb
45{
46
50{
51public:
53 : isReadOnly_(false)
54 {
55 }
56
58 bool read_only() override
59 {
60 return isReadOnly_;
61 }
63 address_t min_address() override
64 {
65 return minAddress_;
66 }
69 address_t max_address() override
70 {
71 return maxAddress_;
72 }
73
85 size_t write(address_t destination, const uint8_t *data, size_t len,
86 errorcode_t *error, Notifiable *again) override
87 {
88 if ((destination > maxAddress_) || ((destination + len) <= minAddress_))
89 {
90 *error = MemoryConfigDefs::ERROR_OUT_OF_BOUNDS;
91 return 0;
92 }
93 if (destination + len > maxAddress_ + 1)
94 {
95 len = maxAddress_ + 1 - destination;
96 }
97 *error = 0;
98 unsigned repeat;
99 const DataElement *element = nullptr;
100 ssize_t skip = find_data_element(destination, len, &element, &repeat);
101 string payload;
102 size_t written_len;
103 if (skip > 0)
104 {
105 // Will cause a new call be delivered with adjusted data and len.
106 return skip;
107 }
108 else if (skip < 0)
109 {
110 HASSERT(element);
111 // We have some missing bytes that we need to read out first, then
112 // can perform the write.
113 address_t field_start = destination + skip;
114 if (!(cacheOffset_ == field_start &&
115 (cachedData_.size() >= (size_t)-skip)))
116 {
117 cacheOffset_ = field_start;
118 cachedData_.clear();
119 bn_.reset(again);
120 element->readImpl_(repeat, &cachedData_, bn_.new_child());
122 {
123 // did not succeed synchronously.
124 bn_.notify(); // our slice
126 return 0;
127 }
128 cachedData_.resize(element->size_); // pads with zeroes
129 }
130 // Now: cachedData_ contains the payload in the current storage.
131 payload = cachedData_;
132 written_len =
133 std::min((size_t)len, (size_t)(element->size_ + skip));
134 memcpy(&payload[-skip], (const char *)data, written_len);
135 }
136 else // exact address write.
137 {
138 HASSERT(element);
139 payload.assign((const char *)data,
140 std::min((size_t)len, (size_t)element->size_));
141 written_len = payload.size();
142 }
143 bn_.reset(again);
144 element->writeImpl_(repeat, std::move(payload), bn_.new_child());
146 {
147 cachedData_.clear();
148 return written_len;
149 }
150 else
151 {
152 // did not succeed synchronously.
153 bn_.notify(); // our slice
155 return 0;
156 }
157 }
158
165 size_t read(address_t source, uint8_t *dst, size_t len, errorcode_t *error,
166 Notifiable *again) override
167 {
168 if ((source > maxAddress_) || ((source + len) <= minAddress_))
169 {
170 *error = MemoryConfigDefs::ERROR_OUT_OF_BOUNDS;
171 return 0;
172 }
173 if (source + len > maxAddress_ + 1)
174 {
175 len = maxAddress_ + 1 - source;
176 }
177 *error = 0;
178 unsigned repeat;
179 const DataElement *element = nullptr;
180 ssize_t skip = find_data_element(source, len, &element, &repeat);
181 if (skip > 0)
182 {
183 memset(dst, 0, skip);
184 return skip;
185 }
186 // Now: skip <= 0
187 HASSERT(element);
188 string payload;
189 bn_.reset(again);
190 element->readImpl_(repeat, &payload, bn_.new_child());
192 {
193 // did not succeed synchronously.
194 bn_.notify(); // our slice
196 return 0;
197 }
198 payload.resize(element->size_); // pads with zeroes
199 size_t data_len = std::min(payload.size() + skip, len);
200 memcpy(dst, payload.data() - skip, data_len);
201 return data_len;
202 }
203
204protected:
212 using WriteFunction = std::function<void(
213 unsigned repeat, string contents, BarrierNotifiable *done)>;
221 using ReadFunction = std::function<void(
222 unsigned repeat, string *contents, BarrierNotifiable *done)>;
223
225 template <typename T>
226 using TypedWriteFunction = typename std::function<void(
227 unsigned repeat, T contents, BarrierNotifiable *done)>;
228
231 template <typename T>
232 using TypedReadFunction = typename std::function<T (
233 unsigned repeat, BarrierNotifiable *done)>;
234
237 template <class G> void set_bounds_from_group(const G &group)
238 {
239 minAddress_ = group.offset();
240 maxAddress_ = group.offset() + group.size() - 1;
241 }
242
245 template <class G> void expand_bounds_from_group(const G &group)
246 {
247 minAddress_ = std::min(minAddress_, (address_t)group.offset());
248 maxAddress_ = std::max(
249 maxAddress_, (address_t)(group.offset() + group.size() - 1));
250 }
251
257 void register_element(address_t address, address_t size,
258 ReadFunction read_f, WriteFunction write_f)
259 {
260 elements_.insert(DataElement(address, size, read_f, write_f));
261 }
262
267 template <unsigned SIZE>
269 ReadFunction read_f, WriteFunction write_f)
270 {
272 register_element(entry.offset(), SIZE, read_f, write_f);
273 }
274
280 template <typename T>
283 {
285 auto trf = [read_f](unsigned repeat, string *contents,
286 BarrierNotifiable *done) {
287 T result = read_f(repeat, done);
288 contents->clear();
289 contents->resize(sizeof(T));
290 *((T *)&((*contents)[0])) =
292 };
293 auto twf = [write_f](unsigned repeat, string contents,
294 BarrierNotifiable *done) {
295 contents.resize(sizeof(T));
297 *(const T *)contents.data());
298 write_f(repeat, result, done);
299 };
301 entry.offset(), entry.size(), std::move(trf), std::move(twf));
302 }
303
312 template <class Group, unsigned N>
314 {
315 RepeatElement re;
316 re.start_ = group.offset();
317 re.end_ = group.end_offset();
318 re.repeatSize_ = Group::size();
319 HASSERT(re.repeatSize_ * N == re.end_ - re.start_);
320 repeats_.insert(std::move(re));
322 }
323
325 address_t minAddress_ = 0xFFFFFFFFu;
328 address_t maxAddress_ = 0;
330 unsigned isReadOnly_ : 1;
331
332private:
335 {
336 DataElement(address_t address, address_t size, ReadFunction read_f,
337 WriteFunction write_f)
338 : address_(address)
339 , size_(size)
340 , writeImpl_(write_f)
341 , readImpl_(read_f)
342 {
343 }
345 address_t address_;
348 address_t size_;
353 };
354
357 {
359 bool operator()(const DataElement &a, const DataElement &b) const
360 {
361 return a.address_ < b.address_;
362 }
364 bool operator()(unsigned a, const DataElement &b) const
365 {
366 return a < b.address_;
367 }
369 bool operator()(const DataElement &a, unsigned b) const
370 {
371 return a.address_ < b;
372 }
373 };
374
377 {
379 uint32_t start_;
381 uint32_t repeatSize_;
383 uint32_t end_;
384 };
385
389 {
391 bool operator()(const RepeatElement &a, const RepeatElement &b) const
392 {
393 return a.end_ < b.end_;
394 }
396 bool operator()(uint32_t a, const RepeatElement &b) const
397 {
398 return a < b.end_;
399 }
400 };
401
417 ssize_t find_data_element(address_t address, address_t len,
418 const DataElement **ptr, unsigned *repeat)
419 {
420 *repeat = 0;
421 *ptr = nullptr;
422 bool in_repeat = false;
423 address_t original_address = address;
426 // Align in the known repetitions first.
427 auto rit = repeats_.upper_bound(address);
428 int max_repeat = 0;
429 if (rit == repeats_.end())
430 {
431 // not a repeat.
432 }
433 else
434 {
435 if (rit->start_ <= address && address < rit->end_)
436 {
437 // we are in the repeat.
438 unsigned cnt = (address - rit->start_) / rit->repeatSize_;
439 *repeat = cnt;
440 if (address + rit->repeatSize_ < rit->end_)
441 {
442 // Try one repetition later too.
443 max_repeat = 1;
444 }
445 // re-aligns address to the first repetition.
446 address -= cnt * rit->repeatSize_;
447 in_repeat = true;
448 b = elements_.lower_bound(rit->start_);
449 e = elements_.lower_bound(rit->start_ + rit->repeatSize_);
450 }
451 }
452 LOG(VERBOSE,
453 "searching for element at address %u in_repeat=%d address=%u "
454 "len=%u",
455 (unsigned)original_address, in_repeat, (unsigned)address,
456 (unsigned)len);
457
458 for (int is_repeat = 0; is_repeat <= max_repeat; ++is_repeat)
459 {
460 auto it = std::upper_bound(b, e, address, DataComparator());
461 if (it != elements_.begin())
462 {
463 auto pit = it - 1;
464 // now: pit->address_ <= address
465 if (pit->address_ + pit->size_ > address)
466 {
467 // found overlap
468 *ptr = &*pit;
469 return (ssize_t)pit->address_ -
470 (ssize_t)address; // may be negative!
471 }
472 // else: no overlap, look at the next item
473 }
474 // now: it->address_ > address
475 if ((it != elements_.end()) && (address + len > it->address_))
476 {
477 // found overlap, but some data needs to be discarded.
478 *ptr = &*it;
479 return it->address_ - address;
480 }
481
482 if (in_repeat)
483 {
484 // We might be too close to the end of a repetition, we will
485 // try with the next repeat instead.
486 address -= rit->repeatSize_;
487 *repeat += 1;
488 if (original_address + rit->repeatSize_ >= rit->end_)
489 {
490 // We ran out of repeats. Look at the range beyond the
491 // group instead.
492 b = elements_.lower_bound(rit->end_);
493 e = elements_.end();
494 *repeat = 0;
495 }
496 }
497 else
498 {
499 break;
500 }
501 }
502
503 // now: no overlap either before or after.
504 LOG(VERBOSE, "element not found for address %u",
505 (unsigned)original_address);
506 return len;
507 }
508
509 static constexpr unsigned NO_CACHE = static_cast<address_t>(-1);
511 address_t cacheOffset_ = NO_CACHE;
522}; // class VirtualMemorySpace
523
524} // namespace openlcb
525
526#endif // _OPENLCB_VIRTUALMEMORYSPACE_HXX
A BarrierNotifiable allows to create a number of child Notifiable and wait for all of them to finish.
void notify() override
Implementation of the barrier semantics.
BarrierNotifiable * reset(Notifiable *done)
Resets the barrier. Returns &*this. Asserts that is_done().
BarrierNotifiable * new_child()
Call this for each child task.
bool abort_if_almost_done()
Checks if there is exactly one outstanding notification left in the barrier.
An object that can schedule itself on an executor to run.
An mostly std::set<> compatible class that stores the internal data in a sorted vector.
iterator begin()
iterator lower_bound(key_type key)
void insert(data_type &&d)
Adds new entry to the vector.
container_type::iterator iterator
Iterator type.
Abstract base class for the address spaces exported via the Memory Config Protocol.
static const errorcode_t ERROR_AGAIN
This error code signals that the operation was only partially completed, the again notify was used an...
Implementation class for numeric configuration entries, templated by the integer type.
static uint8_t endian_convert(uint8_t d)
Performs endian conversion.
static constexpr unsigned size()
Storage bytes occupied by the instance in the config file.
Defines a repeated group of a given type and a given number of repeats.
Implementation class for string configuration entries.
Implementation of a memory space where the values are not stored in a contiguous storage area but are...
unsigned isReadOnly_
Whether the space should report as RO.
ssize_t find_data_element(address_t address, address_t len, const DataElement **ptr, unsigned *repeat)
Look up the first matching data element given an address in the virtual memory space.
BarrierNotifiable bn_
Helper object in the function calls.
void register_string(const StringConfigEntry< SIZE > &entry, ReadFunction read_f, WriteFunction write_f)
Registers a string typed element.
void expand_bounds_from_group(const G &group)
Expand the address bounds from a single CDI group declaration.
void register_numeric(const NumericConfigEntry< T > &entry, TypedReadFunction< T > read_f, TypedWriteFunction< T > write_f)
Registers a numeric typed element.
std::function< void(unsigned repeat, string contents, BarrierNotifiable *done)> WriteFunction
Function that will be called for writes.
void register_repeat(const RepeatedGroup< Group, N > &group)
Registers a repeated group.
void register_element(address_t address, address_t size, ReadFunction read_f, WriteFunction write_f)
Register an untyped element.
SortedListSet< DataElement, DataComparator > ElementsType
Container type for storing the data elements.
string cachedData_
Stored information for read-modify-write calls.
address_t maxAddress_
Bounds for valid addresses.
size_t write(address_t destination, const uint8_t *data, size_t len, errorcode_t *error, Notifiable *again) override
address_t minAddress_
Bounds for valid addresses.
std::function< void(unsigned repeat, string *contents, BarrierNotifiable *done)> ReadFunction
Function that will be called for reads.
size_t read(address_t source, uint8_t *dst, size_t len, errorcode_t *error, Notifiable *again) override
typename std::function< void(unsigned repeat, T contents, BarrierNotifiable *done)> TypedWriteFunction
Typed WriteFunction for primitive types.
ElementsType elements_
Stores all the registered variables.
void set_bounds_from_group(const G &group)
Setup the address bounds from a single CDI group declaration.
SortedListSet< RepeatElement, RepeatComparator > repeats_
Stores all the registered variables.
typename std::function< T(unsigned repeat, BarrierNotifiable *done)> TypedReadFunction
Typed ReadFunction for primitive types.
address_t cacheOffset_
Offset in the memory space at which cachedData_ starts.
#define LOG(level, message...)
Conditionally write a message to the logging output.
Definition logging.h:99
static const int VERBOSE
Loglevel that is usually not printed, reporting debugging information.
Definition logging.h:59
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138
STL-compatible comparator function for sorting DataElements.
bool operator()(const DataElement &a, const DataElement &b) const
Sorting operator by address.
bool operator()(const DataElement &a, unsigned b) const
Sorting operator by address.
bool operator()(unsigned a, const DataElement &b) const
Sorting operator by address.
We keep one of these for each variable that was declared.
address_t address_
Base offset of this variable (first repeat only).
ReadFunction readImpl_
Function that will be called for reads.
WriteFunction writeImpl_
Function that will be called for writes.
STL-compatible comparator function for sorting RepeatElements.
bool operator()(uint32_t a, const RepeatElement &b) const
Sorting operator by end address against a lookup key.
bool operator()(const RepeatElement &a, const RepeatElement &b) const
Sorting operator by end address.
uint32_t end_
Address byte after the last repeat.
uint32_t repeatSize_
Address bytes per repeat.
uint32_t start_
Offset of the repeated group (first repeat).