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)
26 #include <l4/util/util.h>
27 #include <l4/re/util/cap_alloc>
28 #include <l4/re/util/region_mapping>
29 #include <l4/re/util/dataspace_svr>
31 #include <l4/sys/kdebug.h>
33 #include "constants.h"
48 l4_addr_t sp() { return _sp; }
52 * Push a value to the stack
55 l4_addr_t push(T value)
64 * Push a buffer to stack.
66 * Returns difference to previous stack ptr. This diff may be
67 * different from the len parameter for alignment reasons.
70 unsigned push_data(T* buf, unsigned len)
72 l4_addr_t orig_sp = _sp;
73 unsigned space = len * sizeof(T);
75 _sp &= ~0x03; // word alignment XXX
76 memcpy((char*)_sp, buf, space);
85 template <typename TYPE>
88 enum { can_free = true };
90 Allocator() throw() {}
91 Allocator(Romain::Allocator<TYPE> const &) throw() {}
92 ~Allocator() throw() {}
94 TYPE *alloc() throw() { return static_cast<TYPE*>(::malloc(sizeof(TYPE))); }
95 void free(void* ptr) throw() { ::free(ptr); }
100 class Region : public L4Re::Util::Region
103 typedef L4Re::Util::Region Base;
105 Region(l4_addr_t addr = 0) throw()
109 Region(l4_addr_t start, l4_addr_t end) throw()
116 DEBUG() << "touching 0x" << std::hex << this->start()
117 << " + " << this->size() << " rw'able";
119 l4_touch_rw((void*)this->start(), this->size());
125 * Romain Memory region handler. This handler is different from L4Re's region
126 * handler in that it can manage multiple source mappings.
128 * Multiple sources are used for handling multiple replicas which have
129 * write access to a memory region. In this case we need to establish a
130 * mapping per replica.
132 template <typename DS, typename OPS>
133 class Region_handler_template
136 typedef L4::Cap<L4Re::Dataspace> DSCAP;
137 typedef DS Dataspace;
139 typedef typename OPS::Map_result Map_result;
141 enum RegionWritableType {
143 Read_only_emulate_write,
148 l4_cap_idx_t _cap; // capability of the underlying dataspace
149 Romain::Region _local_regions[Romain::MAX_REPLICAS]; // replicated regions (writable regions exist in
150 // multiple instances)
151 l4_addr_t _offs; // offset in DS
152 DS _mem[Romain::MAX_REPLICAS]; // DS capabilities of underlying dataspaces
153 unsigned char _flags; // region flags
154 RegionWritableType _row; // rw state
155 bool _shared; // is DS shared across all replicas (true for internal determinism's lock info page)
159 Region_handler_template()
160 : _cap(L4_INVALID_CAP), _offs(0), _flags(),
161 _row(Romain::Region_handler_template<DS, OPS>::Read_only),
166 Region_handler_template(const Region_handler_template& o)
167 : _cap(o.client_cap_idx()),
173 for (unsigned i = 0; i < o.local_region_count(); ++i) {
174 _local_regions[i] = o.local_region(i);
175 _mem[i] = o.memory(i);
179 Region_handler_template(DSCAP cap, l4_cap_idx_t client_cap = L4_INVALID_CAP, l4_addr_t offset = 0,
180 unsigned flags = 0, Romain::Region local_reg = Romain::Region(0,0))
181 : _cap(client_cap), _offs(offset), _flags(flags), _shared(false)
183 _local_regions[0] = local_reg;
185 for (unsigned i = 1; i < Romain::MAX_REPLICAS; ++i)
189 * Yes, this looks like a copy of the RO flag. But in our case, this is actually
190 * a tri-state. A page may be writable but mapped read-only if the mapping is
191 * shared memory. This fact is however established by someone else (e.g., the syscall
192 * observer) and here we just set one of the two default states.
194 if (flags & L4Re::Rm::Read_only) {
195 writable(Romain::Region_handler_template<DS, OPS>::Read_only);
197 writable(Romain::Region_handler_template<DS, OPS>::Writable);
201 unsigned local_region_count() const { return MAX_REPLICAS; }
202 Romain::Region const & local_region(unsigned idx) const { return _local_regions[idx]; }
204 void writable(RegionWritableType rw) { _row = rw; }
205 RegionWritableType writable() const { return _row; }
208 * Set a value for the mapping established for a given combination
209 * of Region and Instance.
211 void set_local_region(unsigned idx, Romain::Region const & r)
213 _check(idx >= Romain::MAX_REPLICAS, "invalid instance");
214 _local_regions[idx] = r;
217 //DS const &memory() const throw() { return _mem; }
218 DS const & memory(unsigned idx = 0) const throw() { return _mem[idx]; }
219 void memory(unsigned idx, DS const & m) { _mem[idx] = m; }
220 l4_cap_idx_t client_cap_idx() const throw() { return _cap; }
221 l4_addr_t offset() const throw() { return _offs; }
222 void offset(l4_addr_t o) { _offs = o; }
223 l4_addr_t is_ro() const throw() { return _flags & L4Re::Rm::Read_only; }
224 unsigned flags() const throw() { return _flags; }
226 bool shared() const throw() { return _shared; }
227 void shared(bool yn) { _shared = yn; }
229 void take() const { Ops::take(this); }
230 void release() const { Ops::release(this); }
233 Region_handler_template operator + (long offset) throw()
234 { Region_handler_template n = *this; n._offs += offset; return n; }
236 void unmap(l4_addr_t va, l4_addr_t ds_offs, unsigned long size) const throw()
237 { Ops::unmap(this, va, ds_offs, size); }
239 void free(l4_addr_t start, unsigned long size) const throw()
240 { Ops::free(this, start, size); }
242 int map(l4_addr_t adr, Region const &r, bool writable, Map_result *result) const
243 { return Ops::map(this, adr, r, writable, result); }
248 typedef Romain::Region_handler_template<L4::Cap<L4Re::Dataspace>, Romain::Region_ops> Region_handler;
254 typedef l4_umword_t Map_result;
255 static int map(Region_handler const *h, l4_addr_t addr,
256 L4Re::Util::Region const &r, bool writable,
257 l4_umword_t *result);
259 static void unmap(Region_handler const *h, l4_addr_t vaddr,
260 l4_addr_t offs, unsigned long size);
262 static void free(Region_handler const *h, l4_addr_t start, unsigned long size);
264 static void take(Region_handler const *h);
265 static void release(Region_handler const *h);
270 : public L4Re::Util::Region_map<Romain::Region_handler,
274 typedef L4Re::Util::Region_map<Romain::Region_handler, Romain::Allocator> Base;
275 enum { Invalid_inst = ~0U };
277 void activate(unsigned inst)
279 _check(_active_instance != Invalid_inst, "rm already used, lock!!!");
280 _active_instance = inst;
285 _active_instance = Invalid_inst;
289 * Initialization, see loader/server/src/region.cc
293 l4_addr_t remote_to_local(l4_addr_t remote, unsigned inst)
295 Base::Node n = find(remote);
298 if (n->second.local_region(inst).start() == 0)
299 lazy_map_region(n, inst);
300 l4_addr_t offs = remote - n->first.start();
301 return n->second.local_region(inst).start() + offs;
308 bool copy_existing_mapping(Romain::Region_handler& r,
310 unsigned inst_id) const
312 if (orig_id == inst_id) return true;
314 L4::Cap<L4Re::Dataspace> mem;
315 unsigned long size = r.local_region(orig_id).size();
317 l4_addr_t a = Romain::Region_map::allocate_and_attach(&mem, size);
318 _check(a == 0, "DS allocation failed");
320 memcpy((void*)a, (void*)r.local_region(orig_id).start(), size);
322 //DEBUG() << "COPY: DS " << std::hex << mem.cap() << " SIZE " << size
323 // << " attached @ " << a;
324 r.set_local_region(inst_id, Romain::Region(a, a+size-1));
325 r.memory(inst_id, mem);
332 * Allocate a master-local dataspace object.
335 allocate_ds(L4::Cap<L4Re::Dataspace> *cap, unsigned size)
337 *cap = L4Re::Util::cap_alloc.alloc<L4Re::Dataspace>();
338 _check(!cap->is_valid(), "error allocating DS capability");
340 int error = L4Re::Env::env()->mem_alloc()->alloc(size, *cap);
341 _check(error != 0, "error allocating memory");
346 * Allocate and attach a master-local dataspace object.
349 allocate_and_attach(L4::Cap<L4Re::Dataspace> *cap, unsigned size,
350 unsigned offs = 0, unsigned align = L4_PAGESHIFT)
352 Romain::Region_map::allocate_ds(cap, size);
355 int error = L4Re::Env::env()->rm()->attach(&a, size, L4Re::Rm::Search_addr, *cap, offs, align);
356 _check(error != 0, "attach failed");
363 * Check if a node already has a mapping to one of the replicas. If so,
364 * take a shortcut mapping to the next one (determined by inst parameter).
366 bool lazy_map_region(Romain::Region_map::Base::Node &n, unsigned inst);
369 unsigned _active_instance;
372 * Determine the index of the first instance that already holds a
373 * valid mapping for an existing region.
375 int find_existing_region(Romain::Region_map::Base::Node n) const
378 for (ret = 0; ret < Romain::MAX_REPLICAS; ++ret) {
379 if (n->second.local_region(ret).start())
387 * Every DS we attach to the client is also attached locally within
388 * the master process.
390 void *attach_locally(void* addr, unsigned long size, Romain::Region_handler *hdlr,
391 unsigned flags = None, unsigned char align=L4_PAGESHIFT);
394 void *attach(void* addr, unsigned long size, Romain::Region_handler const &hdlr,
395 unsigned flags = None, unsigned char align=L4_PAGESHIFT, bool shared = false);
398 * Copy the content of all writable regions of instance 'from' to
399 * the respective regions of instance 'to'.
401 void replicate(unsigned from, unsigned to)
403 for (Base::Const_iterator i = begin(); i != end(); ++i) {
404 if (!i->second.is_ro()) {
405 unsigned size = i->first.end() - i->first.start() - 1;
406 memcpy((void*)i->second.local_region(to).start(),
407 (void*)i->second.local_region(from).start(), size);
414 class Region_map_server
417 typedef L4::Cap<L4Re::Dataspace> Dataspace;
418 enum { Have_find = true };
420 static int validate_ds(L4::Ipc::Snd_fpage const &ds_cap,
421 unsigned, L4::Cap<L4Re::Dataspace> *ds)
424 * XXX We need to check if actually a cap was received.
425 * However, the default check for ds_cap.local_id_received()
426 * fails here, because we are only proxying this call.
428 *ds = L4::Cap<L4Re::Dataspace>(ds_cap.base());
433 static l4_umword_t find_res(L4::Cap<void> const &ds) { return ds.cap(); }