]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/plr/server/src/memory
update
[l4.git] / l4 / pkg / plr / server / src / memory
1 // vim: ft=cpp
2
3 /*
4  * memory --
5  *
6  *     Memory management classes, mirroring the L4 RM's ones.
7  *
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.
13  */
14
15 /*
16  * Memory management (e.g., region manager)
17  */
18
19 #pragma once
20
21 #include <cstdlib>
22 #include <vector>
23
24 #include <l4/re/env>
25 #include <l4/sys/kip>
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>
30
31 #include <l4/sys/kdebug.h>
32
33 #include "constants.h"
34 #include "log"
35 #include "exceptions"
36
37 namespace Romain
38 {
39         class Stack
40         {
41                 l4_addr_t _sp;
42                 
43                 public:
44                         Stack(l4_addr_t sp)
45                                 : _sp(sp)
46                         { }
47
48                         l4_addr_t sp() { return _sp; }
49
50
51                         /*
52                          * Push a value to the stack
53                          */
54                         template <typename T>
55                         l4_addr_t push(T value)
56                         {
57                                 _sp -= sizeof(T);
58                                 *(T*)_sp = value;
59                                 return _sp;
60                         }
61
62
63                         /*
64                          * Push a buffer to stack.
65                          *
66                          * Returns difference to previous stack ptr. This diff may be
67                          * different from the len parameter for alignment reasons.
68                          */
69                         template <typename T>
70                         unsigned push_data(T* buf, unsigned len)
71                         {
72                                 l4_addr_t orig_sp = _sp;
73                                 unsigned space = len * sizeof(T);
74                                 _sp -= space;
75                                 _sp &= ~0x03; // word alignment XXX
76                                 memcpy((char*)_sp, buf, space);
77                                 return orig_sp - _sp;
78                         }
79
80         };
81
82         /*
83          * Allocator
84          */
85         template <typename TYPE>
86                 struct Allocator
87                 {
88                         enum { can_free = true };
89
90                         Allocator() throw() {}
91                         Allocator(Romain::Allocator<TYPE> const &) throw() {}
92                         ~Allocator() throw() {}
93
94                         TYPE *alloc() throw() { return static_cast<TYPE*>(::malloc(sizeof(TYPE))); }
95                         void free(void* ptr) throw() { ::free(ptr); }
96
97                 };
98
99
100         class Region : public L4Re::Util::Region
101         {
102                 public:
103                         typedef L4Re::Util::Region Base;
104
105                         Region(l4_addr_t addr = 0) throw()
106                                 : Base(addr)
107                         {}
108
109                         Region(l4_addr_t start, l4_addr_t end) throw()
110                                 : Base(start, end)
111                         {}
112
113                         void touch_rw()
114                         {
115 #if 1
116                                 DEBUG() << "touching 0x" << std::hex << this->start()
117                                         << " + " << this->size() << " rw'able";
118 #endif
119                                 l4_touch_rw((void*)this->start(), this->size());
120                         }
121         };
122
123
124         /*
125          * Romain Memory region handler. This handler is different from L4Re's region
126          * handler in that it can manage multiple source mappings.
127          *
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.
131          */
132         template <typename DS, typename OPS>
133         class Region_handler_template
134         {
135                 public:
136                         typedef L4::Cap<L4Re::Dataspace> DSCAP;
137                         typedef DS Dataspace;
138                         typedef OPS Ops;
139                         typedef typename OPS::Map_result Map_result;
140
141                         enum RegionWritableType {
142                                 Read_only,
143                                 Read_only_emulate_write,
144                                 Writable,
145                         };
146
147                 private:
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)
156
157                 public:
158
159                         Region_handler_template()
160                                 : _cap(L4_INVALID_CAP),  _offs(0), _flags(),
161                               _row(Romain::Region_handler_template<DS, OPS>::Read_only),
162                               _shared(false)
163                         {}
164
165
166                         Region_handler_template(const Region_handler_template& o)
167                                 : _cap(o.client_cap_idx()),
168                                   _offs(o.offset()),
169                                   _flags(o.flags()),
170                                   _row(o.writable()),
171                                   _shared(o.shared())
172                         {
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);
176                                 }
177                         }
178
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)
182                         {
183                                 _local_regions[0] = local_reg;
184                                 _mem[0]           = cap;
185                                 for (unsigned i = 1; i < Romain::MAX_REPLICAS; ++i)
186                                         _mem[i] = DSCAP();
187
188                                 /*
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.
193                                  */
194                                 if (flags & L4Re::Rm::Read_only) {
195                                         writable(Romain::Region_handler_template<DS, OPS>::Read_only);
196                                 } else {
197                                         writable(Romain::Region_handler_template<DS, OPS>::Writable);
198                                 }
199                         }
200
201                         unsigned            local_region_count() const { return MAX_REPLICAS; }
202                         Romain::Region const & local_region(unsigned idx) const { return _local_regions[idx]; }
203
204                         void                writable(RegionWritableType rw) { _row = rw; }
205                         RegionWritableType  writable() const { return _row; }
206
207                         /*
208                          * Set a value for the mapping established for a given combination
209                          * of Region and Instance.
210                          */
211                         void set_local_region(unsigned idx, Romain::Region const & r)
212                         {
213                                 _check(idx >= Romain::MAX_REPLICAS, "invalid instance");
214                                 _local_regions[idx] = r;
215                         }
216
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; }
225
226                         bool         shared() const throw()                 { return _shared; }
227                         void         shared(bool yn) { _shared = yn; }
228
229                         void         take() const                           { Ops::take(this); }
230                         void         release() const                        { Ops::release(this); }
231
232
233                         Region_handler_template operator + (long offset) throw()
234                         { Region_handler_template n = *this; n._offs += offset; return n; }
235
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); }
238
239                         void free(l4_addr_t start, unsigned long size) const throw()
240                         { Ops::free(this, start, size); }
241
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); }
244
245         };
246
247         class Region_ops;
248         typedef Romain::Region_handler_template<L4::Cap<L4Re::Dataspace>, Romain::Region_ops> Region_handler;
249
250
251         class Region_ops
252         {
253                 public:
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);
258
259                         static void unmap(Region_handler const *h, l4_addr_t vaddr,
260                                           l4_addr_t offs, unsigned long size);
261
262                         static void free(Region_handler const *h, l4_addr_t start, unsigned long size);
263
264                         static void take(Region_handler const *h);
265                         static void release(Region_handler const *h);
266         };
267
268
269         class Region_map
270                 : public L4Re::Util::Region_map<Romain::Region_handler,
271                                                 Romain::Allocator>
272         {
273                 public:
274                         typedef L4Re::Util::Region_map<Romain::Region_handler, Romain::Allocator> Base;
275                         enum { Invalid_inst = ~0U };
276
277                         void activate(unsigned inst)
278                         {
279                                 _check(_active_instance != Invalid_inst, "rm already used, lock!!!");
280                                 _active_instance = inst;
281                         }
282
283                         void release()
284                         {
285                                 _active_instance = Invalid_inst;
286                         }
287
288                         /*
289                          * Initialization, see loader/server/src/region.cc
290                          */
291                         Region_map();
292
293                         l4_addr_t remote_to_local(l4_addr_t remote, unsigned inst)
294                         {
295                                 Base::Node n = find(remote);
296
297                                 if (n) {
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;
302                                 }
303
304                                 return ~0UL;
305                         }
306
307
308                         bool copy_existing_mapping(Romain::Region_handler& r,
309                                                    unsigned orig_id,
310                                                    unsigned inst_id) const
311                         {
312                                 if (orig_id == inst_id) return true;
313
314                                 L4::Cap<L4Re::Dataspace> mem;
315                                 unsigned long size = r.local_region(orig_id).size();
316
317                                 l4_addr_t a = Romain::Region_map::allocate_and_attach(&mem, size);
318                                 _check(a == 0, "DS allocation failed");
319
320                                 memcpy((void*)a, (void*)r.local_region(orig_id).start(), size);
321
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);
326
327                                 return true;
328                         }
329
330
331                         /*
332                          * Allocate a master-local dataspace object.
333                          */
334                         static void
335                         allocate_ds(L4::Cap<L4Re::Dataspace> *cap, unsigned size)
336                         {
337                                 *cap = L4Re::Util::cap_alloc.alloc<L4Re::Dataspace>();
338                                 _check(!cap->is_valid(), "error allocating DS capability");
339
340                                 int error = L4Re::Env::env()->mem_alloc()->alloc(size, *cap);
341                                 _check(error != 0, "error allocating memory");
342                         }
343
344
345                         /*
346                          * Allocate and attach a master-local dataspace object.
347                          */
348                         static l4_addr_t
349                         allocate_and_attach(L4::Cap<L4Re::Dataspace> *cap, unsigned size,
350                                                 unsigned offs = 0, unsigned align = L4_PAGESHIFT)
351                         {
352                                 Romain::Region_map::allocate_ds(cap, size);
353
354                                 l4_addr_t a = 0;
355                                 int error = L4Re::Env::env()->rm()->attach(&a, size, L4Re::Rm::Search_addr, *cap, offs, align);
356                                 _check(error != 0, "attach failed");
357
358                                 return a;
359                         }
360
361
362                         /*
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).
365                          */
366                         bool lazy_map_region(Romain::Region_map::Base::Node &n, unsigned inst);
367
368                 private:
369                         unsigned _active_instance;
370
371                         /*
372                          * Determine the index of the first instance that already holds a
373                          * valid mapping for an existing region.
374                          */
375                         int find_existing_region(Romain::Region_map::Base::Node n) const
376                         {
377                                 int ret;
378                                 for (ret = 0; ret < Romain::MAX_REPLICAS; ++ret) {
379                                         if (n->second.local_region(ret).start())
380                                                 return ret;
381                                 }
382                                 return -1;
383                         }
384
385                 public:
386                         /*
387                          * Every DS we attach to the client is also attached locally within
388                          * the master process.
389                          */
390                         void *attach_locally(void* addr, unsigned long size, Romain::Region_handler *hdlr,
391                                              unsigned flags = None, unsigned char align=L4_PAGESHIFT);
392
393
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);
396
397                         /*
398                          * Copy the content of all writable regions of instance 'from' to
399                          * the respective regions of instance 'to'.
400                          */
401                         void replicate(unsigned from, unsigned to)
402                         {
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);
408                                         }
409                                 }
410                         }
411         };
412
413
414         class Region_map_server
415         {
416                 public:
417                         typedef L4::Cap<L4Re::Dataspace> Dataspace;
418                         enum { Have_find = true };
419
420                         static int validate_ds(L4::Ipc::Snd_fpage const &ds_cap,
421                                                unsigned, L4::Cap<L4Re::Dataspace> *ds)
422                         {
423                                 /*
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.
427                                  */
428                                 *ds = L4::Cap<L4Re::Dataspace>(ds_cap.base());
429                                 return L4_EOK;
430                         }
431
432
433                         static l4_umword_t find_res(L4::Cap<void> const &ds) { return ds.cap(); }
434         };
435 }