]> 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-2012 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;
149                         Romain::Region            _local_regions[Romain::MAX_REPLICAS];
150                         l4_addr_t                _offs;
151                         DS                       _mem[Romain::MAX_REPLICAS];
152                         unsigned char            _flags;
153                         RegionWritableType       _row;
154                         bool                     _shared;
155
156                 public:
157
158                         Region_handler_template()
159                                 : _cap(L4_INVALID_CAP),  _offs(0), _flags(),
160                               _row(Romain::Region_handler_template<DS, OPS>::Read_only),
161                               _shared(false)
162                         {}
163
164
165                         Region_handler_template(const Region_handler_template& o)
166                                 : _cap(o.client_cap_idx()),
167                                   _offs(o.offset()),
168                                   _flags(o.flags()),
169                                   _row(o.writable()),
170                                   _shared(o.shared())
171                         {
172                                 for (unsigned i = 0; i < o.local_region_count(); ++i) {
173                                         _local_regions[i] = o.local_region(i);
174                                         _mem[i]           = o.memory(i);
175                                 }
176                         }
177
178                         Region_handler_template(DSCAP cap, l4_cap_idx_t client_cap = L4_INVALID_CAP, l4_addr_t offset = 0,
179                                                 unsigned flags = 0, Romain::Region local_reg = Romain::Region(0,0))
180                                 : _cap(client_cap), _offs(offset), _flags(flags), _shared(false)
181                         {
182                                 _local_regions[0] = local_reg;
183                                 _mem[0]           = cap;
184                                 for (unsigned i = 1; i < Romain::MAX_REPLICAS; ++i)
185                                         _mem[i] = DSCAP();
186
187                                 /*
188                                  * Yes, this looks like a copy of the RO flag. But in our case, this is actually
189                                  * a tri-state. A page may be writable but mapped read-only if the mapping is
190                                  * shared memory. This fact is however established by someone else (e.g., the syscall
191                                  * observer) and here we just set one of the two default states.
192                                  */
193                                 if (flags & L4Re::Rm::Read_only) {
194                                         writable(Romain::Region_handler_template<DS, OPS>::Read_only);
195                                 } else {
196                                         writable(Romain::Region_handler_template<DS, OPS>::Writable);
197                                 }
198                         }
199
200                         unsigned            local_region_count() const { return MAX_REPLICAS; }
201                         Romain::Region const & local_region(unsigned idx) const { return _local_regions[idx]; }
202
203                         void                writable(RegionWritableType rw) { _row = rw; }
204                         RegionWritableType  writable() const { return _row; }
205
206                         /*
207                          * Set a value for the mapping established for a given combination
208                          * of Region and Instance.
209                          */
210                         void set_local_region(unsigned idx, Romain::Region const & r)
211                         {
212                                 _check(idx >= Romain::MAX_REPLICAS, "invalid instance");
213                                 _local_regions[idx] = r;
214                         }
215
216                         //DS const &memory() const throw() { return _mem; }
217                         DS const &   memory(unsigned idx = 0) const throw() { return _mem[idx]; }
218                         void         memory(unsigned idx, DS const & m)     { _mem[idx] = m; }
219                         l4_cap_idx_t client_cap_idx() const throw()         { return _cap; }
220                         l4_addr_t    offset() const throw()                 { return _offs; }
221                         void         offset(l4_addr_t o)                    { _offs = o; }
222                         l4_addr_t    is_ro() const throw()                  { return _flags & L4Re::Rm::Read_only; }
223                         unsigned     flags() const throw()                  { return _flags; }
224
225                         bool         shared() const throw()                 { return _shared; }
226                         void         shared(bool yn) { _shared = yn; }
227
228                         void         take() const                           { Ops::take(this); }
229                         void         release() const                        { Ops::release(this); }
230
231
232                         Region_handler_template operator + (long offset) throw()
233                         { Region_handler_template n = *this; n._offs += offset; return n; }
234
235                         void unmap(l4_addr_t va, l4_addr_t ds_offs, unsigned long size) const throw()
236                         { Ops::unmap(this, va, ds_offs, size); }
237
238                         void free(l4_addr_t start, unsigned long size) const throw()
239                         { Ops::free(this, start, size); }
240
241                         int map(l4_addr_t adr, Region const &r, bool writable, Map_result *result) const
242                         { return Ops::map(this, adr, r, writable, result); }
243
244         };
245
246         class Region_ops;
247         typedef Romain::Region_handler_template<L4::Cap<L4Re::Dataspace>, Romain::Region_ops> Region_handler;
248
249
250         class Region_ops
251         {
252                 public:
253                         typedef l4_umword_t Map_result;
254                         static int  map(Region_handler const *h, l4_addr_t addr,
255                                         L4Re::Util::Region const &r, bool writable,
256                                         l4_umword_t *result);
257
258                         static void unmap(Region_handler const *h, l4_addr_t vaddr,
259                                           l4_addr_t offs, unsigned long size);
260
261                         static void free(Region_handler const *h, l4_addr_t start, unsigned long size);
262
263                         static void take(Region_handler const *h);
264                         static void release(Region_handler const *h);
265         };
266
267
268         class Region_map
269                 : public L4Re::Util::Region_map<Romain::Region_handler,
270                                                 Romain::Allocator>
271         {
272                 public:
273                         typedef L4Re::Util::Region_map<Romain::Region_handler, Romain::Allocator> Base;
274                         enum { Invalid_inst = ~0U };
275
276                         void activate(unsigned inst)
277                         {
278                                 _check(_active_instance != Invalid_inst, "rm already used, lock!!!");
279                                 _active_instance = inst;
280                         }
281
282                         void release()
283                         {
284                                 _active_instance = Invalid_inst;
285                         }
286
287                         /*
288                          * Initialization, see loader/server/src/region.cc
289                          */
290                         Region_map();
291
292                         l4_addr_t remote_to_local(l4_addr_t remote, unsigned inst)
293                         {
294                                 Base::Node n = find(remote);
295
296                                 if (n) {
297                                         if (n->second.local_region(inst).start() == 0)
298                                                 lazy_map_region(n, inst);
299                                         l4_addr_t offs = remote - n->first.start();
300                                         return n->second.local_region(inst).start() + offs;
301                                 }
302
303                                 return ~0UL;
304                         }
305
306
307                         bool copy_existing_mapping(Romain::Region_handler& r,
308                                                    unsigned orig_id,
309                                                    unsigned inst_id) const
310                         {
311                                 if (orig_id == inst_id) return true;
312
313                                 L4::Cap<L4Re::Dataspace> mem = L4Re::Util::cap_alloc.alloc<L4Re::Dataspace>();
314                                 _check(!mem.is_valid(), "could not allocate ds cap?");
315
316                                 unsigned long size = r.local_region(orig_id).size();
317                                 int ret = L4Re::Env::env()->mem_alloc()->alloc(size, mem);
318                                 _check(ret != 0, "memory allocation failed");
319
320                                 l4_addr_t a = 0;
321                                 ret = L4Re::Env::env()->rm()->attach(&a, size,
322                                                                      L4Re::Rm::Search_addr,
323                                                                      mem, 0);
324                                 _check(ret != 0, "attach failed");
325                                 memcpy((void*)a, (void*)r.local_region(orig_id).start(), size);
326
327                                 //DEBUG() << "COPY: DS " << std::hex << mem.cap() << " SIZE " << size
328                                 //      << " attached @ " << a;
329                                 r.set_local_region(inst_id, Romain::Region(a, a+size-1));
330                                 r.memory(inst_id, mem);
331
332                                 return true;
333                         }
334
335
336                         /*
337                          * Check if a node already has a mapping to one of the replicas. If so,
338                          * take a shortcut mapping to the next one (determined by inst parameter).
339                          */
340                         bool lazy_map_region(Romain::Region_map::Base::Node &n, unsigned inst);
341
342                 private:
343                         unsigned _active_instance;
344
345                         /*
346                          * Determine the index of the first instance that already holds a
347                          * valid mapping for an existing region.
348                          */
349                         int find_existing_region(Romain::Region_map::Base::Node n) const
350                         {
351                                 int ret;
352                                 for (ret = 0; ret < Romain::MAX_REPLICAS; ++ret) {
353                                         if (n->second.local_region(ret).start())
354                                                 return ret;
355                                 }
356                                 return -1;
357                         }
358
359                 public:
360                         /*
361                          * Every DS we attach to the client is also attached locally within
362                          * the master process.
363                          */
364                         void *attach_locally(void* addr, unsigned long size, Romain::Region_handler *hdlr,
365                                              unsigned flags = None, unsigned char align=L4_PAGESHIFT);
366
367
368                         void *attach(void* addr, unsigned long size, Romain::Region_handler const &hdlr,
369                                      unsigned flags = None, unsigned char align=L4_PAGESHIFT, bool shared = false);
370
371                         /*
372                          * Copy the content of all writable regions of instance 'from' to
373                          * the respective regions of instance 'to'.
374                          */
375                         void replicate(unsigned from, unsigned to)
376                         {
377                                 for (Base::Const_iterator i = begin(); i != end(); ++i) {
378                                         if (!i->second.is_ro()) {
379                                                 unsigned size = i->first.end() - i->first.start() - 1;
380                                                 memcpy((void*)i->second.local_region(to).start(),
381                                                        (void*)i->second.local_region(from).start(), size);
382                                         }
383                                 }
384                         }
385         };
386
387
388         class Region_map_server
389         {
390                 public:
391                         typedef L4::Cap<L4Re::Dataspace> Dataspace;
392                         enum { Have_find = true };
393
394                         static int validate_ds(L4::Ipc::Snd_fpage const &ds_cap,
395                                                unsigned, L4::Cap<L4Re::Dataspace> *ds)
396                         {
397                                 /*
398                                  * XXX  We need to check if actually a cap was received.
399                                  * However, the default check for ds_cap.local_id_received()
400                                  * fails here, because we are only proxying this call.
401                                  */
402                                 *ds = L4::Cap<L4Re::Dataspace>(ds_cap.base());
403                                 return L4_EOK;
404                         }
405
406
407                         static l4_umword_t find_res(L4::Cap<void> const &ds) { return ds.cap(); }
408         };
409 }