]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/plr/server/src/memory.cc
update
[l4.git] / l4 / pkg / plr / server / src / memory.cc
1 /*
2  * memory.cc --
3  *
4  * Memory management implementation
5  *
6  * (c) 2011-2013 Björn Döbel <doebel@os.inf.tu-dresden.de>,
7  *     economic rights: Technische Universität Dresden (Germany)
8  * This file is part of TUD:OS and distributed under the terms of the
9  * GNU General Public License 2.
10  * Please see the COPYING-GPL-2 file for details.
11  */
12 #include "memory"
13 #include "app_loading"
14 #include "locking.h"
15 #include <l4/sys/kdebug.h>
16
17 #define MSG() DEBUGf(Romain::Log::Memory)
18
19 int Romain::Region_ops::map(Region_handler const *h, l4_addr_t addr,
20                             L4Re::Util::Region const &r, bool writable,
21                             l4_umword_t *result)
22 {
23         (void)h;
24         (void)addr;
25         (void)r;
26         (void)writable;
27         (void)result;
28         enter_kdebug("ops::map");
29         return -1;
30 }
31
32 void Romain::Region_ops::unmap(Region_handler const *h, l4_addr_t vaddr,
33                                l4_addr_t offs, unsigned long size)
34 {
35         (void)h;
36         (void)vaddr;
37         (void)offs;
38         (void)size;
39         enter_kdebug("ops::unmap");
40 }
41
42
43 void Romain::Region_ops::free(Region_handler const *h, l4_addr_t start, unsigned long size)
44 {
45         if ((h->flags() & L4Re::Rm::Reserved))
46                 return;
47
48         if (h->flags() & L4Re::Rm::Pager)
49                 return;
50
51         L4::Cap<L4Re::Dataspace> ds(h->client_cap_idx());
52         ds->clear(h->offset() + start, size);
53         
54         //enter_kdebug("ops::free");
55 }
56
57
58 void Romain::Region_ops::take(Region_handler const* h)
59 {
60         (void)h;
61         enter_kdebug("ops::take");
62 }
63
64
65 void Romain::Region_ops::release(Region_handler const* h)
66 {
67         (void)h;
68         enter_kdebug("ops::release");
69 }
70
71
72 Romain::Region_map::Region_map()
73         : Base(0,0), _active_instance(Invalid_inst)
74 {
75         pthread_mutex_init(&_mtx, 0);
76
77         l4_kernel_info_t *kip = l4re_kip();
78         MSG() << "kip found at 0x" << std::hex << kip
79               << " having " << L4::Kip::Mem_desc::count(kip)
80               << " entries.";
81
82         L4::Kip::Mem_desc *d = L4::Kip::Mem_desc::first(kip);
83         unsigned long count  = L4::Kip::Mem_desc::count(kip);
84
85         for (L4::Kip::Mem_desc *desc = d; desc < d + count; ++desc) {
86
87                 if (!desc->is_virtual())
88                         continue;
89
90                 l4_addr_t s = desc->start() == 0 ? 0x1000 : desc->start();
91                 l4_addr_t e = desc->end();
92                 MSG() << "virtual range: [" << std::hex << s << "-" << e << "] ";
93
94                 switch(desc->type()) {
95                         case L4::Kip::Mem_desc::Conventional:
96                                 set_limits(s,e);
97                                 break;
98                         default:
99                                 WARN() << "type " << desc->type() << "???";
100                                 break;
101                 }
102
103         }
104 }
105
106
107 /*
108  * Attach a replica region locally in the master AS.
109  *
110  * The Romain::Master manages replicas' region map. As a part of this, the master
111  * attaches a copy of every region attached to one of the replicas to its own
112  * address space. This local region is then used to service page faults caused by
113  * the replicas.
114  *
115  * For read-only replica dataspaces, the master creates a writable copy of the
116  * original region in a separate dataspace. This allows us to modify their content
117  * if necessary (e.g., setting debug breakpoints on read-only code).
118  *
119  * For writable dataspaces, the master simply attaches the same region that has
120  * already been attached to the replica AS into its own address space and maintains
121  * a copy for every replica.
122  *
123  * The replica copies lead to another problem: to reduce replication overhead, we
124  * would like to handle page faults using as few memory mappings as possible. To
125  * be able to do so, we however need to make sure that all replica copies are
126  * aligned to the same start address. The Romain::Master does this by ensuring
127  * that the replica address as well as the master-local addresses of the copies
128  * are aligned to the same offset within a minimally aligned region. The minimum
129  * alignment is defined by the Region_handler()'s MIN_ALIGNMENT member (XXX and
130  * should at some point become a configurable feature).
131  *
132  * The layout looks like this:
133  *
134  *  REPLICA:
135  *
136  * (1)      (2)                         (3)
137  *  +- - - - +---------------------------+
138  *  |        |                           |
139  *  +- - - - +---------------------------+
140  *
141  *  MASTER (per copy):
142  *
143  * (A)      (B)                         (C)
144  *  +- - - - +---------------------------+
145  *  |        |                           |
146  *  +- - - - +---------------------------+ 
147  *
148  *   (1)  correct minimum alignment
149  *   (2)  region.start()
150  *   (3)  region.end()
151  *
152  *   (A)  address aligned similar to (1)
153  *   (B)  local_region[inst]->start()
154  *   (C)  local_region[inst]->end()
155  *
156  *   MIN_ALIGNMENT_MASK := (1 << MIN_ALIGNMENT) - 1
157  *   (1) := (2) - [(2) & MIN_ALIGNMENT_MASK]
158  *   (A) := (B) - [(B) & MIN_ALIGNMENT_MASK]
159  *
160  *  Attaching then works by first reserving an area with the RM
161  *  that has the size (C) - (A). Then we attach the dataspace to
162  *  (B) and free the reserved area so that the remaining memory
163  *  can again be freely used.
164  */
165 void *Romain::Region_map::attach_locally(void* addr, unsigned long size,
166                                          Romain::Region_handler *hdlr,
167                                          unsigned flags, unsigned char align)
168 {
169         long r; (void)addr;
170         Romain::Region_handler::Dataspace const *ds = &hdlr->local_memory(0);
171         L4Re::Dataspace::Stats dsstat;
172         (*ds)->info(&dsstat);
173
174 #if 1
175         MSG() << PURPLE
176                   << "attaching DS " << std::hex << ds->cap()
177               << ", addr = "     << addr
178               << ", flags = "    << flags << " (" << dsstat.flags << ")"
179               << ", size = "     << size
180               << ", align = "    << (unsigned int)align
181               << NOCOLOR;
182 #endif
183
184         if (!ds->is_valid()) {
185                 enter_kdebug("attaching invalid cap");
186         }
187
188         /*
189          * Here's what the variables below mean in the original DS. If the DS is
190          * read-only, we still want a writable version, because some users, such as
191          * GDB, want to modify ro data (e.g., GDB breakpoints). Therefore, we then
192          * create a copy of the original DS and adapt the variables afterwards.
193          *
194          * 1      2    3    4                      5
195          * +------+----+----+----------------------+
196          * |      |'..'|..'.|                      |       Original DS
197          * |      |.''.|''.'|                      |
198          * +------+----+----+----------------------+
199          *        \         \
200          *         \         \
201          *          \         \
202          *           \         \
203          *           +---------+
204          *           |''.''.''.|                           New DS
205          *           |..'..'..'|
206          *           +---------+
207          *           6         7
208          *
209          * 1 .. original DS start
210          * 2 .. page base of region (page_base)
211          * 3 .. offset within DS    (hdlr()->offset)
212          * 4 .. region end          (hdlr()->offset + size)
213          * 5 .. original DS end
214          * 6 .. new DS start
215          * 7 .. new DS end
216          *
217          * offset_in_page = $3 - $2
218          * eff_size       = size + offset_in_page rounded to page size
219          *
220          * After copy:
221          *    page_base      = 0
222          *    hdlr()->offset = 0
223          *
224          */
225
226         l4_addr_t page_base      = l4_trunc_page(hdlr->offset());
227         l4_addr_t offset_in_page = hdlr->offset() - page_base;
228         unsigned eff_size        = l4_round_page(size + offset_in_page);
229
230 #if 1
231         MSG() << "  page_base " << (void*)page_base << ", offset " << std::hex << offset_in_page
232               << ", eff_size " << eff_size << ", hdlr->offset " << hdlr->offset();
233 #endif
234
235         hdlr->offset(page_base /*hdlr->offset() - offset_in_page*/);
236
237         /*
238          * If the dataspace we attach was originally not mapped writable,
239          * make a writable copy here, so that fault handlers such as GDB
240          * can instrument this memory even if the client should not get
241          * it writable.
242          */
243         if (!(dsstat.flags & L4Re::Dataspace::Map_rw)) {
244                 //MSG() << "Copying to rw dataspace.";
245                 L4::Cap<L4Re::Dataspace> mem;
246
247                 Romain::Region_map::allocate_ds(&mem, eff_size);
248
249                 mem->copy_in(0, *ds, page_base, hdlr->offset() + size);
250                 ds        = &mem; // taking pointer to stack?? XXX
251                 flags    &= ~L4Re::Rm::Read_only;
252                 page_base = 0;
253                 /*
254                  * XXX: If we copied the dataspace above, what happens to the
255                  *      original DS that was passed in? Shouldn't it be released?
256                  *
257                  *      No. It might still be attached elsewhere. Perhaps decrement
258                  *      some refcnt? XXX check
259                  */
260         }
261
262         /*
263          * flags contains the replica's flags setting. If the replica asks to attach into
264          * a previously reserved region, then this is only valid for the replica's
265          * rm::attach() call, but not for attaching our local version/copy of the DS.
266          */
267         flags &= ~L4Re::Rm::In_area;
268
269         l4_addr_t a = Romain::Region_map::attach_aligned(ds, eff_size,
270                                                          page_base, flags,
271                                                          hdlr->alignment(),
272                                                          hdlr->align_diff());
273
274         Romain::Region reg(a, a+eff_size - 1);
275         reg.touch_rw();
276
277         MSG() << "set_local_region(" << _active_instance << ", "
278                   << std::hex << reg.start() << ")";
279         hdlr->set_local_region(_active_instance, reg);
280         return (void*)(a + offset_in_page);
281 }
282
283 void *Romain::Region_map::attach(void* addr, unsigned long size,
284                                  Romain::Region_handler const &hdlr,
285                                  unsigned flags, unsigned char align, bool shared)
286 {
287         void *ret = 0;
288
289         MSG() << std::hex << addr << " " << size << " " << (int)align << " " << flags;
290         MSG() << "cap " << std::hex << hdlr.client_cap_idx();
291
292         /*
293          * XXX: the only reason for this Region_handler to exist is to copy
294          *      the shared parameter into the handler. Shouldn't the caller
295          *      do this???
296          */
297         Romain::Region_handler _handler(hdlr);
298         _handler.shared(shared);
299
300         /*
301          * First, do the attach() in the remote address space. Thereby we get
302          * the remote address and can then during local attaching make sure
303          * that the memory alignment of the local mapping matches the remote
304          * alignment. This allows us to play mapping tricks later.
305          * 
306          * Note, we can only do this if the Search_addr flag is set, otherwise
307          * we might hurt the client's expectations about the mem region.
308          */
309         if ((flags & L4Re::Rm::Search_addr) and 
310             !(flags & L4Re::Rm::In_area) and
311             (size > L4_SUPERPAGESIZE) and
312             (align < L4_LOG2_SUPERPAGESIZE)) {
313                 align = L4_LOG2_SUPERPAGESIZE;
314                 size  = l4_round_size(size, L4_SUPERPAGESHIFT);
315         }
316         
317
318         ret = Base::attach(addr, size, _handler, flags, align);
319         MSG() << "Base::attach = " << std::hex << ret << " hdlr.offs: " << _handler.offset();
320         if (ret == (void*)~0UL) {
321                 INFO() << std::hex << addr << " " << size << " " << (int)align << " " << flags;
322                 enter_kdebug("Attach error");
323         }
324
325         /*
326          * The node is now present in the region map. This means we now need
327          * to continue working with the node's handler member instead of our
328          * local copy (because the attach() call has made a copy of it).
329          */
330         Romain::Region_map::Base::Node n = find((l4_addr_t)ret);
331         // and the region handler is const by default ... *grrrml*
332         Romain::Region_handler* theHandler = const_cast<Romain::Region_handler*>(&(n->second));
333         
334         /*
335          * Figure out the best possible alignment we can have between the local
336          * and the remote region. If the alignments match, we can later map more
337          * than one page at a time.
338          */
339         theHandler->alignToAddressAndSize(reinterpret_cast<l4_addr_t>(ret), size);
340
341         /* Only attach locally, if this hasn't been done beforehand yet. */
342         if (!n->second.local_region(_active_instance).start()) {
343                 void* a = attach_locally(addr, size, theHandler,
344                                          flags, theHandler->alignment());
345                 _check(a == 0, "Error in local attach");
346         }
347
348         MSG() << "new mapping (" << _active_instance << ") "
349               << "[" << std::hex << n->second.local_region(_active_instance).start()
350               << " - " << n->second.local_region(_active_instance).end()
351               << "] -> " << ret;
352         //enter_kdebug("after attach");
353
354         return ret;
355 }
356
357
358
359 int Romain::Region_map::detach(void* /* IN */ addr, unsigned long /* IN */ size,
360                                unsigned /* IN */ flags,
361                                L4Re::Util::Region* /* OUT */ reg,
362                                Romain::Region_handler* /* OUT */ h)
363 {
364         /* First, do a lookup for the handler, because Base::Detach() will not always
365          * return the handler node, but we need it for detach later.
366          */
367         l4_addr_t srch = (l4_addr_t)addr;
368         Romain::Region_handler hd = Base::find(Region(srch, srch + size - 1))->second;
369         MSG() << "Client cap: " << std::hex << hd.client_cap_idx();
370         if (hd.client_cap_idx() == L4_INVALID_CAP) {
371                 enter_kdebug("invalid cap!");
372         }
373
374         MSG() << std::hex << "Base::detach(" << (l4_addr_t)addr << " " << size << " " << flags << ")";
375         MSG() << std::hex << find((l4_addr_t)addr)->second.client_cap_idx();
376         int ret = Base::detach(addr, size, flags, reg, h);
377         MSG() << std::hex << "Base::detach(): " << ret << " r.start: " << reg->start() << " " << reg->size();
378
379         /*
380          * Iterate over replicas and detach() the replicas' mappings within
381          * the master AS.
382          */
383         for (unsigned idx = 0; idx < hd.local_region_count(); ++idx) {
384                 if (hd.local_region(idx).start() == 0) {
385                         continue;
386                 }
387
388                 MSG() << "[" << std::hex
389                           << hd.local_region(idx).start() << " - "
390                           << hd.local_region(idx).end() << "] ==> "
391                           << "[" << reg->start() << " - "
392                           << reg->end() << "]";
393
394                 L4::Cap<L4Re::Dataspace> memcap;
395                 int r = L4Re::Env::env()->rm()->detach(hd.local_region(idx).start(),
396                                                        hd.local_region(idx).size(),
397                                                                                            &memcap, L4Re::This_task);
398                 MSG() << "detached locally: " << r << " cap: " << std::hex << memcap.cap();
399
400                 if (!hd.writable()) {
401                         /* For read-only regions, we only need to detach once */
402                         break;
403                 }
404         }
405
406         return ret;
407 }
408
409
410 bool Romain::Region_map::lazy_map_region(Romain::Region_map::Base::Node &n, unsigned inst, bool iswritepf)
411 {
412         /* already mapped? */
413         if (n->second.local_region(inst).start())
414                 return false;
415
416         MSG() << "start " <<  n->second.local_region(inst).start();
417         MSG() << "replica without yet established mapping.";
418         MSG() << "ro: " << n->second.is_ro();
419         MSG() << "shared: " << (n->second.shared() ? "true" : "false");
420
421         /*
422          * As we found a node, we know there exists at least one replica
423          * that already has this mapping. Therefore, we go and look for
424          * one of those mappings to start from.
425          */
426         int existing = find_existing_region(n);
427         _check(existing < 0, "no mapping???");
428         _check(existing > Romain::MAX_REPLICAS, "no mapping???");
429
430         Romain::Rm_guard(this, inst);
431         Romain::Region_handler *rh = const_cast<Romain::Region_handler*>(&n->second);
432
433         /*
434          * Case 1: region is read-only -> we share the mapping from
435          *         the first node, because it was already established.
436          */
437         if (n->second.is_ro() or rh->shared()) {
438                 /*
439                  * XXX: Why is setting local_region and memory split up?
440                  */
441                 rh->set_local_region(inst, n->second.local_region(existing));
442                 rh->local_memory(inst, n->second.local_memory(existing));
443         } else {
444                         MSG() << "Copying existing mapping.";
445                         bool b = copy_existing_mapping(*rh, existing, inst, iswritepf);
446                         _check(!b, "error creating rw copy");
447         }
448         return true;
449 }
450