]> 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 #include <sstream>
24
25 #include <l4/re/env>
26 #include <l4/sys/kip>
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>
33
34 #include <l4/sys/kdebug.h>
35
36 #include "constants.h"
37 #include "log"
38 #include "exceptions"
39
40 namespace Romain
41 {
42         class Stack
43         {
44                 l4_addr_t _sp;
45                 
46                 public:
47                         Stack(l4_addr_t sp)
48                                 : _sp(sp)
49                         { }
50
51                         l4_addr_t sp() { return _sp; }
52
53
54                         /*
55                          * Push a value to the stack
56                          */
57                         template <typename T>
58                         l4_addr_t push(T value)
59                         {
60                                 _sp -= sizeof(T);
61                                 *(T*)_sp = value;
62                                 return _sp;
63                         }
64
65
66                         /*
67                          * Push a buffer to stack.
68                          *
69                          * Returns difference to previous stack ptr. This diff may be
70                          * different from the len parameter for alignment reasons.
71                          */
72                         template <typename T>
73                         l4_umword_t push_data(T* buf, l4_umword_t len)
74                         {
75                                 l4_addr_t orig_sp = _sp;
76                                 l4_umword_t space = len * sizeof(T);
77                                 _sp -= space;
78                                 _sp &= ~0x03; // word alignment XXX
79                                 memcpy((char*)_sp, buf, space);
80                                 return orig_sp - _sp;
81                         }
82
83         };
84
85         /*
86          * Allocator
87          */
88         template <typename TYPE>
89                 struct Allocator
90                 {
91                         enum { can_free = true };
92
93                         Allocator() throw() {}
94                         Allocator(Romain::Allocator<TYPE> const &) throw() {}
95                         ~Allocator() throw() {}
96
97                         TYPE *alloc() throw() { return static_cast<TYPE*>(::malloc(sizeof(TYPE))); }
98                         void free(void* ptr) throw() { ::free(ptr); }
99
100                 };
101
102
103         class Region : public L4Re::Util::Region
104         {
105                 public:
106                         typedef L4Re::Util::Region Base;
107
108                         Region(l4_addr_t addr = 0) throw()
109                                 : Base(addr)
110                         {}
111
112                         Region(l4_addr_t start, l4_addr_t end) throw()
113                                 : Base(start, end)
114                         {}
115
116 #if 0
117                         void touch_rw()
118                         {
119 #if 1
120                                 DEBUG() << "touching 0x" << std::hex << this->start()
121                                         << " + " << this->size() << " rw'able";
122 #endif
123                                 l4_touch_rw((void*)this->start(), this->size());
124                         }
125 #endif
126         };
127
128
129         /*
130          * Romain Memory region handler. This handler is different from L4Re's region
131          * handler in that it can manage multiple source mappings.
132          *
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.
136          */
137         template <typename DS, typename OPS>
138         class Region_handler_template
139         {
140                 public:
141                         typedef L4::Cap<L4Re::Dataspace> DSCAP;
142                         typedef DS Dataspace;
143                         typedef OPS Ops;
144                         typedef typename OPS::Map_result Map_result;
145
146                         enum RegionWritableType {
147                                 Read_only,
148                                 Read_only_emulate_write,
149                                 Writable,
150                                 Copy_and_execute,
151                         };
152
153                 private:
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
167
168                 public:
169
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)
174                         {}
175
176
177                         Region_handler_template(const Region_handler_template& o)
178                                 : _cap(o.client_cap_idx()),
179                                   _offs(o.offset()),
180                                   _flags(o.flags()),
181                                   _row(o.writable()),
182                                   _shared(o.shared()),
183                                   _alignment(o.alignment()),
184                                   _align_diff(o.align_diff()),
185                                   _numpf(o.numpf())
186                         {
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);
190                                 }
191                         }
192
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)
197                         {
198                                 _local_regions[0] = local_reg;
199                                 _mem[0]           = cap;
200                                 for (l4_umword_t i = 1; i < Romain::MAX_REPLICAS; ++i)
201                                         _mem[i] = DSCAP();
202
203                                 /*
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.
208                                  */
209                                 if (flags & L4Re::Rm::Read_only) {
210                                         writable(Romain::Region_handler_template<DS, OPS>::Read_only);
211                                 } else {
212                                         writable(Romain::Region_handler_template<DS, OPS>::Writable);
213                                 }
214                         }
215
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]; }
218
219                         void                writable(RegionWritableType rw) { _row = rw; }
220                         RegionWritableType  writable() const { return _row; }
221
222                         /*
223                          * Set a value for the mapping established for a given combination
224                          * of Region and Instance.
225                          */
226                         void set_local_region(l4_umword_t idx, Romain::Region const & r)
227                         {
228                                 _check(idx >= Romain::MAX_REPLICAS, "invalid instance");
229                                 _local_regions[idx] = r;
230                         }
231
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; }
241
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++; }
248
249                         /* Compute the target alignment for attaching locally.
250                          *
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.
255                          */
256                         void alignToAddressAndSize(l4_addr_t address, l4_size_t size)
257                         {
258                                 enum {
259                                         MIN_ALIGNMENT = 
260                                         //12, /* 4 kB -> minimum! */
261                                         //16, /* 64 kB */
262                                             //19, /* 512 kB */
263                                         22, /* 4 MB */
264                                 };
265
266                                 DEBUGf(Romain::Log::Memory) << "alignToAddressAndSize(" << std::hex << address
267                                         << ", " << size << ")";
268
269                                 l4_addr_t min_align = MIN_ALIGNMENT;
270                                 if (size < (1 << MIN_ALIGNMENT)) {
271                                         min_align = l4util_bsr(size);
272                                 }
273                                 DEBUGf(Romain::Log::Memory) << "Minimum alignment: " << std::hex << min_align;
274
275                                 if (min_align > L4_SUPERPAGESHIFT) {
276                                         min_align = L4_SUPERPAGESHIFT;
277                                 }
278                                 DEBUGf(Romain::Log::Memory) << std::hex << address
279                                         << " --> [max: super page] alignment: "
280                                         << min_align;
281
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();
287
288                         }
289
290                         void         take() const                           { Ops::take(this); }
291                         void         release() const                        { Ops::release(this); }
292
293
294                         Region_handler_template operator + (long offset) const throw()
295                         { Region_handler_template n = *this; n._offs += offset; return n; }
296
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); }
299
300                         void free(l4_addr_t start, l4_umword_t size) const throw()
301                         { Ops::free(this, start, size); }
302
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); }
305
306         };
307
308         class Region_ops;
309         typedef Romain::Region_handler_template<L4::Cap<L4Re::Dataspace>, Romain::Region_ops> Region_handler;
310
311
312         class Region_ops
313         {
314                 public:
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);
319
320                         static void unmap(Region_handler const *h, l4_addr_t vaddr,
321                                           l4_addr_t offs, l4_umword_t size);
322
323                         static void free(Region_handler const *h, l4_addr_t start, l4_umword_t size);
324
325                         static void take(Region_handler const *h);
326                         static void release(Region_handler const *h);
327         };
328
329
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:
333          *
334          *   node->first  -> the Region object
335          *   node->second -> the Region_handler object
336          */
337         class Region_map
338                 : public L4Re::Util::Region_map<Romain::Region_handler,
339                                                 Romain::Allocator>
340         {
341                 pthread_mutex_t _mtx;
342                 public:
343                         typedef L4Re::Util::Region_map<Romain::Region_handler, Romain::Allocator> Base;
344                         enum { Invalid_inst = ~0U };
345
346                         void activate(l4_umword_t inst)
347                         {
348                                 pthread_mutex_lock(&_mtx);
349                                 _check(_active_instance != Invalid_inst, "rm already used, lock!!!");
350                                 _active_instance = inst;
351                         }
352
353                         void release()
354                         {
355                                 _active_instance = Invalid_inst;
356                                 pthread_mutex_unlock(&_mtx);
357                         }
358
359                         /*
360                          * Initialization, see loader/server/src/region.cc
361                          */
362                         Region_map();
363
364                         l4_addr_t remote_to_local(l4_addr_t remote, l4_umword_t inst)
365                         {
366                                 Base::Node n = find(remote);
367
368                                 if (n) {
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;
373                                 }
374
375                                 return ~0UL;
376                         }
377
378
379                         bool copy_existing_mapping(Romain::Region_handler& r,
380                                                    l4_umword_t orig_id,
381                                                    l4_umword_t inst_id,
382                                                    bool writepf = false) const
383                         {
384                                 (void)writepf;
385                                 if (orig_id == inst_id) return true;
386
387                                 L4::Cap<L4Re::Dataspace> mem;
388                                 l4_umword_t size = r.local_region(orig_id).size();
389
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,
393                                                                                   0,  0,
394                                                                                   r.alignment(),
395                                                                                   r.align_diff());
396                                 _check(a == 0, "DS allocation failed");
397
398 #if 0
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 << ")";
404 #endif
405                                 memcpy((void*)a, (void*)r.local_region(orig_id).start(), size);
406
407                                 r.set_local_region(inst_id, Romain::Region(a, a+size-1));
408                                 r.local_memory(inst_id, mem);
409
410                                 return true;
411                         }
412
413
414                         /*
415                          * Allocate a master-local dataspace object.
416                          */
417                         static void
418                         allocate_ds(L4::Cap<L4Re::Dataspace> *cap, l4_umword_t size)
419                         {
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;
424                                 }
425                                 
426                                 *cap = L4Re::Util::cap_alloc.alloc<L4Re::Dataspace>();
427                                 _check(!cap->is_valid(), "error allocating DS capability");
428
429                                 int error = L4Re::Env::env()->mem_alloc()->alloc(size, *cap, flags);
430                                 _check(error != 0, "error allocating memory");
431                         }
432
433
434                         static l4_addr_t
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)
438                         {
439                                 l4_addr_t a           = 0;
440                                 l4_addr_t search_size = size + aligndiff;
441
442                                 l4_umword_t r = L4Re::Env::env()->rm()->reserve_area(&a, search_size,
443                                                                                      L4Re::Rm::Search_addr | flags,
444                                                                                      align);
445                                 _check(r != 0, "reserve area failed");
446                                 DEBUG() << std::hex << "Reserved area at " << a << " and will attach to "
447                                         << a + aligndiff;
448
449                                 a += aligndiff;
450                                 l4_addr_t tmp_a = a;
451
452                                 r = L4Re::Env::env()->rm()->attach(&a, size, L4Re::Rm::In_area | L4Re::Rm::Eager_map | flags,
453                                                                    *ds, offs);
454                                 _check(r != 0, "attach error");
455                                 _check(a != tmp_a, "did not attach to correct location");
456
457                                 DEBUG() << "att: " << (void*)a;
458
459                                 r = L4Re::Env::env()->rm()->free_area(a);
460                                 _check(r != 0, "free area failed");
461
462                                 return a;
463                         }
464
465
466                         /*
467                          * Allocate and attach a master-local dataspace object.
468                          */
469                         static l4_addr_t
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)
474                         {
475                                 Romain::Region_map::allocate_ds(cap, size);
476
477                                 return Romain::Region_map::attach_aligned(cap, size, offs,
478                                                                           flags, align,
479                                                                           aligndiff);
480                         }
481
482
483                         /*
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).
486                          */
487                         bool lazy_map_region(Romain::Region_map::Base::Node &n, l4_umword_t inst, bool writepf=false);
488
489                 private:
490                         l4_umword_t _active_instance;
491
492                         /*
493                          * Determine the index of the first instance that already holds a
494                          * valid mapping for an existing region.
495                          */
496                         int find_existing_region(Romain::Region_map::Base::Node n) const
497                         {
498                                 l4_mword_t ret;
499                                 for (ret = 0; ret < static_cast<l4_mword_t>(Romain::MAX_REPLICAS); ++ret) {
500                                         if (n->second.local_region(ret).start())
501                                                 return ret;
502                                 }
503                                 return -1;
504                         }
505
506                 public:
507                         /*
508                          * Every DS we attach to the client is also attached locally within
509                          * the master process.
510                          */
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);
513
514
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);
517
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()
520                         {
521                                 DEBUG() << "attach_area(" << std::hex << addr << ", " << size << ", " << flags << ")";
522                                 return Base::attach_area(addr, size, flags, align);
523                         }
524
525
526                         bool detach_area(l4_addr_t addr) throw()
527                         {
528                                 DEBUG() << "detach_area(" << std::hex << addr << ")";
529                                 return Base::detach_area(addr);
530                         }
531
532                         Base::Node find(Base::Key_type const& k) const throw()
533                         {
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);
538                                 return n;
539                         }
540
541                         int detach(void* addr, l4_umword_t size, l4_umword_t flags,
542                        L4Re::Util::Region* reg, Romain::Region_handler* h);
543
544                         /*
545                          * Copy the content of all writable regions of instance 'from' to
546                          * the respective regions of instance 'to'.
547                          */
548                         void replicate(l4_umword_t from, l4_umword_t to)
549                         {
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);
556                                         }
557                                 }
558                         }
559
560
561                         static char const* print_mapping(Node n, l4_umword_t id)
562                         {
563                                 std::stringstream s;
564                                 s << "[" << std::hex << n->second.local_region(id).start()
565                                   << " - " << n->second.local_region(id).end() << "] ==> "
566                                   << "[" << n->first.start() << " - " << n->first.end() << "]";
567                                 s << std::endl;
568                                 s << "   DS " << std::hex <<  n->second.local_memory(id).cap();
569
570                                 return s.str().c_str();
571                         }
572         };
573
574
575         class Region_map_server
576         {
577                 public:
578                         typedef L4::Cap<L4Re::Dataspace> Dataspace;
579                         enum { Have_find = true };
580
581                         static int validate_ds(void *, L4::Ipc::Snd_fpage const &ds_cap,
582                                                l4_umword_t, L4::Cap<L4Re::Dataspace> *ds)
583                         {
584                                 /*
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.
588                                  */
589                                 *ds = L4::Cap<L4Re::Dataspace>(ds_cap.base());
590                                 return L4_EOK;
591                         }
592
593
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; }
596         };
597 }