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