T154: mask out vregion flag for arm
[barrelfish] / lib / barrelfish / arch / arm / pmap_arch.c
1 /**
2  * \file
3  * \brief pmap management
4  */
5
6 /*
7  * Copyright (c) 2010-2015 ETH Zurich.
8  * All rights reserved.
9  *
10  * This file is distributed under the terms in the attached LICENSE file.
11  * If you do not find this file, copies can be found by writing to:
12  * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
13  */
14
15 /*
16  * There was some minor difficulty here with mapping the cpus native
17  * page table arrangement onto Barrelfish. The problem lies with
18  * resource bootstrapping. The bootstrap ram allocator allocates pages.
19  *
20  *
21  * The natural division of bits is 12/10/12, corresponding to 4K
22  * L1 entries in the L1 table and 256 L2 entries per L2
23  * table. Unfortunately 256 entries consumes 1KB rather than a
24  * page (4KB) so we pretend here and in the kernel caps page
25  * code that the L1 has 1024 entries and L2 tables are 4KB in
26  * size. The 4KB constraint comes from ram_alloc_fixed
27  * allocating single pages and the difficulty in bootstrapping
28  * cap slots (alloc_node takes a single slot.
29  *
30  * For now this suffices, but might need to be revisited in future.
31  *
32  * An earlier cut at this, used the first 1KB from each
33  * allocation made from ram_alloc_fixed and wasted the remaining
34  * space. Aside from the space wasted it entailed a couple of minor
35  * platform ifdefs to work around the discrepency.
36  *
37  * Alternative fixes discussed include:
38  *
39  * 1. avoid the need to create vnodes before connecting to a
40  *    real allocator (probably not plausible).
41  *
42  * 2. somehow make ram_alloc_fixed handle sub-page allocations
43  *    (it's clunky, but perhaps we can give each domain a separate
44  *     cnode full of 1k- sized RAM caps?)
45  *
46  * 3. handle the problem at the level of vnode_create (can't see how to
47  *    do this)
48  *
49  * 4. waste the space -- doing this cleanly will require a new parameter
50  * to retype to prevent all 4 caps being created
51  *
52  * 5. introduce a new arm-specific version of vnode_create that creates
53  * 4 1k vnodes, and is only called from the ARM VM code.
54  *
55  */
56
57 #include <barrelfish/barrelfish.h>
58 #include <barrelfish/caddr.h>
59 #include <barrelfish/invocations_arch.h>
60 #include <stdio.h>
61
62 // Location of VSpace managed by this system.
63 #ifdef __ARM_ARCH_7M__
64 //virtual section 0x40000000-0x40100000 can not be used as regular memory
65 //because of "bit-banding".
66 //0x42000000-0x44000000 is also dangerous, so we start after that
67 //XXX: there are more virtual regions we
68 //are not allowed to use -> find out where to reserve those
69 #define VSPACE_BEGIN   ((lvaddr_t)(1UL*1024*1024*1024 + 64UL*1024*1024))    //0x44000000
70 #else       //"normal" arm architectures
71 #define VSPACE_BEGIN   ((lvaddr_t)1UL*1024*1024*1024)   //0x40000000
72 #endif
73
74
75 // Amount of virtual address space reserved for mapping frames
76 // backing refill_slabs.
77 //#define META_DATA_RESERVED_SPACE (BASE_PAGE_SIZE * 128) // 64
78 #define META_DATA_RESERVED_SPACE (BASE_PAGE_SIZE * 256)
79 // increased above value from 128 for pandaboard port
80
81 // Convenience macros to figure out user space page table indices
82 // we use 10 bits for both L1 and L2 tables in user space, even though
83 // in hardware we use 12 bits for L1 and 8 bits for L2.
84 #define ARM_USER_L1_OFFSET(addr) ((uintptr_t)(addr >> 22) & 0x3ffu)
85 #define ARM_USER_L2_OFFSET(addr) ((uintptr_t)(addr >> 12) & 0x3ffu)
86
87 static inline uintptr_t
88 vregion_flags_to_kpi_paging_flags(vregion_flags_t flags)
89 {
90     STATIC_ASSERT(0x1ff == VREGION_FLAGS_MASK, "");
91     STATIC_ASSERT(0x0f == KPI_PAGING_FLAGS_MASK, "");
92     STATIC_ASSERT(VREGION_FLAGS_READ    == KPI_PAGING_FLAGS_READ,    "");
93     STATIC_ASSERT(VREGION_FLAGS_WRITE   == KPI_PAGING_FLAGS_WRITE,   "");
94     STATIC_ASSERT(VREGION_FLAGS_EXECUTE == KPI_PAGING_FLAGS_EXECUTE, "");
95     STATIC_ASSERT(VREGION_FLAGS_NOCACHE == KPI_PAGING_FLAGS_NOCACHE, "");
96     if ((flags & VREGION_FLAGS_MPB) != 0) {
97         // XXX: ignore MPB flag on ARM, otherwise the assert below fires -AB
98         flags &= ~VREGION_FLAGS_MPB;
99     }
100     if ((flags & VREGION_FLAGS_WRITE_COMBINING) != 0) {
101         // XXX mask out write-combining flag on ARM
102         flags &= ~VREGION_FLAGS_WRITE_COMBINING;
103     }
104     if ((flags & VREGION_FLAGS_GUARD) != 0) {
105         flags = 0;
106     }
107     assert(0 == (~KPI_PAGING_FLAGS_MASK & (uintptr_t)flags));
108     return (uintptr_t)flags;
109 }
110
111 // debug print preprocessor flag for this file
112 //#define LIBBARRELFISH_DEBUG_PMAP
113
114 /**
115  * \brief check whether region A = [start_a .. end_a) overlaps
116  * region B = [start_b .. end_b).
117  * \return true iff A overlaps B
118  */
119 static bool is_overlapping(uint16_t start_a, uint16_t end_a, uint16_t start_b, uint16_t end_b)
120 {
121     return
122         // B strict subset of A
123         (start_a < start_b && end_a >= end_b)
124         // start_a inside B
125         || (start_a >= start_b && start_a < end_b)
126         // end_a inside B
127         || (end_a > start_b && end_a < end_b);
128 }
129
130 /**
131  * \brief Check whether vnode `root' has entries in between [entry ..
132  * entry+len).
133  * \param root the vnode to look at
134  * \param entry first entry of the region to check
135  * \param len   length of the region to check
136  * \param only_pages true == do not report previously allocated lower-level
137  *                   page tables that are empty
138  * \return true iff entries exist in region.
139  */
140 #if defined(LIBBARRELFISH_DEBUG_PMAP)
141 #define DEBUG_HAS_VNODE
142 #endif
143 static bool has_vnode(struct vnode *root, uint32_t entry, size_t len,
144                bool only_pages)
145 {
146     assert(root != NULL);
147     assert(root->is_vnode);
148     struct vnode *n;
149
150     uint32_t end_entry = entry + len;
151 #ifdef DEBUG_HAS_VNODE
152     debug_printf("%s: checking region [%"PRIu32"--%"PRIu32"], only_pages = %d\n",
153             __FUNCTION__, entry, end_entry, only_pages);
154 #endif
155
156     bool found_pages = false;
157     for (n = root->u.vnode.children; n; n = n->next) {
158         // region to check [entry .. end_entry)
159         if (n->is_vnode) {
160 #ifdef DEBUG_HAS_VNODE
161             debug_printf("%s: looking at vnode: entry = %d, mapped = %"PRIx8"\n",
162                     __FUNCTION__, n->entry, n->u.vnode.mapped);
163 #endif
164             // n is page table, we need to check if each mapped 1k L2 overlaps
165             // with the region to check [entry .. end_entry)
166             for (int i = 0; i < L2_PER_PAGE; i++) {
167                 // ith 1k L2 is mapped
168 #ifdef DEBUG_HAS_VNODE
169                 debug_printf("%s: n->u.vnode.mapped & (1 << %d) == %d\n",
170                         __FUNCTION__, i, n->u.vnode.mapped & (1 << i));
171 #endif
172                 if (L2_IS_MAPPED(n, i)) {
173 #ifdef DEBUG_HAS_VNODE
174                     debug_printf("%s: check overlapping: %"PRIu32"--%"PRIu32
175                             " <> %"PRIu32"--%"PRIu32"\n",
176                             __FUNCTION__, entry, end_entry,
177                             n->entry + i, n->entry + i + 1);
178 #endif
179                     if (is_overlapping(entry, end_entry, n->entry + i, n->entry + i + 1)) {
180                         if (only_pages) {
181                             uint16_t rec_start = i * PTABLE_SIZE;
182 #ifdef DEBUG_HAS_VNODE
183                             debug_printf("%s: checking recursively in %"PRIu16"--%"PRIu16"\n",
184                                     __FUNCTION__, rec_start, rec_start + PTABLE_SIZE);
185 #endif
186                             found_pages = found_pages
187                                 || has_vnode(n, rec_start, PTABLE_SIZE, true);
188                             if (found_pages) {
189                                 return true;
190                             }
191                         } else {
192                             return true;
193                         }
194                     }
195                 }
196             }
197         } else {
198             // not vnode
199             uint32_t end = n->entry + n->u.frame.pte_count;
200 #ifdef DEBUG_HAS_VNODE
201             debug_printf("%s: looking at region: [%"PRIu32"--%"PRIu32"]\n",
202                     __FUNCTION__, n->entry, end);
203 #endif
204
205             // do checks
206             if (is_overlapping(entry, end_entry, n->entry, end)) {
207                 return true;
208             }
209         }
210     }
211
212     return false;
213 }
214
215 /**
216  * \brief Starting at a given root, return the vnode with entry equal to #entry
217  * \return vnode at index `entry` or NULL
218  */
219 #ifdef LIBBARRELFISH_DEBUG_PMAP
220 #define DEBUG_FIND_VNODE
221 #endif
222 static struct vnode *find_vnode(struct vnode *root, uint16_t entry)
223 {
224     assert(root != NULL);
225     assert(root->is_vnode);
226     struct vnode *n;
227
228 #ifdef DEBUG_FIND_VNODE
229     debug_printf("%s: looking for %"PRIu16"\n", __FUNCTION__, entry);
230 #endif
231
232     for(n = root->u.vnode.children; n != NULL; n = n->next) {
233         if (n->is_vnode &&
234             is_overlapping(entry, entry + 1, n->entry, n->entry + L2_PER_PAGE)) {
235 #ifdef DEBUG_FIND_VNODE
236             debug_printf("%s: found ptable at [%"PRIu16"--%"PRIu16"]\n",
237                     __FUNCTION__, n->entry, n->entry + L2_PER_PAGE);
238 #endif
239             return n;
240         }
241         else if (n->is_vnode) {
242             assert(!is_overlapping(entry, entry + 1, n->entry, n->entry + L2_PER_PAGE));
243             // ignore all other vnodes;
244             continue;
245         }
246
247         // not vnode
248         assert(!n->is_vnode);
249         uint16_t end = n->entry + n->u.frame.pte_count;
250 #ifdef DEBUG_FIND_VNODE
251         debug_printf("%s: looking at section [%"PRIu16"--%"PRIu16"]\n", __FUNCTION__, n->entry, end);
252 #endif
253         if (n->entry <= entry && entry < end) {
254 #ifdef DEBUG_FIND_VNODE
255             debug_printf("%d \\in [%d, %d]\n", entry, n->entry, end);
256 #endif
257             return n;
258         }
259     }
260     return NULL;
261 }
262
263 /**
264  * \brief check whether region [entry, entry+npages) is contained in a child
265  * of `root`.
266  */
267 static bool inside_region(struct vnode *root, uint32_t entry, uint32_t npages)
268 {
269     assert(root != NULL);
270     assert(root->is_vnode);
271
272     struct vnode *n;
273
274     for (n = root->u.vnode.children; n; n = n->next) {
275         if (!n->is_vnode) {
276             uint16_t end = n->entry + n->u.frame.pte_count;
277             if (n->entry <= entry && entry + npages <= end) {
278                 return true;
279             }
280         }
281     }
282
283     return false;
284 }
285
286 /**
287  * \brief remove vnode `item` from linked list of children of `root`
288  */
289 static void remove_vnode(struct vnode *root, struct vnode *item)
290 {
291     assert(root->is_vnode);
292     struct vnode *walk = root->u.vnode.children;
293     struct vnode *prev = NULL;
294     while (walk) {
295         if (walk == item) {
296             if (prev) {
297                 prev->next = walk->next;
298                 return;
299             } else {
300                 root->u.vnode.children = walk->next;
301                 return;
302             }
303         }
304         prev = walk;
305         walk = walk->next;
306     }
307     USER_PANIC("Should not get here");
308 }
309
310 static void unmap_l2_table(struct vnode *root, struct vnode *n, uint16_t e)
311 {
312     errval_t err;
313     uint32_t entry = ROUND_DOWN(n->entry, L2_PER_PAGE) + e;
314     if (L2_IS_MAPPED(n, e)) {
315         err = vnode_unmap(root->u.vnode.cap[0], n->u.vnode.cap[e],
316                 entry, 1);
317         if (err_is_fail(err)) {
318             debug_printf("remove_empty_vnodes: vnode_unmap: %s\n",
319                     err_getstring(err));
320             abort();
321         }
322
323         // delete capability, if not entry 0
324         if (e) {
325             err = cap_destroy(n->u.vnode.cap[e]);
326             if (err_is_fail(err)) {
327                 debug_printf("remove_empty_vnodes: cap_destroy: %s\n",
328                         err_getstring(err));
329                 abort();
330             }
331         }
332     }
333 }
334
335 /**
336  * \brief (recursively) remove empty page tables in region [entry ..
337  * entry+len) in vnode `root`.
338  */
339 #ifdef LIBBARRELFISH_DEBUG_PMAP
340 #define DEBUG_REMOVE_EMPTY_VNODES
341 #endif
342 static void remove_empty_vnodes(struct slab_allocator *vnode_alloc, struct vnode *root,
343                          uint32_t entry, size_t len)
344 {
345     // precondition: root does not have pages in [entry, entry+len)
346     assert(!has_vnode(root, entry, len, true));
347
348     errval_t err;
349     uint32_t end_entry = entry + len;
350     for (struct vnode *n = root->u.vnode.children; n; n = n->next) {
351         // sanity check and skip leaf entries
352         if (!n->is_vnode) {
353             continue;
354         }
355         // here we know that all vnodes we're interested in are
356         // page tables
357         assert(n->is_vnode);
358
359         if (entry < n->entry && end_entry >= n->entry + L2_PER_PAGE) {
360             // whole entry in region: completely unmap&free L2 page
361             for (int i = 0; i < L2_PER_PAGE; i++) {
362                 unmap_l2_table(root, n, i);
363             }
364
365             // delete last copy of pt cap
366             err = cap_destroy(n->u.vnode.cap[0]);
367             assert(err_is_ok(err));
368
369             // remove vnode from list
370             remove_vnode(root, n);
371             slab_free(vnode_alloc, n);
372         } else if (entry >= n->entry && entry < n->entry + L2_PER_PAGE) {
373             // tail end of vnode in region:
374             uint16_t e = entry - n->entry;
375 #ifdef DEBUG_REMOVE_EMPTY_VNODES
376             debug_printf("overlap: %"PRIu16" entries\n", e);
377 #endif
378             for (; e < L2_PER_PAGE; e++) {
379                 unmap_l2_table(root, n, e);
380             }
381         } else if (end_entry > n->entry && end_entry < n->entry + L2_PER_PAGE) {
382             // start of vnode in region
383             uint16_t e = end_entry - n->entry;
384 #ifdef DEBUG_REMOVE_EMPTY_VNODES
385             debug_printf("overlap: %"PRIu16" entries\n", e);
386 #endif
387             for (int i = 0; i < e; i++) {
388                 unmap_l2_table(root, n, i);
389             }
390         }
391     }
392 }
393
394 /**
395  * \brief Allocates a new VNode, adding it to the page table and our metadata
396  */
397 static errval_t alloc_vnode(struct pmap_arm *pmap_arm, struct vnode *root,
398                             enum objtype type, uint32_t entry,
399                             struct vnode **retvnode)
400 {
401     assert(root->is_vnode);
402     errval_t err;
403
404     struct vnode *newvnode = slab_alloc(&pmap_arm->slab);
405     if (newvnode == NULL) {
406         return LIB_ERR_SLAB_ALLOC_FAIL;
407     }
408     newvnode->is_vnode = true;
409
410     // The VNode capability
411     err = slot_alloc(&newvnode->u.vnode.cap[0]);
412     if (err_is_fail(err)) {
413         return err_push(err, LIB_ERR_SLOT_ALLOC);
414     }
415
416     err = vnode_create(newvnode->u.vnode.cap[0], type);
417     if (err_is_fail(err)) {
418         return err_push(err, LIB_ERR_VNODE_CREATE);
419     }
420     for (int i = 1; i < L2_PER_PAGE; i++) {
421         newvnode->u.vnode.cap[i] = NULL_CAP;
422     }
423
424     // The VNode meta data
425     newvnode->entry            = ROUND_DOWN(entry, L2_PER_PAGE);
426     assert(newvnode->entry % L2_PER_PAGE == 0);
427     newvnode->next             = root->u.vnode.children;
428     newvnode->u.vnode.mapped   = 0x0; // no entries mapped
429     root->u.vnode.children     = newvnode;
430     newvnode->u.vnode.children = NULL;
431
432     if (retvnode) {
433         *retvnode = newvnode;
434     }
435     return SYS_ERR_OK;
436 }
437
438 /**
439  * \brief Returns the vnode for the pagetable mapping a given vspace address
440  */
441 #ifdef LIBBARRELFISH_DEBUG_PMAP
442 #define DEBUG_GET_PTABLE
443 #endif
444 static errval_t get_ptable(struct pmap_arm  *pmap,
445                            genvaddr_t        vaddr,
446                            struct vnode    **ptable)
447 {
448     // NB Strictly there are 12 bits in the ARM L1, but allocations unit
449     // of L2 is 1 page of L2 entries (4 tables) so we use 10 bits for the L1
450     // idx here
451     uintptr_t idx = ARM_L1_OFFSET(vaddr);
452     uintptr_t page_idx = L2_PAGE_IDX(idx);
453     if ((*ptable = find_vnode(&pmap->root, idx)) == NULL)
454     {
455         // L1 table entries point to L2 tables so allocate an L2
456         // table for this L1 entry.
457
458         struct vnode *tmp = NULL; // Tmp variable for passing to alloc_vnode
459
460 #ifdef DEBUG_GET_PTABLE
461         uintptr_t fidx = ROUND_DOWN(idx, L2_PER_PAGE);
462         debug_printf("allocating 4 x L2, entries = %"PRIuPTR"--%z"PRIuPTR"\n",
463                  fidx, fidx+4);
464 #endif
465         errval_t err = alloc_vnode(pmap, &pmap->root, ObjType_VNode_ARM_l2,
466                                    idx, &tmp);
467         if (err_is_fail(err)) {
468             DEBUG_ERR(err, "alloc_vnode");
469             return err;
470         }
471         assert(tmp != NULL);
472         *ptable = tmp; // Set argument to received value
473
474         if (err_is_fail(err)) {
475             return err_push(err, LIB_ERR_PMAP_ALLOC_VNODE);
476         }
477     }
478     assert(ptable);
479     struct vnode *pt = *ptable;
480     if (!pt->is_vnode) {
481         debug_printf("found section @%d, trying to get ptable for %d\n",
482                 pt->entry, idx);
483     }
484     assert(pt->is_vnode);
485 #ifdef DEBUG_GET_PTABLE
486     debug_printf("have ptable: %p\n", pt);
487     debug_printf("mapped = %x\n", pt->u.vnode.mapped);
488     debug_printf("page_idx = %d\n", page_idx);
489     debug_printf("l2_is_mapped: %d\n", L2_IS_MAPPED(pt, page_idx));
490 #endif
491     if (!L2_IS_MAPPED(pt, page_idx)) {
492 #ifdef DEBUG_GET_PTABLE
493         debug_printf("need to map entry %d\n", page_idx);
494 #endif
495         errval_t err;
496         uintptr_t offset = L2_PAGE_OFFSET(idx);
497 #ifdef DEBUG_GET_PTABLE
498         debug_printf("mapping L1 entry %d at offset %"PRIuPTR"\n", idx, offset);
499 #endif
500
501         // create copy of ptable cap for this index, if it doesn't exist
502         // already
503         if (capref_is_null(pt->u.vnode.cap[page_idx])) {
504 #ifdef DEBUG_GET_PTABLE
505             debug_printf("allocating slot for chunk %d\n", page_idx);
506 #endif
507             err = slot_alloc(&pt->u.vnode.cap[page_idx]);
508             if (err_is_fail(err)) {
509                 return err_push(err, LIB_ERR_VNODE_MAP);
510             }
511
512 #ifdef DEBUG_GET_PTABLE
513             debug_printf("creating copy for chunk %d\n", page_idx);
514 #endif
515             err = cap_copy(pt->u.vnode.cap[page_idx], pt->u.vnode.cap[0]);
516             if (err_is_fail(err)) {
517                 return err_push(err, LIB_ERR_VNODE_MAP);
518             }
519         }
520
521 #ifdef DEBUG_GET_PTABLE
522             debug_printf("calling vnode_map() for chunk %d\n", page_idx);
523 #endif
524         // map single 1k ptable
525         err = vnode_map(pmap->root.u.vnode.cap[0], pt->u.vnode.cap[page_idx], idx,
526                 KPI_PAGING_FLAGS_READ | KPI_PAGING_FLAGS_WRITE, offset, 1);
527
528         if (err_is_fail(err)) {
529             return err_push(err, LIB_ERR_VNODE_MAP);
530         }
531
532         // set 1k ptable as mapped
533         pt->u.vnode.mapped |= 1 << page_idx;
534     }
535
536     return SYS_ERR_OK;
537 }
538
539 static struct vnode *find_ptable(struct pmap_arm  *pmap,
540                                  genvaddr_t vaddr)
541 {
542     // NB Strictly there are 12 bits in the ARM L1, but allocations unit
543     // of L2 is 1 page of L2 entries (4 tables) so
544     uintptr_t idx = ARM_L1_OFFSET(vaddr);
545     return find_vnode(&pmap->root, idx);
546 }
547
548 static errval_t do_single_map(struct pmap_arm *pmap, genvaddr_t vaddr, genvaddr_t vend,
549                               struct capref frame, size_t offset, size_t pte_count,
550                               vregion_flags_t flags)
551 {
552     errval_t err = SYS_ERR_OK;
553     // Get the page table
554     struct vnode *ptable;
555     uintptr_t entry;
556     bool is_large = false;
557
558     struct frame_identity fi;
559     err = invoke_frame_identify(frame, &fi);
560     if (err_is_fail(err)) {
561         return err_push(err, LIB_ERR_PMAP_FRAME_IDENTIFY);
562     }
563
564     if (flags & VREGION_FLAGS_LARGE &&
565         (vaddr & LARGE_PAGE_MASK) == 0 &&
566         fi.bits >= LARGE_PAGE_BITS &&
567         (fi.base & LARGE_PAGE_MASK) == 0) {
568         //section mapping (1MB)
569         //mapped in the L1 table at root
570         //
571         ptable = &pmap->root;
572         entry = ARM_L1_OFFSET(vaddr);
573         is_large = true;
574 #ifdef LIBBARRELFISH_DEBUG_PMAP
575         printf("do_single_map: large path: entry=%zu\n", entry);
576 #endif
577     } else {
578 #ifdef LIBBARRELFISH_DEBUG_PMAP
579         debug_printf("%s: 4k path: mapping %"PRIxGENVADDR"\n", __FUNCTION__, vaddr);
580         debug_printf("4k path: L1 entry: %zu\n", ARM_USER_L1_OFFSET(vaddr));
581 #endif
582         //4k mapping
583         // XXX: reassess the following note -SG
584         // NOTE: strictly speaking a l2 entry only has 8 bits, while a l1 entry
585         // has 12 bits, but due to the way Barrelfish allocates l1 and l2 tables,
586         // we use 10 bits for the entry here and in the map syscall
587         err = get_ptable(pmap, vaddr, &ptable);
588         if (err_is_fail(err)) {
589             DEBUG_ERR(err, "get_ptable() in do_single_map");
590             return err_push(err, LIB_ERR_PMAP_GET_PTABLE);
591         }
592         entry = ARM_USER_L2_OFFSET(vaddr);
593 #ifdef LIBBARRELFISH_DEBUG_PMAP
594         debug_printf("%s: 4k path: L2 entry=%zu\n", __FUNCTION__, entry);
595         debug_printf("%s: ptable->is_vnode = %d\n",
596                 __FUNCTION__, ptable->is_vnode);
597 #endif
598     }
599
600     // convert flags
601     flags &= ~(VREGION_FLAGS_LARGE | VREGION_FLAGS_HUGE);
602     uintptr_t pmap_flags = vregion_flags_to_kpi_paging_flags(flags);
603
604     uintptr_t user_pte_count = pte_count;
605     if (is_large) {
606         user_pte_count = DIVIDE_ROUND_UP(pte_count, L2_PER_PAGE);
607     }
608
609     // check if there is an overlapping mapping
610     if (has_vnode(ptable, entry, pte_count, false)) {
611 #ifdef LIBBARRELFISH_DEBUG_PMAP
612         debug_printf("has_vnode, only_pages=false  returned true\n");
613 #endif
614         if (has_vnode(ptable, entry, pte_count, true)) {
615             printf("page already exists in 0x%"
616                     PRIxGENVADDR"--0x%"PRIxGENVADDR"\n", vaddr, vend);
617             return LIB_ERR_PMAP_EXISTING_MAPPING;
618         } else {
619 #ifdef LIBBARRELFISH_DEBUG_PMAP
620             debug_printf("has_vnode, only_pages=true  returned false, cleaning up empty ptables\n");
621 #endif
622             // clean out empty page tables. We do this here because we benefit
623             // from having the page tables in place when doing lots of small
624             // mappings
625             // XXX: TODO: fix this + mapping of L2 to work on single 1k
626             // chunks
627             remove_empty_vnodes(&pmap->slab, ptable, entry, pte_count);
628         }
629     }
630
631     // Create user level datastructure for the mapping
632     struct vnode *page = slab_alloc(&pmap->slab);
633     assert(page);
634     page->is_vnode = false;
635     page->entry = entry;
636     page->next  = ptable->u.vnode.children;
637     ptable->u.vnode.children = page;
638     page->u.frame.cap = frame;
639     page->u.frame.flags = flags;
640     page->u.frame.pte_count = user_pte_count;
641     page->u.frame.kernel_pte_count = pte_count;
642
643     // Map entry into the page table
644     err = vnode_map(ptable->u.vnode.cap[0], frame, entry,
645                     pmap_flags, offset, pte_count);
646     if (err_is_fail(err)) {
647         return err_push(err, LIB_ERR_VNODE_MAP);
648     }
649     return SYS_ERR_OK;
650 }
651
652 static errval_t do_map(struct pmap_arm *pmap, genvaddr_t vaddr,
653                        struct capref frame, size_t offset, size_t size,
654                        vregion_flags_t flags, size_t *retoff, size_t *retsize)
655 {
656     errval_t err;
657     size_t page_size;
658     size_t offset_level;
659
660     // get base address and size of frame
661     struct frame_identity fi;
662     err = invoke_frame_identify(frame, &fi);
663     if (err_is_fail(err)) {
664         return err_push(err, LIB_ERR_PMAP_DO_MAP);
665     }
666
667     // determine mapping specific parts
668     if (flags & VREGION_FLAGS_LARGE &&
669         (vaddr & LARGE_PAGE_MASK) == 0 &&
670         fi.bits >= LARGE_PAGE_BITS &&
671         (fi.base & LARGE_PAGE_MASK) == 0) {
672         //section mapping (1MB)
673         page_size = LARGE_PAGE_SIZE;
674         offset_level = ARM_L1_OFFSET(vaddr);
675 #ifdef LIBBARRELFISH_DEBUG_PMAP
676         printf("do_map: large path\n");
677         printf("page_size: %zx, size: %zx\n", page_size, size);
678 #endif
679     } else {
680         //normal 4k mapping
681         page_size = BASE_PAGE_SIZE;
682         offset_level = ARM_L2_OFFSET(vaddr);
683     }
684
685     size = ROUND_UP(size, page_size);
686     size_t pte_count = DIVIDE_ROUND_UP(size, page_size);
687     if (flags & VREGION_FLAGS_LARGE) {
688 #ifdef LIBBARRELFISH_DEBUG_PMAP
689         printf("#pages: 0x%zu\n", pte_count);
690 #endif
691     }
692     genvaddr_t vend = vaddr + size;
693
694     if ((1UL << fi.bits) < size) {
695         return LIB_ERR_PMAP_FRAME_SIZE;
696     }
697
698     //should be trivially true for section mappings
699     if ((ARM_L1_OFFSET(vaddr) == ARM_L1_OFFSET(vend)) ||
700         flags & VREGION_FLAGS_LARGE) {
701         // fast path
702         err = do_single_map(pmap, vaddr, vend, frame, offset, pte_count, flags);
703         if (err_is_fail(err)) {
704             DEBUG_ERR(err, "[do_map] in fast path");
705             return err_push(err, LIB_ERR_PMAP_DO_MAP);
706         }
707     } else { // multiple leaf page tables
708         // first leaf
709         uint32_t c = ARM_L2_MAX_ENTRIES - offset_level;
710         genvaddr_t temp_end = vaddr + c * page_size;
711         err = do_single_map(pmap, vaddr, temp_end, frame, offset, c, flags);
712         if (err_is_fail(err)) {
713             return err_push(err, LIB_ERR_PMAP_DO_MAP);
714         }
715
716         // map full leaves
717         while (ARM_L1_OFFSET(temp_end) < ARM_L1_OFFSET(vend)) { // update vars
718             vaddr = temp_end;
719             temp_end = vaddr + ARM_L2_MAX_ENTRIES * page_size;
720             offset += c * page_size;
721             c = ARM_L2_MAX_ENTRIES;
722             // copy cap
723             struct capref next;
724             err = slot_alloc(&next);
725             if (err_is_fail(err)) {
726                 return err_push(err, LIB_ERR_PMAP_DO_MAP);
727             }
728             err = cap_copy(next, frame);
729             if (err_is_fail(err)) {
730                 return err_push(err, LIB_ERR_PMAP_DO_MAP);
731             }
732             frame = next;
733
734             // do mapping
735             err = do_single_map(pmap, vaddr, temp_end, frame, offset, ARM_L2_MAX_ENTRIES, flags);
736             if (err_is_fail(err)) {
737                 return err_push(err, LIB_ERR_PMAP_DO_MAP);
738             }
739         }
740
741         // map remaining part
742         offset += c * page_size;
743         c = ARM_L2_OFFSET(vend) - ARM_L2_OFFSET(temp_end);
744         if (c) {
745             // copy cap
746             struct capref next;
747             err = slot_alloc(&next);
748             if (err_is_fail(err)) {
749                 return err_push(err, LIB_ERR_PMAP_DO_MAP);
750             }
751             err = cap_copy(next, frame);
752             if (err_is_fail(err)) {
753                 return err_push(err, LIB_ERR_PMAP_DO_MAP);
754             }
755
756             // do mapping
757             err = do_single_map(pmap, temp_end, vend, next, offset, c, flags);
758             if (err_is_fail(err)) {
759                 return err_push(err, LIB_ERR_PMAP_DO_MAP);
760             }
761         }
762     }
763     if (retoff) {
764         *retoff = offset;
765     }
766     if (retsize) {
767         *retsize = size;
768     }
769     //has_vnode_debug = false;
770     return SYS_ERR_OK;
771 #if 0
772     errval_t err;
773     uintptr_t pmap_flags = vregion_flags_to_kpi_paging_flags(flags);
774
775     for (size_t i = offset; i < offset + size; i += BASE_PAGE_SIZE) {
776
777         vaddr += BASE_PAGE_SIZE;
778     }
779
780     if (retoff) {
781         *retoff = offset;
782     }
783     if (retsize) {
784         *retsize = size;
785     }
786     return SYS_ERR_OK;
787 #endif
788 }
789
790 static size_t
791 max_slabs_required(size_t bytes)
792 {
793     // Perform a slab allocation for every page (do_map -> slab_alloc)
794     size_t pages     = DIVIDE_ROUND_UP(bytes, BASE_PAGE_SIZE);
795     // Perform a slab allocation for every L2 (get_ptable -> find_vnode)
796     size_t l2entries = DIVIDE_ROUND_UP(pages, 256 * 4);
797     // Perform a slab allocation for every L1 (do_map -> find_vnode)
798     size_t l1entries = DIVIDE_ROUND_UP(l2entries, 1024);
799     return pages + l2entries + l1entries;
800 }
801 static size_t max_slabs_required_large(size_t bytes)
802 {
803     // always need only one slab, as we can represent any size section mapping
804     // in a single struct vnode.
805     return 1;
806 }
807
808 /**
809  * \brief Refill slabs used for metadata
810  *
811  * \param pmap     The pmap to refill in
812  * \param request  The number of slabs the allocator must have
813  * when the function returns
814  *
815  * When the current pmap is initialized,
816  * it reserves some virtual address space for metadata.
817  * This reserved address space is used here
818  *
819  * Can only be called for the current pmap
820  * Will recursively call into itself till it has enough slabs
821  */
822 #include <stdio.h>
823 static errval_t refill_slabs(struct pmap_arm *pmap, size_t request)
824 {
825     errval_t err;
826
827     /* Keep looping till we have #request slabs */
828     while (slab_freecount(&pmap->slab) < request) {
829         // Amount of bytes required for #request
830         size_t bytes = SLAB_STATIC_SIZE(request - slab_freecount(&pmap->slab),
831                                         sizeof(struct vnode));
832
833         /* Get a frame of that size */
834         struct capref cap;
835         err = frame_alloc(&cap, bytes, &bytes);
836         if (err_is_fail(err)) {
837             return err_push(err, LIB_ERR_FRAME_ALLOC);
838         }
839
840         /* If we do not have enough slabs to map the frame in, recurse */
841         size_t required_slabs_for_frame = max_slabs_required(bytes);
842         if (slab_freecount(&pmap->slab) < required_slabs_for_frame) {
843             // If we recurse, we require more slabs than to map a single page
844             assert(required_slabs_for_frame > 4);
845
846             err = refill_slabs(pmap, required_slabs_for_frame);
847             if (err_is_fail(err)) {
848                 return err_push(err, LIB_ERR_SLAB_REFILL);
849             }
850         }
851
852         /* Perform mapping */
853         genvaddr_t genvaddr = pmap->vregion_offset;
854         pmap->vregion_offset += (genvaddr_t)bytes;
855
856         // if this assert fires, increase META_DATA_RESERVED_SPACE
857         assert(pmap->vregion_offset < (vregion_get_base_addr(&pmap->vregion) +
858                vregion_get_size(&pmap->vregion)));
859
860         err = do_map(pmap, genvaddr, cap, 0, bytes,
861                      VREGION_FLAGS_READ_WRITE, NULL, NULL);
862         if (err_is_fail(err)) {
863             return err_push(err, LIB_ERR_PMAP_DO_MAP);
864         }
865
866         /* Grow the slab */
867         lvaddr_t buf = vspace_genvaddr_to_lvaddr(genvaddr);
868         slab_grow(&pmap->slab, (void*)buf, bytes);
869     }
870
871     return SYS_ERR_OK;
872 }
873
874 /**
875  * \brief Create page mappings
876  *
877  * \param pmap     The pmap object
878  * \param vaddr    The virtual address to create the mapping for
879  * \param frame    The frame cap to map in
880  * \param offset   Offset into the frame cap
881  * \param size     Size of the mapping
882  * \param flags    Flags for the mapping
883  * \param retoff   If non-NULL, filled in with adjusted offset of mapped region
884  * \param retsize  If non-NULL, filled in with adjusted size of mapped region
885  */
886 static errval_t
887 map(struct pmap     *pmap,
888     genvaddr_t       vaddr,
889     struct capref    frame,
890     size_t           offset,
891     size_t           size,
892     vregion_flags_t  flags,
893     size_t          *retoff,
894     size_t          *retsize)
895 {
896     struct pmap_arm *pmap_arm = (struct pmap_arm *)pmap;
897
898     errval_t err;
899     size_t base;
900     size_t page_size;
901     size_t slabs_required;
902
903     struct frame_identity fi;
904     err = invoke_frame_identify(frame, &fi);
905     if (err_is_fail(err)) {
906         return err_push(err, LIB_ERR_PMAP_FRAME_IDENTIFY);
907     }
908
909     // adjust the mapping to be on page boundaries
910     if (flags & VREGION_FLAGS_LARGE &&
911         (vaddr & LARGE_PAGE_MASK) == 0 &&
912         fi.bits >= LARGE_PAGE_BITS &&
913         (fi.base & LARGE_PAGE_MASK) == 0) {
914         //section mapping (1MB)
915         base = LARGE_PAGE_OFFSET(offset);
916         page_size = LARGE_PAGE_SIZE;
917         slabs_required = max_slabs_required_large(size);
918 #ifdef LIBBARRELFISH_DEBUG_PMAP
919         size_t frame_sz = 1ULL<<fi.bits;
920         printf("map: large path, page_size: %i, base: %i, slabs: %i, size: %i,"
921                 "frame size: %zu\n", page_size, base, slabs_required, size, frame_sz);
922 #endif
923     } else {
924         //4k mapping
925         base = BASE_PAGE_OFFSET(offset);
926         page_size = BASE_PAGE_SIZE;
927         slabs_required = max_slabs_required(size);
928     }
929     size   += base;
930     size    = ROUND_UP(size, page_size);
931     offset -= base;
932
933     const size_t slabs_reserve = 3; // == max_slabs_required(1)
934     uint64_t  slabs_free       = slab_freecount(&pmap_arm->slab);
935
936     slabs_required += slabs_reserve;
937
938     if (slabs_required > slabs_free) {
939         if (get_current_pmap() == pmap) {
940             err = refill_slabs(pmap_arm, slabs_required);
941             if (err_is_fail(err)) {
942                 return err_push(err, LIB_ERR_SLAB_REFILL);
943             }
944         }
945         else {
946             size_t bytes = SLAB_STATIC_SIZE(slabs_required - slabs_free,
947                                             sizeof(struct vnode));
948             void *buf = malloc(bytes);
949             if (!buf) {
950                 return LIB_ERR_MALLOC_FAIL;
951             }
952             slab_grow(&pmap_arm->slab, buf, bytes);
953         }
954     }
955
956     return do_map(pmap_arm, vaddr, frame, offset, size, flags,
957                   retoff, retsize);
958 }
959
960 static errval_t do_single_unmap(struct pmap_arm *pmap, genvaddr_t vaddr,
961                                 size_t pte_count, bool delete_cap)
962 {
963     errval_t err;
964     struct vnode *pt = find_ptable(pmap, vaddr);
965     // pt->is_vnode == non-large mapping
966     if (pt && pt->is_vnode) {
967         // analog to do_single_map we use 10 bits for tracking pages in user space -SG
968         struct vnode *page = find_vnode(pt, ARM_USER_L2_OFFSET(vaddr));
969         if (page && page->u.frame.pte_count == pte_count) {
970             err = vnode_unmap(pt->u.vnode.cap[0], page->u.frame.cap,
971                               page->entry, page->u.frame.pte_count);
972             if (err_is_fail(err)) {
973                 DEBUG_ERR(err, "vnode_unmap");
974                 return err_push(err, LIB_ERR_VNODE_UNMAP);
975             }
976
977             // Free up the resources
978             if (delete_cap) {
979                 err = cap_destroy(page->u.frame.cap);
980                 if (err_is_fail(err)) {
981                     return err_push(err, LIB_ERR_PMAP_DO_SINGLE_UNMAP);
982                 }
983             }
984             remove_vnode(pt, page);
985             slab_free(&pmap->slab, page);
986         }
987         else {
988             return LIB_ERR_PMAP_FIND_VNODE;
989         }
990     } else if (pt) {
991 #ifdef LIBBARRELFISH_DEBUG_PMAP
992         debug_printf("section unmap: entry = %zu, pte_count = %zu\n",
993                 pt->entry, pt->u.frame.kernel_pte_count);
994 #endif
995         err = vnode_unmap(pmap->root.u.vnode.cap[0], pt->u.frame.cap,
996                           pt->entry, pt->u.frame.kernel_pte_count);
997         if (err_is_fail(err)) {
998             DEBUG_ERR(err, "vnode_unmap");
999             return err_push(err, LIB_ERR_VNODE_UNMAP);
1000         }
1001
1002         remove_vnode(&pmap->root, pt);
1003         slab_free(&pmap->slab, pt);
1004     } else {
1005         return LIB_ERR_PMAP_FIND_VNODE;
1006     }
1007
1008     return SYS_ERR_OK;
1009 }
1010
1011 /**
1012  * \brief Remove page mappings
1013  *
1014  * \param pmap     The pmap object
1015  * \param vaddr    The start of the virtual addres to remove
1016  * \param size     The size of virtual address to remove
1017  * \param retsize  If non-NULL, filled in with the actual size removed
1018  */
1019 static errval_t
1020 unmap(struct pmap *pmap,
1021       genvaddr_t   vaddr,
1022       size_t       size,
1023       size_t      *retsize)
1024 {
1025     errval_t err, ret = SYS_ERR_OK;
1026     struct pmap_arm *pmap_arm = (struct pmap_arm*)pmap;
1027     size = ROUND_UP(size, BASE_PAGE_SIZE);
1028     size_t pte_count = size / BASE_PAGE_SIZE;
1029     genvaddr_t vend = vaddr + size;
1030
1031     if (ARM_L1_OFFSET(vaddr) == ARM_L1_OFFSET(vend-1)) {
1032         // fast path
1033         err = do_single_unmap(pmap_arm, vaddr, pte_count, false);
1034         if (err_is_fail(err)) {
1035             return err_push(err, LIB_ERR_PMAP_UNMAP);
1036         }
1037     } else { // slow path
1038         // unmap first leaf
1039         uint32_t c = ARM_L2_MAX_ENTRIES - ARM_L2_OFFSET(vaddr);
1040         err = do_single_unmap(pmap_arm, vaddr, c, false);
1041         if (err_is_fail(err)) {
1042             return err_push(err, LIB_ERR_PMAP_UNMAP);
1043         }
1044
1045         // unmap full leaves
1046         vaddr += c * BASE_PAGE_SIZE;
1047         while (ARM_L1_OFFSET(vaddr) < ARM_L1_OFFSET(vend)) {
1048             c = ARM_L2_MAX_ENTRIES;
1049             err = do_single_unmap(pmap_arm, vaddr, c, true);
1050             if (err_is_fail(err)) {
1051                 return err_push(err, LIB_ERR_PMAP_UNMAP);
1052             }
1053             vaddr += c * BASE_PAGE_SIZE;
1054         }
1055
1056         // unmap remaining part
1057         c = ARM_L2_OFFSET(vend) - ARM_L2_OFFSET(vaddr);
1058         if (c) {
1059             err = do_single_unmap(pmap_arm, vaddr, c, true);
1060             if (err_is_fail(err)) {
1061                 return err_push(err, LIB_ERR_PMAP_UNMAP);
1062             }
1063         }
1064     }
1065
1066     if (retsize) {
1067         *retsize = size;
1068     }
1069
1070     return ret;
1071 }
1072
1073 /**
1074  * \brief Determine a suitable address for a given memory object
1075  *
1076  * \param pmap    The pmap object
1077  * \param memobj  The memory object to determine the address for
1078  * \param alignment Minimum alignment
1079  * \param vaddr   Pointer to return the determined address
1080  *
1081  * Relies on vspace.c code maintaining an ordered list of vregions
1082  */
1083 static errval_t
1084 determine_addr(struct pmap   *pmap,
1085                struct memobj *memobj,
1086                size_t        alignment,
1087                genvaddr_t    *vaddr)
1088 {
1089     assert(pmap->vspace->head);
1090
1091     if (alignment == 0) {
1092         alignment = BASE_PAGE_SIZE;
1093     } else {
1094         alignment = ROUND_UP(alignment, BASE_PAGE_SIZE);
1095     }
1096     size_t size = ROUND_UP(memobj->size, alignment);
1097
1098     struct vregion *walk = pmap->vspace->head;
1099     while (walk->next) { // Try to insert between existing mappings
1100         genvaddr_t walk_base = vregion_get_base_addr(walk);
1101         genvaddr_t walk_size = ROUND_UP(vregion_get_size(walk), BASE_PAGE_SIZE);
1102         genvaddr_t walk_end  = ROUND_UP(walk_base + walk_size, alignment);
1103         genvaddr_t next_base = vregion_get_base_addr(walk->next);
1104
1105         if (next_base > walk_end + size &&
1106             walk_base + walk_size > VSPACE_BEGIN) { // Ensure mappings are larger than VSPACE_BEGIN
1107             *vaddr = walk_end;
1108             return SYS_ERR_OK;
1109         }
1110         walk = walk->next;
1111     }
1112
1113     *vaddr = ROUND_UP((vregion_get_base_addr(walk)
1114                        + ROUND_UP(vregion_get_size(walk), alignment)),
1115                        alignment);
1116     return SYS_ERR_OK;
1117 }
1118
1119 /** \brief Retrieves an address that can currently be used for large mappings
1120   *
1121   */
1122 static errval_t determine_addr_raw(struct pmap *pmap, size_t size,
1123                                    size_t alignment, genvaddr_t *retvaddr)
1124 {
1125     struct pmap_arm *pmap_arm = (struct pmap_arm *)pmap;
1126
1127     struct vnode *walk_pdir = pmap_arm->root.u.vnode.children;
1128     assert(walk_pdir != NULL); // assume there's always at least one existing entry
1129
1130     if (alignment == 0) {
1131         alignment = BASE_PAGE_SIZE;
1132     } else {
1133         alignment = ROUND_UP(alignment, BASE_PAGE_SIZE);
1134     }
1135     size = ROUND_UP(size, alignment);
1136
1137     size_t free_count = DIVIDE_ROUND_UP(size, LARGE_PAGE_SIZE);
1138     //debug_printf("need %zu contiguous free pdirs\n", free_count);
1139
1140     // compile pdir free list
1141     // barrelfish treats L1 as 1024 entries
1142     bool f[1024];
1143     for (int i = 0; i < 1024; i++) {
1144         f[i] = true;
1145     }
1146     f[walk_pdir->entry] = false;
1147     while (walk_pdir) {
1148         assert(walk_pdir->is_vnode);
1149         f[walk_pdir->entry] = false;
1150         walk_pdir = walk_pdir->next;
1151     }
1152     genvaddr_t first_free = 384;
1153     for (; first_free < 512; first_free++) {
1154         if (f[first_free]) {
1155             for (int i = 1; i < free_count; i++) {
1156                 if (!f[first_free + i]) {
1157                     // advance pointer
1158                     first_free = first_free+i;
1159                     goto next;
1160                 }
1161             }
1162             break;
1163         }
1164 next:
1165         assert(1 == 1);// make compiler shut up about label
1166     }
1167     //printf("first free: %li\n", (uint32_t)first_free);
1168     if (first_free + free_count <= 512) {
1169         *retvaddr = first_free << 22;
1170         return SYS_ERR_OK;
1171     } else {
1172         return LIB_ERR_OUT_OF_VIRTUAL_ADDR;
1173     }
1174 }
1175
1176
1177
1178 static errval_t do_single_modify_flags(struct pmap_arm *pmap, genvaddr_t vaddr,
1179                                        size_t pages, vregion_flags_t flags)
1180 {
1181     errval_t err = SYS_ERR_OK;
1182     struct vnode *ptable = find_ptable(pmap, vaddr);
1183     uint16_t ptentry = ARM_USER_L2_OFFSET(vaddr);
1184     if (ptable) {
1185         struct vnode *page = find_vnode(ptable, ptentry);
1186         if (page) {
1187             if (inside_region(ptable, ptentry, pages)) {
1188                 // we're modifying part of a valid mapped region
1189                 // arguments to invocation: invoke frame cap, first affected
1190                 // page (as offset from first page in mapping), #affected
1191                 // pages, new flags. Invocation should check compatibility of
1192                 // new set of flags with cap permissions.
1193                 size_t off = ptentry - page->entry;
1194                 uintptr_t pmap_flags = vregion_flags_to_kpi_paging_flags(flags);
1195                 err = invoke_frame_modify_flags(page->u.frame.cap, off, pages, pmap_flags);
1196                 printf("invoke_frame_modify_flags returned error: %s (%"PRIuERRV")\n",
1197                         err_getstring(err), err);
1198                 return err;
1199             } else {
1200                 // overlaps some region border
1201                 return LIB_ERR_PMAP_EXISTING_MAPPING;
1202             }
1203         }
1204     }
1205     return SYS_ERR_OK;
1206 }
1207
1208 /**
1209  * \brief Modify page mapping
1210  *
1211  * \param pmap     The pmap object
1212  * \param vaddr    The virtual address to unmap
1213  * \param flags    New flags for the mapping
1214  * \param retsize  If non-NULL, filled in with the actual size modified
1215  */
1216 static errval_t
1217 modify_flags(struct pmap     *pmap,
1218              genvaddr_t       vaddr,
1219              size_t           size,
1220              vregion_flags_t  flags,
1221              size_t          *retsize)
1222 {
1223     errval_t err, ret = SYS_ERR_OK;
1224     struct pmap_arm *pmap_arm = (struct pmap_arm*)pmap;
1225     size = ROUND_UP(size, BASE_PAGE_SIZE);
1226     size_t pte_count = size / BASE_PAGE_SIZE;
1227     genvaddr_t vend = vaddr + size;
1228
1229     if (ARM_L1_OFFSET(vaddr) == ARM_L1_OFFSET(vend-1)) {
1230         // fast path
1231         err = do_single_modify_flags(pmap_arm, vaddr, pte_count, false);
1232         if (err_is_fail(err)) {
1233             return err_push(err, LIB_ERR_PMAP_UNMAP);
1234         }
1235     }
1236     else { // slow path
1237         // unmap first leaf
1238         uint32_t c = ARM_L2_MAX_ENTRIES - ARM_L2_OFFSET(vaddr);
1239         err = do_single_modify_flags(pmap_arm, vaddr, c, false);
1240         if (err_is_fail(err)) {
1241             return err_push(err, LIB_ERR_PMAP_UNMAP);
1242         }
1243
1244         // unmap full leaves
1245         vaddr += c * BASE_PAGE_SIZE;
1246         while (ARM_L1_OFFSET(vaddr) < ARM_L1_OFFSET(vend)) {
1247             c = ARM_L2_MAX_ENTRIES;
1248             err = do_single_modify_flags(pmap_arm, vaddr, c, true);
1249             if (err_is_fail(err)) {
1250                 return err_push(err, LIB_ERR_PMAP_UNMAP);
1251             }
1252             vaddr += c * BASE_PAGE_SIZE;
1253         }
1254
1255         // unmap remaining part
1256         c = ARM_L2_OFFSET(vend) - ARM_L2_OFFSET(vaddr);
1257         if (c) {
1258             err = do_single_modify_flags(pmap_arm, vaddr, c, true);
1259             if (err_is_fail(err)) {
1260                 return err_push(err, LIB_ERR_PMAP_UNMAP);
1261             }
1262         }
1263     }
1264
1265     if (retsize) {
1266         *retsize = size;
1267     }
1268
1269     return ret;
1270 }
1271
1272 /**
1273  * \brief Query existing page mapping
1274  *
1275  * \param pmap     The pmap object
1276  * \param vaddr    The virtual address to query
1277  * \param retvaddr Returns the base virtual address of the mapping
1278  * \param retsize  Returns the actual size of the mapping
1279  * \param retcap   Returns the cap mapped at this address
1280  * \param retoffset Returns the offset within the cap that is mapped
1281  * \param retflags Returns the flags for this mapping
1282  *
1283  * All of the ret parameters are optional.
1284  */
1285 static errval_t lookup(struct pmap *pmap, genvaddr_t vaddr,
1286                        genvaddr_t *retvaddr, size_t *retsize,
1287                        struct capref *retcap, genvaddr_t *retoffset,
1288                        vregion_flags_t *retflags)
1289 {
1290     USER_PANIC("NYI");
1291     return 0;
1292 }
1293
1294
1295 static errval_t
1296 serialise(struct pmap *pmap, void *buf, size_t buflen)
1297 {
1298     // Unimplemented: ignored
1299     return SYS_ERR_OK;
1300 }
1301
1302 static errval_t
1303 deserialise(struct pmap *pmap, void *buf, size_t buflen)
1304 {
1305     // Unimplemented: we start with an empty pmap, and avoid the bottom of the A/S
1306     return SYS_ERR_OK;
1307 }
1308
1309 static struct pmap_funcs pmap_funcs = {
1310     .determine_addr = determine_addr,
1311     .determine_addr_raw = determine_addr_raw,
1312     .map = map,
1313     .unmap = unmap,
1314     .modify_flags = modify_flags,
1315     .lookup = lookup,
1316     .serialise = serialise,
1317     .deserialise = deserialise,
1318 };
1319
1320 /**
1321  * \brief Initialize the pmap object
1322  */
1323 errval_t
1324 pmap_init(struct pmap   *pmap,
1325           struct vspace *vspace,
1326           struct capref  vnode,
1327           struct slot_allocator *opt_slot_alloc)
1328 {
1329     struct pmap_arm* pmap_arm = (struct pmap_arm*)pmap;
1330
1331     /* Generic portion */
1332     pmap->f = pmap_funcs;
1333     pmap->vspace = vspace;
1334
1335     // Slab allocator for vnodes
1336     slab_init(&pmap_arm->slab, sizeof(struct vnode), NULL);
1337     slab_grow(&pmap_arm->slab,
1338               pmap_arm->slab_buffer,
1339               sizeof(pmap_arm->slab_buffer));
1340
1341     pmap_arm->root.is_vnode         = true;
1342     pmap_arm->root.u.vnode.cap[0]   = vnode;
1343     pmap_arm->root.next             = NULL;
1344     pmap_arm->root.u.vnode.children = NULL;
1345
1346     return SYS_ERR_OK;
1347 }
1348
1349 errval_t pmap_current_init(bool init_domain)
1350 {
1351     struct pmap_arm *pmap_arm = (struct pmap_arm*)get_current_pmap();
1352
1353     // To reserve a block of virtual address space,
1354     // a vregion representing the address space is required.
1355     // We construct a superficial one here and add it to the vregion list.
1356     struct vregion *vregion = &pmap_arm->vregion;
1357     assert((void*)vregion > (void*)pmap_arm);
1358     assert((void*)vregion < (void*)(pmap_arm + 1));
1359     vregion->vspace = NULL;
1360     vregion->memobj = NULL;
1361     vregion->base   = VSPACE_BEGIN;
1362     vregion->offset = 0;
1363     vregion->size   = META_DATA_RESERVED_SPACE;
1364     vregion->flags  = 0;
1365     vregion->next = NULL;
1366
1367     struct vspace *vspace = pmap_arm->p.vspace;
1368     assert(!vspace->head);
1369     vspace->head = vregion;
1370
1371     pmap_arm->vregion_offset = pmap_arm->vregion.base;
1372
1373     return SYS_ERR_OK;
1374 }