6 * Memory management classes, mirroring the L4 RM's ones.
8 * (c) 2011-2013 Björn Döbel <doebel@os.inf.tu-dresden.de>,
9 * economic rights: Technische Universität Dresden (Germany)
10 * This file is part of TUD:OS and distributed under the terms of the
11 * GNU General Public License 2.
12 * Please see the COPYING-GPL-2 file for details.
16 * Memory management (e.g., region manager)
27 #include <l4/util/util.h>
28 #include <l4/re/util/cap_alloc>
29 #include <l4/re/util/region_mapping>
30 #include <l4/re/util/dataspace_svr>
31 #include <l4/util/bitops.h>
32 #include <l4/util/rdtsc.h>
34 #include <l4/sys/kdebug.h>
36 #include "constants.h"
51 l4_addr_t sp() { return _sp; }
55 * Push a value to the stack
58 l4_addr_t push(T value)
67 * Push a buffer to stack.
69 * Returns difference to previous stack ptr. This diff may be
70 * different from the len parameter for alignment reasons.
73 l4_umword_t push_data(T* buf, l4_umword_t len)
75 l4_addr_t orig_sp = _sp;
76 l4_umword_t space = len * sizeof(T);
78 _sp &= ~0x03; // word alignment XXX
79 memcpy((char*)_sp, buf, space);
88 template <typename TYPE>
91 enum { can_free = true };
93 Allocator() throw() {}
94 Allocator(Romain::Allocator<TYPE> const &) throw() {}
95 ~Allocator() throw() {}
97 TYPE *alloc() throw() { return static_cast<TYPE*>(::malloc(sizeof(TYPE))); }
98 void free(void* ptr) throw() { ::free(ptr); }
103 class Region : public L4Re::Util::Region
106 typedef L4Re::Util::Region Base;
108 Region(l4_addr_t addr = 0) throw()
112 Region(l4_addr_t start, l4_addr_t end) throw()
120 DEBUG() << "touching 0x" << std::hex << this->start()
121 << " + " << this->size() << " rw'able";
123 l4_touch_rw((void*)this->start(), this->size());
130 * Romain Memory region handler. This handler is different from L4Re's region
131 * handler in that it can manage multiple source mappings.
133 * Multiple sources are used for handling multiple replicas which have
134 * write access to a memory region. In this case we need to establish a
135 * mapping per replica.
137 template <typename DS, typename OPS>
138 class Region_handler_template
141 typedef L4::Cap<L4Re::Dataspace> DSCAP;
142 typedef DS Dataspace;
144 typedef typename OPS::Map_result Map_result;
146 enum RegionWritableType {
148 Read_only_emulate_write,
154 l4_cap_idx_t _cap; // capability of the underlying dataspace
155 Romain::Region _local_regions[Romain::MAX_REPLICAS]; // replicated regions (writable regions exist in
156 // multiple instances)
157 l4_addr_t _offs; // offset in DS
158 DS _mem[Romain::MAX_REPLICAS]; // DS capabilities of underlying dataspaces
159 l4_uint8_t _flags; // region flags
160 RegionWritableType _row; // rw state
161 bool _shared; // is DS shared across all replicas
162 // (true for internal determinism's lock info page)
163 l4_umword_t _alignment; // alignment of this DS within master
164 l4_umword_t _align_diff; // diff between alignment and real start address
165 // (see Region_map::attach_locally() for an explanation)
166 l4_umword_t _numpf; // page faults seen on this region
170 Region_handler_template()
171 : _cap(L4_INVALID_CAP), _offs(0), _flags(),
172 _row(Romain::Region_handler_template<DS, OPS>::Read_only),
173 _shared(false), _alignment(L4_PAGESHIFT), _align_diff(0), _numpf(0)
177 Region_handler_template(const Region_handler_template& o)
178 : _cap(o.client_cap_idx()),
183 _alignment(o.alignment()),
184 _align_diff(o.align_diff()),
187 for (l4_umword_t i = 0; i < o.local_region_count(); ++i) {
188 _local_regions[i] = o.local_region(i);
189 _mem[i] = o.local_memory(i);
193 Region_handler_template(DSCAP cap, l4_cap_idx_t client_cap = L4_INVALID_CAP, l4_addr_t offset = 0,
194 l4_umword_t flags = 0, Romain::Region local_reg = Romain::Region(0,0))
195 : _cap(client_cap), _offs(offset), _flags(flags), _shared(false),
196 _alignment(L4_PAGESHIFT), _align_diff(0), _numpf(0)
198 _local_regions[0] = local_reg;
200 for (l4_umword_t i = 1; i < Romain::MAX_REPLICAS; ++i)
204 * Yes, this looks like a copy of the RO flag. But in our case, this is actually
205 * a tri-state. A page may be writable but mapped read-only if the mapping is
206 * shared memory. This fact is however established by someone else (e.g., the syscall
207 * observer) and here we just set one of the two default states.
209 if (flags & L4Re::Rm::Read_only) {
210 writable(Romain::Region_handler_template<DS, OPS>::Read_only);
212 writable(Romain::Region_handler_template<DS, OPS>::Writable);
216 l4_umword_t local_region_count() const { return MAX_REPLICAS; }
217 Romain::Region const & local_region(l4_umword_t idx) const { return _local_regions[idx]; }
219 void writable(RegionWritableType rw) { _row = rw; }
220 RegionWritableType writable() const { return _row; }
223 * Set a value for the mapping established for a given combination
224 * of Region and Instance.
226 void set_local_region(l4_umword_t idx, Romain::Region const & r)
228 _check(idx >= Romain::MAX_REPLICAS, "invalid instance");
229 _local_regions[idx] = r;
232 //DS const &memory() const throw() { return _mem; }
233 l4_cap_idx_t memory() const throw() { return _cap; }
234 l4_cap_idx_t client_cap_idx() const throw() { return _cap; }
235 DS const & local_memory(l4_umword_t idx) const throw() { return _mem[idx]; }
236 void local_memory(l4_umword_t idx, DS const & m) { _mem[idx] = m; }
237 l4_addr_t offset() const throw() { return _offs; }
238 void offset(l4_addr_t o) { _offs = o; }
239 l4_addr_t is_ro() const throw() { return _flags & L4Re::Rm::Read_only; }
240 l4_umword_t flags() const throw() { return _flags; }
242 bool shared() const throw() { return _shared; }
243 void shared(bool yn) { _shared = yn; }
244 l4_addr_t alignment() const throw() { return _alignment; }
245 l4_addr_t align_diff() const throw() { return _align_diff; }
246 l4_umword_t numpf() const throw() { return _numpf; }
247 void count_pf() throw() { _numpf++; }
249 /* Compute the target alignment for attaching locally.
251 * This alignment is necessary to make sure that a large region
252 * is attached to matching addresses in the replica and master
253 * address spaces. Only then can we map memory in chunks of more
254 * than one page a time when resolving page faults.
256 void alignToAddressAndSize(l4_addr_t address, l4_size_t size)
260 //12, /* 4 kB -> minimum! */
266 DEBUGf(Romain::Log::Memory) << "alignToAddressAndSize(" << std::hex << address
267 << ", " << size << ")";
269 l4_addr_t min_align = MIN_ALIGNMENT;
270 if (size < (1 << MIN_ALIGNMENT)) {
271 min_align = l4util_bsr(size);
273 DEBUGf(Romain::Log::Memory) << "Minimum alignment: " << std::hex << min_align;
275 if (min_align > L4_SUPERPAGESHIFT) {
276 min_align = L4_SUPERPAGESHIFT;
278 DEBUGf(Romain::Log::Memory) << std::hex << address
279 << " --> [max: super page] alignment: "
282 _alignment = min_align;
283 l4_umword_t align_mask = (1 << _alignment) - 1;
284 _align_diff = address & align_mask;
285 DEBUGf(Romain::Log::Memory) << std::hex
286 << address << " --> align diff = " << align_diff();
290 void take() const { Ops::take(this); }
291 void release() const { Ops::release(this); }
294 Region_handler_template operator + (long offset) const throw()
295 { Region_handler_template n = *this; n._offs += offset; return n; }
297 void unmap(l4_addr_t va, l4_addr_t ds_offs, l4_umword_t size) const throw()
298 { Ops::unmap(this, va, ds_offs, size); }
300 void free(l4_addr_t start, l4_umword_t size) const throw()
301 { Ops::free(this, start, size); }
303 int map(l4_addr_t adr, Region const &r, bool writable, Map_result *result) const
304 { return Ops::map(this, adr, r, writable, result); }
309 typedef Romain::Region_handler_template<L4::Cap<L4Re::Dataspace>, Romain::Region_ops> Region_handler;
315 typedef l4_umword_t Map_result;
316 static int map(Region_handler const *h, l4_addr_t addr,
317 L4Re::Util::Region const &r, bool writable,
318 l4_umword_t *result);
320 static void unmap(Region_handler const *h, l4_addr_t vaddr,
321 l4_addr_t offs, l4_umword_t size);
323 static void free(Region_handler const *h, l4_addr_t start, l4_umword_t size);
325 static void take(Region_handler const *h);
326 static void release(Region_handler const *h);
330 /* Note to self: L4Re's Region_map is a cxx::avl_map<> that maps from a Region key to
331 * Region_handler data. It can be indexed using the find() funtion that takes an l4_addr_t
332 * and returns the respective tree node. The node's entries are:
334 * node->first -> the Region object
335 * node->second -> the Region_handler object
338 : public L4Re::Util::Region_map<Romain::Region_handler,
341 pthread_mutex_t _mtx;
343 typedef L4Re::Util::Region_map<Romain::Region_handler, Romain::Allocator> Base;
344 enum { Invalid_inst = ~0U };
346 void activate(l4_umword_t inst)
348 pthread_mutex_lock(&_mtx);
349 _check(_active_instance != Invalid_inst, "rm already used, lock!!!");
350 _active_instance = inst;
355 _active_instance = Invalid_inst;
356 pthread_mutex_unlock(&_mtx);
360 * Initialization, see loader/server/src/region.cc
364 l4_addr_t remote_to_local(l4_addr_t remote, l4_umword_t inst)
366 Base::Node n = find(remote);
369 if (n->second.local_region(inst).start() == 0)
370 lazy_map_region(n, inst);
371 l4_addr_t offs = remote - n->first.start();
372 return n->second.local_region(inst).start() + offs;
379 bool copy_existing_mapping(Romain::Region_handler& r,
382 bool writepf = false) const
385 if (orig_id == inst_id) return true;
387 L4::Cap<L4Re::Dataspace> mem;
388 l4_umword_t size = r.local_region(orig_id).size();
390 DEBUG() << "copy existing: " << std::hex << r.alignment()
391 << " diff " << r.align_diff();
392 l4_addr_t a = Romain::Region_map::allocate_and_attach(&mem, size,
396 _check(a == 0, "DS allocation failed");
399 DEBUG() << "COPY: DS " << std::hex << mem.cap() << " SIZE " << size
400 << " attached @ " << a;
401 DEBUG() << "memcpy(" << (void*)a << ", "
402 << (void*)r.local_region(orig_id).start()
403 << ", " << size << ")";
405 memcpy((void*)a, (void*)r.local_region(orig_id).start(), size);
407 r.set_local_region(inst_id, Romain::Region(a, a+size-1));
408 r.local_memory(inst_id, mem);
415 * Allocate a master-local dataspace object.
418 allocate_ds(L4::Cap<L4Re::Dataspace> *cap, l4_umword_t size)
420 l4_umword_t flags = 0;
421 if (size >= (L4_SUPERPAGESIZE >> 2)) { // use super pages from 1 MB...
422 flags |= L4Re::Mem_alloc::Super_pages;
423 flags |= L4Re::Mem_alloc::Continuous;
426 *cap = L4Re::Util::cap_alloc.alloc<L4Re::Dataspace>();
427 _check(!cap->is_valid(), "error allocating DS capability");
429 int error = L4Re::Env::env()->mem_alloc()->alloc(size, *cap, flags);
430 _check(error != 0, "error allocating memory");
435 attach_aligned(L4::Cap<L4Re::Dataspace> const * ds, l4_umword_t size,
436 l4_umword_t offs = 0, l4_umword_t flags = 0,
437 l4_umword_t align = L4_PAGESHIFT, l4_umword_t aligndiff = 0)
440 l4_addr_t search_size = size + aligndiff;
442 l4_umword_t r = L4Re::Env::env()->rm()->reserve_area(&a, search_size,
443 L4Re::Rm::Search_addr | flags,
445 _check(r != 0, "reserve area failed");
446 DEBUG() << std::hex << "Reserved area at " << a << " and will attach to "
452 r = L4Re::Env::env()->rm()->attach(&a, size, L4Re::Rm::In_area | L4Re::Rm::Eager_map | flags,
454 _check(r != 0, "attach error");
455 _check(a != tmp_a, "did not attach to correct location");
457 DEBUG() << "att: " << (void*)a;
459 r = L4Re::Env::env()->rm()->free_area(a);
460 _check(r != 0, "free area failed");
467 * Allocate and attach a master-local dataspace object.
470 allocate_and_attach(L4::Cap<L4Re::Dataspace> *cap, l4_umword_t size,
471 l4_umword_t offs = 0, l4_umword_t flags = 0,
472 l4_umword_t align = L4_PAGESHIFT,
473 l4_umword_t aligndiff = 0)
475 Romain::Region_map::allocate_ds(cap, size);
477 return Romain::Region_map::attach_aligned(cap, size, offs,
484 * Check if a node already has a mapping to one of the replicas. If so,
485 * take a shortcut mapping to the next one (determined by inst parameter).
487 bool lazy_map_region(Romain::Region_map::Base::Node &n, l4_umword_t inst, bool writepf=false);
490 l4_umword_t _active_instance;
493 * Determine the index of the first instance that already holds a
494 * valid mapping for an existing region.
496 int find_existing_region(Romain::Region_map::Base::Node n) const
499 for (ret = 0; ret < static_cast<l4_mword_t>(Romain::MAX_REPLICAS); ++ret) {
500 if (n->second.local_region(ret).start())
508 * Every DS we attach to the client is also attached locally within
509 * the master process.
511 void *attach_locally(void* addr, l4_umword_t size, Romain::Region_handler *hdlr,
512 l4_umword_t flags = None, l4_uint8_t align=L4_PAGESHIFT);
515 void *attach(void* addr, l4_umword_t size, Romain::Region_handler const &hdlr,
516 l4_umword_t flags = None, l4_uint8_t align=L4_PAGESHIFT, bool shared = false);
518 l4_addr_t attach_area(l4_addr_t addr, l4_umword_t size, l4_umword_t flags = None,
519 l4_uint8_t align = L4_PAGESHIFT) throw()
521 DEBUG() << "attach_area(" << std::hex << addr << ", " << size << ", " << flags << ")";
522 return Base::attach_area(addr, size, flags, align);
526 bool detach_area(l4_addr_t addr) throw()
528 DEBUG() << "detach_area(" << std::hex << addr << ")";
529 return Base::detach_area(addr);
532 Base::Node find(Base::Key_type const& k) const throw()
534 Base::Node n = Base::find(k);
535 DEBUGf(Romain::Log::Memory) << GREEN << "find(" << std::hex << k.start() << "): " << NOCOLOR
536 << "addr " << (n ? n->first.start() : 0xF00)
537 << " memcap " << (n ? n->second.client_cap_idx() : 0xF00);
541 int detach(void* addr, l4_umword_t size, l4_umword_t flags,
542 L4Re::Util::Region* reg, Romain::Region_handler* h);
545 * Copy the content of all writable regions of instance 'from' to
546 * the respective regions of instance 'to'.
548 void replicate(l4_umword_t from, l4_umword_t to)
550 for (auto i = begin(); i != end(); ++i) {
551 // if the region is writeable and is already mapped
552 if (!i->second.is_ro() && i->second.local_region(from).start() != 0) {
553 l4_umword_t size = i->first.end() - i->first.start() - 1;
554 memcpy((void*)i->second.local_region(to).start(),
555 (void*)i->second.local_region(from).start(), size);
561 static char const* print_mapping(Node n, l4_umword_t id)
564 s << "[" << std::hex << n->second.local_region(id).start()
565 << " - " << n->second.local_region(id).end() << "] ==> "
566 << "[" << n->first.start() << " - " << n->first.end() << "]";
568 s << " DS " << std::hex << n->second.local_memory(id).cap();
570 return s.str().c_str();
575 class Region_map_server
578 typedef L4::Cap<L4Re::Dataspace> Dataspace;
579 enum { Have_find = true };
581 static int validate_ds(void *, L4::Ipc::Snd_fpage const &ds_cap,
582 l4_umword_t, L4::Cap<L4Re::Dataspace> *ds)
585 * XXX We need to check if actually a cap was received.
586 * However, the default check for ds_cap.local_id_received()
587 * fails here, because we are only proxying this call.
589 *ds = L4::Cap<L4Re::Dataspace>(ds_cap.base());
594 //static l4_umword_t find_res(L4::Cap<void> const &ds) { return ds.cap(); }
595 static l4_umword_t find_res(l4_cap_idx_t cap) { return cap; }