L4Re Operating System Framework
Interface and Usage Documentation
Loading...
Searching...
No Matches
region_mapping
Go to the documentation of this file.
1// -*- Mode: C++ -*-
2// vim:ft=cpp
7/*
8 * (c) 2008-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
9 * Alexander Warg <warg@os.inf.tu-dresden.de>,
10 * Björn Döbel <doebel@os.inf.tu-dresden.de>
11 * economic rights: Technische Universität Dresden (Germany)
12 *
13 * License: see LICENSE.spdx (in this directory or the directories above)
14 */
15
16#pragma once
17
18#include <l4/cxx/avl_map>
19#include <l4/sys/types.h>
20#include <l4/re/rm>
21
22namespace L4Re { namespace Util {
23
24class Region
25{
26private:
27 l4_addr_t _start, _end;
28#ifdef CONFIG_L4RE_REGION_INFO
29 char _dbg_name[40]; // Not a 0-terminating string
30 unsigned char _dbg_name_len = 0;
31 static_assert(sizeof(_dbg_name) < 256);
32 Rm::Offset _dbg_backing_offset = 0;
33#endif
34
35public:
36 Region() noexcept : _start(~0UL), _end(~0UL) {}
37 Region(l4_addr_t addr) noexcept : _start(addr), _end(addr) {}
38 Region(l4_addr_t start, l4_addr_t end) noexcept
39 : _start(start), _end(end) {}
40 Region(l4_addr_t start, l4_addr_t end,
41 char const *name, unsigned name_len,
42 Rm::Offset backing_offset) noexcept
43 : _start(start), _end(end)
44 {
45#ifdef CONFIG_L4RE_REGION_INFO
46 _dbg_name_len = name_len > sizeof(_dbg_name)
47 ? sizeof(_dbg_name) : name_len;
48 for (unsigned i = 0; i < _dbg_name_len; ++i)
49 _dbg_name[i] = name[i];
50
51 _dbg_backing_offset = backing_offset;
52#else
53 (void)name;
54 (void)name_len;
55 (void)backing_offset;
56#endif
57 }
58 l4_addr_t start() const noexcept { return _start; }
59 l4_addr_t end() const noexcept { return _end; }
60 unsigned long size() const noexcept { return end() - start() + 1; }
61 bool invalid() const noexcept { return _start == ~0UL && _end == ~0UL; }
62 bool operator < (Region const &o) const noexcept
63 { return end() < o.start(); }
64 bool contains(Region const &o) const noexcept
65 { return o.start() >= start() && o.end() <= end(); }
66 bool operator == (Region const &o) const noexcept
67 { return o.start() == start() && o.end() == end(); }
68 ~Region() noexcept {}
69
70#ifdef CONFIG_L4RE_REGION_INFO
71 char const *name() const { return _dbg_name; }
72 unsigned char name_len() const { return _dbg_name_len; }
73 Rm::Offset backing_offset() const { return _dbg_backing_offset; }
74#else
75 char const *name() const { return "N/A"; }
76 unsigned char name_len() const { return 3; }
77 Rm::Offset backing_offset() const { return 0; }
78#endif
79};
80
81
82template< typename Hdlr, template<typename T> class Alloc >
83class Region_map
84{
85protected:
86 typedef cxx::Avl_map<Region, Hdlr, cxx::Lt_functor, Alloc> Tree;
87 typedef cxx::Avl_map<Region, l4_addr_t, cxx::Lt_functor, Alloc> Rescue_tree;
88
89 Tree _rm;
90 Tree _am;
91 Rescue_tree _rescue_map;
92
93private:
94 l4_addr_t _start;
95 l4_addr_t _end;
96
97protected:
98 void set_limits(l4_addr_t start, l4_addr_t end) noexcept
99 {
100 _start = start;
101 _end = end;
102 }
103
104public:
105 typedef typename Tree::Item_type Item;
106 typedef typename Tree::Node Node;
107 typedef typename Tree::Key_type Key_type;
108 typedef Hdlr Region_handler;
109
110 typedef typename Tree::Iterator Iterator;
111 typedef typename Tree::Const_iterator Const_iterator;
112 typedef typename Tree::Rev_iterator Rev_iterator;
113 typedef typename Tree::Const_rev_iterator Const_rev_iterator;
114
115 Iterator begin() noexcept { return _rm.begin(); }
116 Const_iterator begin() const noexcept { return _rm.begin(); }
117 Iterator end() noexcept { return _rm.end(); }
118 Const_iterator end() const noexcept { return _rm.end(); }
119
120 Iterator area_begin() noexcept { return _am.begin(); }
121 Const_iterator area_begin() const noexcept { return _am.begin(); }
122 Iterator area_end() noexcept { return _am.end(); }
123 Const_iterator area_end() const noexcept { return _am.end(); }
124 Node area_find(Key_type const &c) const noexcept { return _am.find_node(c); }
125
126 l4_addr_t min_addr() const noexcept { return _start; }
127 l4_addr_t max_addr() const noexcept { return _end; }
128
129 Region_map(l4_addr_t start, l4_addr_t end) noexcept : _start(start), _end(end) {}
130
131 Node find(Key_type const &key) const noexcept
132 {
133 Node n = _rm.find_node(key);
134 if (!n)
135 return Node();
136
137 // 'find' should find any region overlapping with the searched one, the
138 // caller should check for further requirements
139 if (0)
140 if (!n->first.contains(key))
141 return Node();
142
143 return n;
144 }
145
146 Node lower_bound(Key_type const &key) const noexcept
147 {
148 Node n = _rm.lower_bound_node(key);
149 return n;
150 }
151
152 Node lower_bound_area(Key_type const &key) const noexcept
153 {
154 Node n = _am.lower_bound_node(key);
155 return n;
156 }
157
158 bool add_area(Region const &region, L4Re::Rm::F::Region_flags region_flags)
159 {
160 using DS = typename Hdlr::Dataspace;
161 return _am.insert(region, Hdlr(DS(), 0, 0, region_flags)).second == 0;
162 }
163
164 l4_addr_t attach_area(l4_addr_t addr, unsigned long size,
165 L4Re::Rm::Flags flags = L4Re::Rm::Flags(0),
166 unsigned char align = L4_PAGESHIFT) noexcept
167 {
168 Region c;
169
170 if (flags & L4Re::Rm::F::Search_addr)
171 {
172 if (addr < min_addr())
173 addr = min_addr();
174 }
175 else
176 {
177 c = Region(addr, addr + size - 1);
178 if (_am.find_node(c))
179 return L4_INVALID_ADDR;
180
181 // Regions cannot be placed in reserved areas. We explicitly allow the
182 // reverse: place areas over regions that are already present.
183 if ((flags & L4Re::Rm::F::Reserved) && _rm.find_node(c))
184 return L4_INVALID_ADDR;
185 }
186
187 while (flags & L4Re::Rm::F::Search_addr)
188 {
189 if (addr > max_addr() || (max_addr() - addr) < (size - 1))
190 return L4_INVALID_ADDR;
191
192 addr = find_free(addr, max_addr(), size, align, flags);
193 if (addr == L4_INVALID_ADDR)
194 return L4_INVALID_ADDR;
195
196 c = Region(addr, addr + size - 1);
197 Node r = _am.find_node(c);
198 if (!r)
199 break;
200
201 if (r->first.end() >= max_addr())
202 return L4_INVALID_ADDR;
203
204 addr = r->first.end() + 1;
205 }
206
207 if (add_area(c, flags.region_flags()))
208 return addr;
209
210 return L4_INVALID_ADDR;
211 }
212
213 bool detach_area(l4_addr_t addr) noexcept
214 {
215 if (_am.remove(addr))
216 return false;
217
218 return true;
219 }
220
221 bool add_region(Region const &region, Hdlr const &hdlr)
222 {
223 auto [it, res] = _rm.insert(region, hdlr);
224 if (res != 0)
225 return false;
226
227 if (!it->second.attached(region.start(), region.end()))
228 {
229 _rm.erase(it->first);
230 return false;
231 }
232
233 return true;
234 }
235
236 void *attach(void *addr, unsigned long size, Hdlr const &hdlr,
237 L4Re::Rm::Flags attach_flags = L4Re::Rm::Flags(0),
238 unsigned char align = L4_PAGESHIFT,
239 char const *name = nullptr, unsigned name_len = 0,
240 L4Re::Rm::Offset backing_offset = 0) noexcept
241 {
242 l4_addr_t beg, end;
243 int err = hdlr.map_info(&beg, &end);
244 if (err > 0)
245 {
246 // Mapping address determined by underlying dataspace. Make sure we
247 // prevent any additional alignment. We already know the place!
248 end = beg + size - 1U;
249 align = L4_PAGESHIFT;
250
251 // In case of exact mappings, the supplied address must match because
252 // we cannot remap.
253 if (!(attach_flags & L4Re::Rm::F::Search_addr)
254 && reinterpret_cast<l4_addr_t>(addr) != beg)
255 return L4_INVALID_PTR;
256
257 // When searching for a suitable address, the start must cover the
258 // dataspace beginning to "find" the right spot.
259 if ((attach_flags & L4Re::Rm::F::Search_addr)
260 && reinterpret_cast<l4_addr_t>(addr) > beg)
261 return L4_INVALID_PTR;
262 }
263 else if (err == 0)
264 {
265 beg = reinterpret_cast<l4_addr_t>(addr);
266 end = max_addr();
267 }
268 else if (err < 0)
269 return L4_INVALID_PTR;
270
271 if (attach_flags & L4Re::Rm::F::In_area)
272 {
273 Node r = _am.find_node(Region(beg, beg + size - 1));
274 if (!r || (r->second.flags() & L4Re::Rm::F::Reserved))
275 return L4_INVALID_PTR;
276
277 end = r->first.end();
278 }
279
280 if (attach_flags & L4Re::Rm::F::Search_addr)
281 {
282 beg = find_free(beg, end, size, align, attach_flags);
283 if (beg == L4_INVALID_ADDR)
284 return L4_INVALID_PTR;
285 }
286
287 if (!(attach_flags & (L4Re::Rm::F::Search_addr | L4Re::Rm::F::In_area))
288 && _am.find_node(Region(beg, beg + size - 1)))
289 return L4_INVALID_PTR;
290
291 if (beg < min_addr() || beg + size - 1 > end)
292 return L4_INVALID_PTR;
293
294 if (add_region(Region(beg, beg + size - 1, name, name_len, backing_offset),
295 hdlr))
296 return reinterpret_cast<void*>(beg);
297
298 return L4_INVALID_PTR;
299 }
300
301 int detach(void *addr, unsigned long sz, unsigned flags,
302 Region *reg, Hdlr *hdlr) noexcept
303 {
304 l4_addr_t a = reinterpret_cast<l4_addr_t>(addr);
305 Region dr(a, a + sz - 1);
306 Region res(~0UL, 0);
307
308 Node r = find(dr);
309 if (!r)
310 return -L4_ENOENT;
311
312 Region g = r->first;
313 Hdlr const &h = r->second;
314
315 if (flags & L4Re::Rm::Detach_overlap || dr.contains(g))
316 {
317 // Successful removal of the AVL tree item also frees the node.
318 Hdlr h_copy = h;
319
320 if (_rm.remove(g))
321 return -L4_ENOENT;
322
323 if (h_copy.flags() & (L4Re::Rm::F::Anonymous | L4Re::Rm::F::Private))
324 h_copy.free(0, g.size());
325
326 int ret = Rm::Detached_ds;
327 if (h_copy.detached(g.start(), g.end()))
328 ret |= Rm::Unmapped_range;
329
330 if (hdlr)
331 *hdlr = h_copy;
332 if (reg)
333 *reg = g;
334
335 if (find(dr))
336 ret |= Rm::Detach_again;
337
338 return ret;
339 }
340 else if (dr.start() <= g.start())
341 {
342 // Move the start of a region.
343
345 h.free(0, dr.end() + 1 - g.start());
346
347 int ret = Rm::Kept_ds;
348 if (h.detached(g.start(), dr.end()))
349 ret |= Rm::Unmapped_range;
350
351 unsigned long sz = dr.end() + 1 - g.start();
352 Item &cn = const_cast<Item &>(*r);
353 cn.first = Region(dr.end() + 1, g.end(), g.name(), g.name_len(),
354 g.backing_offset() + sz);
355 cn.second = cn.second + sz;
356 if (hdlr)
357 *hdlr = Hdlr();
358 if (reg)
359 *reg = Region(g.start(), dr.end());
360
361 if (find(dr))
362 ret |= Rm::Detach_again;
363
364 return ret;
365 }
366 else if (dr.end() >= g.end())
367 {
368 // Move the end of a region.
369
371 h.free(dr.start() - g.start(), g.end() + 1 - dr.start());
372
373 int ret = Rm::Kept_ds;
374 if (h.detached(dr.start(), g.end()))
375 ret |= Rm::Unmapped_range;
376
377 Item &cn = const_cast<Item &>(*r);
378 cn.first = Region(g.start(), dr.start() - 1, g.name(), g.name_len(),
379 g.backing_offset());
380 if (hdlr)
381 *hdlr = Hdlr();
382 if (reg)
383 *reg = Region(dr.start(), g.end());
384
385 if (find(dr))
386 ret |= Rm::Detach_again;
387
388 return ret;
389 }
390 else if (g.contains(dr))
391 {
392 // Split a single region that contains the new region.
393
395 h.free(dr.start() - g.start(), dr.size());
396
397 int ret = Rm::Split_ds;
398 if (h.detached(dr.start(), dr.end()))
399 ret |= Rm::Unmapped_range;
400
401 // First move the end off the existing region before the new one.
402 Item &cn = const_cast<Item &>(*r);
403 cn.first = Region(g.start(), dr.start()-1, g.name(), g.name_len(),
404 g.backing_offset());
405
406 int err;
407
408 // Insert a second region for the remaining tail of
409 // the old existing region.
410 auto tail_sz = dr.end() + 1 - g.start();
411 err = _rm.insert(Region(dr.end() + 1, g.end(), g.name(), g.name_len(),
412 g.backing_offset() + tail_sz),
413 h + tail_sz).second;
414
415 if (err)
416 return err;
417
418 if (hdlr)
419 *hdlr = h;
420 if (reg)
421 *reg = dr;
422
423 return ret;
424 }
425
426 return -L4_ENOENT;
427 }
428
429 l4_addr_t find_free(l4_addr_t start, l4_addr_t end, l4_addr_t size,
430 unsigned char align, L4Re::Rm::Flags attach_flags) const noexcept;
431
440 l4_ret_t add_rescue_jump(l4_addr_t pc_begin, l4_addr_t pc_end,
441 l4_addr_t rescue_pc) noexcept
442 {
443 if (pc_begin >= pc_end)
444 return -L4_EINVAL;
445
446 Region key(pc_begin, pc_end - 1);
447 if (_rescue_map.find_node(key))
448 return -L4_EEXIST;
449 if (_rescue_map.insert(key, rescue_pc).second)
450 return -L4_ENOMEM;
451
452 return L4_EOK;
453 }
454
462 l4_addr_t find_rescue_jump(l4_addr_t pc) const noexcept
463 {
464 auto n = _rescue_map.find_node(Region(pc));
465 if (!n)
466 return 0;
467 return n->second;
468 }
469
478 l4_ret_t remove_rescue_jump(l4_addr_t pc) noexcept
479 {
480 if (_rescue_map.remove(Region(pc)))
481 return -L4_ENOENT;
482 return L4_EOK;
483 }
484
485 l4_ret_t page_in(l4_addr_t min_addr, l4_addr_t max_addr,
486 L4Re::Rm::Region_flags rights,
487 typename Hdlr::Map_result *map_res)
488 {
489 // We'll try to map one flexpage...
490 if (auto r = find(Region(min_addr)))
491 {
492 if (r->second.flags() & (L4Re::Rm::F::Kernel | L4Re::Rm::F::Reserved))
493 return -L4_ENODEV;
494
495 l4_addr_t start = min_addr;
496 l4_addr_t end = cxx::min(max_addr, r->first.end());
497 L4Re::Rm::Region_flags r_flags = r->second.flags();
498
499 // Cannot request more rights than with what the region was attached in
500 // the first place.
501 if ((r_flags & rights) != rights)
502 return -L4_EACCESS;
503
504 return r->second.page_in(r->first, start, end, rights, map_res);
505 }
506 else
507 return -L4_ENOENT;
508 }
509};
510
511template<typename Hdlr, template<typename T> class Alloc>
513Region_map<Hdlr, Alloc>::find_free(l4_addr_t start, l4_addr_t end,
514 unsigned long size, unsigned char align, L4Re::Rm::Flags attach_flags) const noexcept
515{
516 l4_addr_t addr = start;
517
518 if (addr == ~0UL || addr < min_addr() || addr >= end)
519 addr = min_addr();
520
521 addr = l4_round_size(addr, align);
522 Node r;
523
524 for (;;)
525 {
526 if (addr > 0 && addr - 1 > end - size)
527 return L4_INVALID_ADDR;
528
529 Region c(addr, addr + size - 1);
530 r = _rm.find_node(c);
531
532 if (!r)
533 {
534 if (!(attach_flags & L4Re::Rm::F::In_area) && (r = _am.find_node(c)))
535 {
536 if (r->first.end() > end - size)
537 return L4_INVALID_ADDR;
538
539 addr = l4_round_size(r->first.end() + 1, align);
540 continue;
541 }
542
543 break;
544 }
545 else if (r->first.end() > end - size)
546 return L4_INVALID_ADDR;
547
548 addr = l4_round_size(r->first.end() + 1, align);
549 }
550
551 if (!r)
552 return addr;
553
554 return L4_INVALID_ADDR;
555}
556
557}}
AVL map.
@ Detached_ds
Detached data space.
Definition rm:91
@ Unmapped_range
The region mananager has already unmapped the pages.
Definition rm:98
@ Detach_again
Detached data space, more to do.
Definition rm:96
@ Split_ds
Split data space, and done.
Definition rm:93
@ Kept_ds
Kept data space.
Definition rm:92
@ Detach_overlap
Do an unmap of all overlapping regions.
Definition rm:245
ITEM_TYPE Item_type
Type for the items store in the set.
Definition avl_set:147
unsigned long l4_addr_t
Address type.
Definition l4int.h:34
@ L4_EEXIST
Already exists.
Definition err.h:44
@ L4_ENOENT
No such entity.
Definition err.h:35
@ L4_EACCESS
Permission denied.
Definition err.h:41
@ L4_EINVAL
Invalid argument.
Definition err.h:47
@ L4_ENODEV
No such thing.
Definition err.h:45
@ L4_EOK
Ok.
Definition err.h:33
@ L4_ENOMEM
No memory.
Definition err.h:40
#define L4_INVALID_PTR
Invalid address as pointer type.
Definition consts.h:523
#define L4_PAGESHIFT
Size of a page, log2-based.
Definition consts.h:26
l4_addr_t l4_round_size(l4_addr_t value, unsigned char bits) L4_NOTHROW
Round value up to the next alignment with bits size.
Definition consts.h:495
@ L4_INVALID_ADDR
Invalid address.
Definition consts.h:516
Common L4 ABI Data Types.
l4_int16_t l4_ret_t
Return value of an IPC call as well as an RPC call.
Definition types.h:29
Documentation of the L4 Runtime Environment utility functionality in C++.
Definition l4re.dox:21
L4Re C++ Interfaces.
Definition cmd_control:14
Region mapper interface.
Region_flags
Region flags (permissions, cacheability, special).
Definition rm:131
@ Private
Attach the dataspace privately with copy-on-write semantics.
Definition rm:156
@ Reserved
Region is reserved (blocked).
Definition rm:153
@ Anonymous
Attach anonymous memory in the region.
Definition rm:160
@ Kernel
Kernel-provided memory (KUMEM).
Definition rm:148
@ Search_addr
Search for a suitable address range.
Definition rm:115
@ In_area
Search only in area, or map into area.
Definition rm:117