1/**
2 * \file
3 * \brief pmap management
4 */
5
6/*
7 * Copyright (c) 2010,2015, ETH Zurich.
8 * Copyright (c) 2015, Hewlett Packard Enterprise Development LP.
9 * All rights reserved.
10 *
11 * This file is distributed under the terms in the attached LICENSE file.
12 * If you do not find this file, copies can be found by writing to:
13 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
14 */
15
16/*
17 * There was some minor difficulty here with mapping the cpus native
18 * page table arrangement onto Barrelfish. The problem lies with
19 * resource bootstrapping. The bootstrap ram allocator allocates pages.
20 *
21 *
22 * The natural division of bits is 12/10/12, corresponding to 4K
23 * L1 entries in the L1 table and 256 L2 entries per L2
24 * table. Unfortunately 256 entries consumes 1KB rather than a
25 * page (4KB) so we pretend here and in the kernel caps page
26 * code that the L1 has 1024 entries and L2 tables are 4KB in
27 * size. The 4KB constraint comes from ram_alloc_fixed
28 * allocating single pages and the difficulty in bootstrapping
29 * cap slots (alloc_node takes a single slot.
30 *
31 * For now this suffices, but might need to be revisited in future.
32 *
33 * An earlier cut at this, used the first 1KB from each
34 * allocation made from ram_alloc_fixed and wasted the remaining
35 * space. Aside from the space wasted it entailed a couple of minor
36 * platform ifdefs to work around the discrepency.
37 *
38 * Alternative fixes discussed include:
39 *
40 * 1. avoid the need to create vnodes before connecting to a
41 *    real allocator (probably not plausible).
42 *
43 * 2. somehow make ram_alloc_fixed handle sub-page allocations
44 *    (it's clunky, but perhaps we can give each domain a separate
45 *     cnode full of 1k- sized RAM caps?)
46 *
47 * 3. handle the problem at the level of vnode_create (can't see how to
48 *    do this)
49 *
50 * 4. waste the space -- doing this cleanly will require a new parameter
51 * to retype to prevent all 4 caps being created
52 *
53 * 5. introduce a new arm-specific version of vnode_create that creates
54 * 4 1k vnodes, and is only called from the ARM VM code.
55 *
56 */
57
58#include <barrelfish/barrelfish.h>
59#include <barrelfish/caddr.h>
60#include <barrelfish/invocations_arch.h>
61#include <pmap_priv.h>
62#include <pmap_ds.h> // for selected pmap datastructure
63
64static inline paging_aarch64_flags_t
65vregion_flags_to_kpi_paging_flags(vregion_flags_t flags)
66{
67    STATIC_ASSERT(0x1ff == VREGION_FLAGS_MASK, "");
68    STATIC_ASSERT(0x0f == KPI_PAGING_FLAGS_MASK, "");
69    STATIC_ASSERT(VREGION_FLAGS_READ    == KPI_PAGING_FLAGS_READ,    "");
70    STATIC_ASSERT(VREGION_FLAGS_WRITE   == KPI_PAGING_FLAGS_WRITE,   "");
71    STATIC_ASSERT(VREGION_FLAGS_EXECUTE == KPI_PAGING_FLAGS_EXECUTE, "");
72    STATIC_ASSERT(VREGION_FLAGS_NOCACHE == KPI_PAGING_FLAGS_NOCACHE, "");
73    if ((flags & VREGION_FLAGS_MPB) != 0) {
74        // XXX: ignore MPB flag on ARM,
75        //      otherwise the assert below fires -AB
76        flags &= ~VREGION_FLAGS_MPB;
77    }
78    // XXX: Ignore VTD Snoop flag on AArch64 - this stuff really isn't
79    // portable -DC
80    flags &= ~VREGION_FLAGS_VTD_SNOOP;
81    if ((flags & VREGION_FLAGS_GUARD) != 0) {
82        flags = 0;
83    }
84
85    assert(0 == (~KPI_PAGING_FLAGS_MASK & (paging_aarch64_flags_t)flags));
86    return (paging_aarch64_flags_t)flags;
87}
88
89static bool has_vnode(struct vnode *root, uint16_t entry, size_t len)
90{
91    assert(root != NULL);
92    assert(root->v.is_vnode);
93    struct vnode *n;
94
95    uint32_t end_entry = entry + len;
96
97    pmap_foreach_child(root, n) {
98        assert(n);
99        if (n->v.is_vnode && n->v.entry == entry) {
100            return true;
101        }
102        // n is frame
103        uint32_t end = n->v.entry + n->v.u.frame.pte_count;
104        if (n->v.entry < entry && end > end_entry) {
105            return true;
106        }
107        if (n->v.entry >= entry && n->v.entry < end_entry) {
108            return true;
109        }
110    }
111
112    return false;
113}
114
115/**
116 * \brief Allocates a new VNode, adding it to the page table and our metadata
117 */
118static errval_t alloc_vnode(struct pmap_aarch64 *pmap_aarch64, struct vnode *root,
119                            enum objtype type, uint32_t entry,
120                            struct vnode **retvnode)
121{
122    assert(root->v.is_vnode);
123    errval_t err;
124
125    if (!retvnode) {
126        debug_printf("%s called without retvnode from %p, expect badness!\n", __FUNCTION__, __builtin_return_address(0));
127        // XXX: should probably return error.
128    }
129    assert(retvnode);
130
131    struct vnode *newvnode = slab_alloc(&pmap_aarch64->p.m.slab);
132    if (newvnode == NULL) {
133        return LIB_ERR_SLAB_ALLOC_FAIL;
134    }
135    newvnode->v.is_vnode = true;
136
137    // The VNode capability
138    err = pmap_aarch64->p.slot_alloc->alloc(pmap_aarch64->p.slot_alloc, &newvnode->v.cap);
139    if (err_is_fail(err)) {
140        return err_push(err, LIB_ERR_SLOT_ALLOC);
141    }
142
143    assert(!capref_is_null(newvnode->v.cap));
144
145    err = vnode_create(newvnode->v.cap, type);
146    if (err_is_fail(err)) {
147        return err_push(err, LIB_ERR_VNODE_CREATE);
148    }
149
150    assert(!capref_is_null(newvnode->v.cap));
151
152    // XXX: need to make sure that vnode cap that we will invoke is in our cspace!
153    if (get_croot_addr(newvnode->v.cap) != CPTR_ROOTCN) {
154        // debug_printf("%s: creating vnode for another domain in that domain's cspace; need to copy vnode cap to our cspace to make it invokable\n", __FUNCTION__);
155        assert(!capref_is_null(newvnode->v.cap));
156        err = slot_alloc(&newvnode->v.u.vnode.invokable);
157        assert(!capref_is_null(newvnode->v.cap));
158        assert(err_is_ok(err));
159        assert(!capref_is_null(newvnode->v.cap));
160        err = cap_copy(newvnode->v.u.vnode.invokable, newvnode->v.cap);
161        assert(err_is_ok(err));
162        assert(!capref_is_null(newvnode->v.u.vnode.invokable));
163        assert(!capref_is_null(newvnode->v.cap));
164
165        assert(!capref_is_null(newvnode->v.cap));
166    } else {
167        // debug_printf("vnode in our cspace: copying capref to invokable\n");
168        assert(!capref_is_null(newvnode->v.cap));
169        newvnode->v.u.vnode.invokable = newvnode->v.cap;
170        assert(!capref_is_null(newvnode->v.cap));
171    }
172    assert(!capref_is_null(newvnode->v.cap));
173    assert(!capref_is_null(newvnode->v.u.vnode.invokable));
174
175    // set mapping cap to correct slot in mapping cnodes.
176    set_mapping_cap(&pmap_aarch64->p, newvnode, root, entry);
177
178    // Map it
179    err = vnode_map(root->v.u.vnode.invokable, newvnode->v.cap, entry,
180                    KPI_PAGING_FLAGS_READ | KPI_PAGING_FLAGS_WRITE, 0, 1, newvnode->v.mapping);
181    if (err_is_fail(err)) {
182        return err_push(err, LIB_ERR_VNODE_MAP);
183    }
184
185    // The VNode meta data
186    newvnode->v.is_vnode  = true;
187    newvnode->v.entry     = entry;
188    pmap_vnode_init(&pmap_aarch64->p, newvnode);
189    pmap_vnode_insert_child(root, newvnode);
190
191#ifdef GLOBAL_MCN
192    /* allocate mapping cnodes */
193    for (int i = 0; i < MCN_COUNT; i++) {
194        err = cnode_create_l2(&newvnode->u.vnode.mcn[i], &newvnode->u.vnode.mcnode[i]);
195        if (err_is_fail(err)) {
196            return err_push(err, LIB_ERR_PMAP_ALLOC_CNODE);
197        }
198    }
199#endif
200
201    *retvnode = newvnode;
202    return SYS_ERR_OK;
203}
204
205/**
206 * \brief Returns the vnode for the pagetable mapping a given vspace address
207 */
208static errval_t get_ptable(struct pmap_aarch64  *pmap,
209                           genvaddr_t        vaddr,
210                           struct vnode    **ptable)
211{
212    errval_t err;
213    struct vnode *root = &pmap->root;
214    struct vnode *pl1, *pl2, *pl3;
215    assert(root != NULL);
216
217    // L0 mapping
218    if ((pl1 = pmap_find_vnode(root, VMSAv8_64_L0_BASE(vaddr))) == NULL) {
219        err = alloc_vnode(pmap, root, ObjType_VNode_AARCH64_l1,
220                            VMSAv8_64_L0_BASE(vaddr), &pl1);
221        if (err_is_fail(err)) {
222            return err_push(err, LIB_ERR_PMAP_ALLOC_VNODE);
223        }
224    }
225
226    // L1 mapping
227    if ((pl2 = pmap_find_vnode(pl1, VMSAv8_64_L1_BASE(vaddr))) == NULL) {
228        err = alloc_vnode(pmap, pl1, ObjType_VNode_AARCH64_l2,
229                            VMSAv8_64_L1_BASE(vaddr), &pl2);
230        if (err_is_fail(err)) {
231            return err_push(err, LIB_ERR_PMAP_ALLOC_VNODE);
232        }
233    }
234
235    // L2 mapping
236    if ((pl3 = pmap_find_vnode(pl2, VMSAv8_64_L2_BASE(vaddr))) == NULL) {
237        err = alloc_vnode(pmap, pl2, ObjType_VNode_AARCH64_l3,
238                            VMSAv8_64_L2_BASE(vaddr), &pl3);
239        if (err_is_fail(err)) {
240            return err_push(err, LIB_ERR_PMAP_ALLOC_VNODE);
241        }
242    }
243
244	assert(pl3 != NULL);
245	*ptable = pl3;
246    return SYS_ERR_OK;
247}
248
249static struct vnode *find_ptable(struct pmap_aarch64  *pmap,
250                                 genvaddr_t vaddr)
251{
252    struct vnode *root = &pmap->root;
253    struct vnode *pl1, *pl2;
254    assert(root != NULL);
255
256    // L0 mapping
257    if((pl1 = pmap_find_vnode(root, VMSAv8_64_L0_BASE(vaddr))) == NULL) {
258        return NULL;
259    }
260
261    // L1 mapping
262    if((pl2 = pmap_find_vnode(pl1, VMSAv8_64_L1_BASE(vaddr))) == NULL) {
263        return NULL;
264    }
265
266    // L2 mapping
267    return pmap_find_vnode(pl2, VMSAv8_64_L2_BASE(vaddr));
268}
269
270static errval_t do_single_map(struct pmap_aarch64 *pmap, genvaddr_t vaddr, genvaddr_t vend,
271                              struct capref frame, size_t offset, size_t pte_count,
272                              vregion_flags_t flags)
273{
274    // Get the page table
275    struct vnode *ptable= NULL;
276    errval_t err = get_ptable(pmap, vaddr, &ptable);
277    if (err_is_fail(err)) {
278        return err_push(err, LIB_ERR_PMAP_GET_PTABLE);
279    }
280
281    flags &= ~(VREGION_FLAGS_LARGE | VREGION_FLAGS_HUGE);
282    paging_aarch64_flags_t pmap_flags = vregion_flags_to_kpi_paging_flags(flags);
283
284    uintptr_t idx = VMSAv8_64_L3_BASE(vaddr);
285
286    // Create user level datastructure for the mapping
287    bool has_page = has_vnode(ptable, idx, pte_count);
288    assert(!has_page);
289
290    struct vnode *page = slab_alloc(&pmap->p.m.slab);
291    assert(page);
292
293    page->v.is_vnode = false;
294    page->v.entry = idx;
295    page->v.cap = frame;
296    page->v.u.frame.offset = offset;
297    page->v.u.frame.flags = flags;
298    page->v.u.frame.pte_count = pte_count;
299
300    // only insert child in vtree after new vnode fully initialized
301    pmap_vnode_insert_child(ptable, page);
302
303    set_mapping_cap(&pmap->p, page, ptable, idx);
304
305    // Map entry into the page table
306    assert(!capref_is_null(ptable->v.u.vnode.invokable));
307    err = vnode_map(ptable->v.u.vnode.invokable, frame, idx,
308                    pmap_flags, offset, pte_count, page->v.mapping);
309
310    if (err_is_fail(err)) {
311        return err_push(err, LIB_ERR_VNODE_MAP);
312    }
313
314    return SYS_ERR_OK;
315}
316
317errval_t do_map(struct pmap *pmap_gen, genvaddr_t vaddr,
318                struct capref frame, size_t offset, size_t size,
319                vregion_flags_t flags, size_t *retoff, size_t *retsize)
320{
321    errval_t err;
322
323    struct pmap_aarch64 *pmap = (struct pmap_aarch64 *)pmap_gen;
324
325    size = ROUND_UP(size, BASE_PAGE_SIZE);
326    size_t pte_count = DIVIDE_ROUND_UP(size, BASE_PAGE_SIZE);
327    genvaddr_t vend = vaddr + size;
328
329    if (VMSAv8_64_L012_BASE(vaddr) == VMSAv8_64_L012_BASE(vend - 1)) {
330        // fast path
331        err = do_single_map(pmap, vaddr, vend, frame, offset, pte_count, flags);
332        if (err_is_fail(err)) {
333            DEBUG_ERR(err, "[do_map] in fast path");
334            return err_push(err, LIB_ERR_PMAP_DO_MAP);
335        }
336    } else { // multiple leaf page tables
337        // first leaf
338        uint32_t c = VMSAv8_64_PTABLE_NUM_ENTRIES - VMSAv8_64_L3_BASE(vaddr);
339        genvaddr_t temp_end = vaddr + c * BASE_PAGE_SIZE;
340        err = do_single_map(pmap, vaddr, temp_end, frame, offset, c, flags);
341        if (err_is_fail(err)) {
342            return err_push(err, LIB_ERR_PMAP_DO_MAP);
343        }
344
345        // map full leaves
346        while (VMSAv8_64_L012_BASE(temp_end) < VMSAv8_64_L012_BASE(vend)) { // update vars
347            vaddr = temp_end;
348            temp_end = vaddr + VMSAv8_64_PTABLE_NUM_ENTRIES * BASE_PAGE_SIZE;
349            offset += c * BASE_PAGE_SIZE;
350            c = VMSAv8_64_PTABLE_NUM_ENTRIES;
351
352            // do mapping
353            err = do_single_map(pmap, vaddr, temp_end, frame, offset,
354                    VMSAv8_64_PTABLE_NUM_ENTRIES, flags);
355            if (err_is_fail(err)) {
356                return err_push(err, LIB_ERR_PMAP_DO_MAP);
357            }
358        }
359
360        // map remaining part
361        offset += c * BASE_PAGE_SIZE;
362        c = VMSAv8_64_L3_BASE(vend) - VMSAv8_64_L3_BASE(temp_end);
363        if (c) {
364            // do mapping
365            err = do_single_map(pmap, temp_end, vend, frame, offset, c, flags);
366            if (err_is_fail(err)) {
367                return err_push(err, LIB_ERR_PMAP_DO_MAP);
368            }
369        }
370    }
371    if (retoff) {
372        *retoff = offset;
373    }
374    if (retsize) {
375        *retsize = size;
376    }
377    //has_vnode_debug = false;
378    return SYS_ERR_OK;
379#if 0
380    errval_t err;
381    paging_aarch64_flags_t pmap_flags = vregion_flags_to_kpi_paging_flags(flags);
382
383    for (size_t i = offset; i < offset + size; i += BASE_PAGE_SIZE) {
384
385        vaddr += BASE_PAGE_SIZE;
386    }
387
388    if (retoff) {
389        *retoff = offset;
390    }
391    if (retsize) {
392        *retsize = size;
393    }
394    return SYS_ERR_OK;
395#endif
396}
397
398size_t
399max_slabs_required(size_t bytes)
400{
401    //XXX: use the definitions here
402    size_t max_pages  = DIVIDE_ROUND_UP(bytes, 4096);
403    size_t max_l3 = DIVIDE_ROUND_UP(max_pages, 512);
404    size_t max_l2   = DIVIDE_ROUND_UP(max_l3, 512);
405    size_t max_l1   = DIVIDE_ROUND_UP(max_l2, 512);
406    // Worst case, our mapping spans over two pdpts
407    return 2 * (max_l3 + max_l2 + max_l1);
408}
409/**
410 * \brief Create page mappings
411 *
412 * \param pmap     The pmap object
413 * \param vaddr    The virtual address to create the mapping for
414 * \param frame    The frame cap to map in
415 * \param offset   Offset into the frame cap
416 * \param size     Size of the mapping
417 * \param flags    Flags for the mapping
418 * \param retoff   If non-NULL, filled in with adjusted offset of mapped region
419 * \param retsize  If non-NULL, filled in with adjusted size of mapped region
420 */
421static errval_t
422map(struct pmap     *pmap,
423    genvaddr_t       vaddr,
424    struct capref    frame,
425    size_t           offset,
426    size_t           size,
427    vregion_flags_t  flags,
428    size_t          *retoff,
429    size_t          *retsize)
430{
431    errval_t err;
432
433    size   += BASE_PAGE_OFFSET(offset);
434    size    = ROUND_UP(size, BASE_PAGE_SIZE);
435    offset -= BASE_PAGE_OFFSET(offset);
436
437    const size_t slabs_reserve = 6; // == max_slabs_required(1)
438    size_t    slabs_required   = max_slabs_required(size) + slabs_reserve;
439
440    err = pmap_refill_slabs(pmap, slabs_required);
441    if (err_is_fail(err)) {
442        return err;
443    }
444
445    return do_map(pmap, vaddr, frame, offset, size, flags, retoff, retsize);
446}
447
448static errval_t do_single_unmap(struct pmap_aarch64 *pmap, genvaddr_t vaddr,
449                                size_t pte_count)
450{
451    errval_t err;
452    struct vnode *pt = find_ptable(pmap, vaddr);
453    if (pt) {
454        struct vnode *page = pmap_find_vnode(pt, VMSAv8_64_L3_BASE(vaddr));
455        if (page && page->v.u.frame.pte_count == pte_count) {
456            err = vnode_unmap(pt->v.cap, page->v.mapping);
457            if (err_is_fail(err)) {
458                DEBUG_ERR(err, "vnode_unmap");
459                return err_push(err, LIB_ERR_VNODE_UNMAP);
460            }
461
462            err = cap_delete(page->v.mapping);
463            if (err_is_fail(err)) {
464                return err_push(err, LIB_ERR_CAP_DELETE);
465            }
466#ifndef GLOBAL_MCN
467            err = pmap->p.slot_alloc->free(pmap->p.slot_alloc, page->v.mapping);
468            if (err_is_fail(err)) {
469                debug_printf("remove_empty_vnodes: slot_free (mapping): %s\n",
470                        err_getstring(err));
471            }
472#endif
473            pmap_remove_vnode(pt, page);
474            slab_free(&pmap->p.m.slab, page);
475        }
476        else {
477            return LIB_ERR_PMAP_FIND_VNODE;
478        }
479    }
480
481    return SYS_ERR_OK;
482}
483
484/**
485 * \brief Remove page mappings
486 *
487 * \param pmap     The pmap object
488 * \param vaddr    The start of the virtual addres to remove
489 * \param size     The size of virtual address to remove
490 * \param retsize  If non-NULL, filled in with the actual size removed
491 */
492static errval_t
493unmap(struct pmap *pmap,
494      genvaddr_t   vaddr,
495      size_t       size,
496      size_t      *retsize)
497{
498    errval_t err, ret = SYS_ERR_OK;
499    struct pmap_aarch64 *pmap_aarch64 = (struct pmap_aarch64*)pmap;
500    size = ROUND_UP(size, BASE_PAGE_SIZE);
501    size_t pte_count = size / BASE_PAGE_SIZE;
502    genvaddr_t vend = vaddr + size;
503
504    if (VMSAv8_64_L012_BASE(vaddr) == VMSAv8_64_L012_BASE(vend - 1)) {
505        // fast path
506        err = do_single_unmap(pmap_aarch64, vaddr, pte_count);
507        if (err_is_fail(err)) {
508            return err_push(err, LIB_ERR_PMAP_UNMAP);
509        }
510    } else { // slow path
511        // unmap first leaf
512        uint32_t c = VMSAv8_64_PTABLE_NUM_ENTRIES - VMSAv8_64_L3_BASE(vaddr);
513        err = do_single_unmap(pmap_aarch64, vaddr, c);
514        if (err_is_fail(err)) {
515            return err_push(err, LIB_ERR_PMAP_UNMAP);
516        }
517
518        // unmap full leaves
519        vaddr += c * BASE_PAGE_SIZE;
520        while (VMSAv8_64_L012_BASE(vaddr) < VMSAv8_64_L012_BASE(vend)) {
521            c = VMSAv8_64_PTABLE_NUM_ENTRIES;
522            err = do_single_unmap(pmap_aarch64, vaddr, c);
523            if (err_is_fail(err)) {
524                return err_push(err, LIB_ERR_PMAP_UNMAP);
525            }
526            vaddr += c * BASE_PAGE_SIZE;
527        }
528
529        // unmap remaining part
530        c = VMSAv8_64_L3_BASE(vend) - VMSAv8_64_L3_BASE(vaddr);
531        if (c) {
532            err = do_single_unmap(pmap_aarch64, vaddr, c);
533            if (err_is_fail(err)) {
534                return err_push(err, LIB_ERR_PMAP_UNMAP);
535            }
536        }
537    }
538
539    if (retsize) {
540        *retsize = size;
541    }
542
543    return ret;
544}
545
546/**
547 * \brief Determine a suitable address for a given memory object
548 *
549 * \param pmap    The pmap object
550 * \param memobj  The memory object to determine the address for
551 * \param alignment Minimum alignment
552 * \param vaddr   Pointer to return the determined address
553 *
554 * Relies on vspace.c code maintaining an ordered list of vregions
555 */
556static errval_t
557determine_addr(struct pmap   *pmap,
558               struct memobj *memobj,
559               size_t        alignment,
560               genvaddr_t    *retvaddr)
561{
562    assert(pmap->vspace->head);
563    struct pmap_aarch64* pmap_aarch64 = (struct pmap_aarch64*)pmap;
564    genvaddr_t vaddr;
565
566    if (alignment == 0) {
567        alignment = BASE_PAGE_SIZE;
568    } else {
569        alignment = ROUND_UP(alignment, BASE_PAGE_SIZE);
570    }
571    size_t size = ROUND_UP(memobj->size, alignment);
572
573    struct vregion *walk = pmap->vspace->head;
574    // if there's space before the first object, map there
575    genvaddr_t minva = ROUND_UP(pmap_aarch64->min_mappable_va, alignment);
576
577    while (walk->next) { // Try to insert between existing mappings
578        genvaddr_t walk_base = vregion_get_base_addr(walk);
579        genvaddr_t walk_size = ROUND_UP(vregion_get_size(walk), BASE_PAGE_SIZE);
580        genvaddr_t walk_end  = ROUND_UP(walk_base + walk_size, alignment);
581        genvaddr_t next_base = vregion_get_base_addr(walk->next);
582
583        // sanity-check for page alignment
584        assert(walk_base % BASE_PAGE_SIZE == 0);
585        assert(next_base % BASE_PAGE_SIZE == 0);
586
587        if (next_base > walk_end + size && walk_end > minva) {
588            vaddr = walk_end;
589            goto out;
590        }
591
592        walk = walk->next;
593    }
594
595    // place beyond last mapping with alignment
596    vaddr = ROUND_UP((vregion_get_base_addr(walk)
597                + ROUND_UP(vregion_get_size(walk), BASE_PAGE_SIZE)),
598                alignment);
599
600
601
602out:
603    // ensure that we haven't run out of the valid part of the address space
604    if (vaddr + memobj->size > pmap_aarch64->max_mappable_va) {
605        return LIB_ERR_OUT_OF_VIRTUAL_ADDR;
606    }
607    assert(retvaddr != NULL);
608    *retvaddr = vaddr;
609
610    return SYS_ERR_OK;
611}
612
613int pmap_selective_flush = 0;
614static errval_t do_single_modify_flags(struct pmap_aarch64 *pmap, genvaddr_t vaddr,
615                                       size_t pages, vregion_flags_t flags)
616{
617    errval_t err = SYS_ERR_OK;
618    struct vnode *ptable = find_ptable(pmap, vaddr);
619    uint16_t ptentry = VMSAv8_64_L3_BASE(vaddr);
620    if (ptable) {
621        struct vnode *page = pmap_find_vnode(ptable, ptentry);
622        if (page) {
623            if (pmap_inside_region(ptable, ptentry, pages)) {
624                // we're modifying part of a valid mapped region
625                // arguments to invocation: invoke frame cap, first affected
626                // page (as offset from first page in mapping), #affected
627                // pages, new flags. Invocation should check compatibility of
628                // new set of flags with cap permissions.
629                size_t off = ptentry - page->v.entry;
630                flags &= ~(VREGION_FLAGS_LARGE | VREGION_FLAGS_HUGE);
631                // debug_printf("Vregion flags: %zx\n", flags);
632                paging_aarch64_flags_t pmap_flags = vregion_flags_to_kpi_paging_flags(flags);
633                // debug_printf("KPI flags: %zx\n", pmap_flags);
634                // VA hinting NYI on ARMv8, always passing 0
635                err = invoke_mapping_modify_flags(page->v.mapping, off, pages, pmap_flags, 0);
636                return err;
637            } else {
638                // overlaps some region border
639                return LIB_ERR_PMAP_EXISTING_MAPPING;
640            }
641        }
642    }
643    return SYS_ERR_OK;
644}
645
646/**
647 * \brief Modify page mapping
648 *
649 * \param pmap     The pmap object
650 * \param vaddr    The virtual address to unmap
651 * \param flags    New flags for the mapping
652 * \param retsize  If non-NULL, filled in with the actual size modified
653 */
654static errval_t
655modify_flags(struct pmap     *pmap,
656             genvaddr_t       vaddr,
657             size_t           size,
658             vregion_flags_t  flags,
659             size_t          *retsize)
660{
661    errval_t err, ret = SYS_ERR_OK;
662    struct pmap_aarch64 *pmap_aarch64 = (struct pmap_aarch64*)pmap;
663    size = ROUND_UP(size, BASE_PAGE_SIZE);
664    size_t pte_count = size / BASE_PAGE_SIZE;
665    genvaddr_t vend = vaddr + size;
666
667    if (VMSAv8_64_L012_BASE(vaddr) == VMSAv8_64_L012_BASE(vend - 1)) {
668        // fast path
669        err = do_single_modify_flags(pmap_aarch64, vaddr, pte_count, flags);
670        if (err_is_fail(err)) {
671            return err_push(err, LIB_ERR_PMAP_UNMAP);
672        }
673    } else { // slow path
674        // unmap first leaf
675        uint32_t c = VMSAv8_64_PTABLE_NUM_ENTRIES - VMSAv8_64_L3_BASE(vaddr);
676        err = do_single_modify_flags(pmap_aarch64, vaddr, c, flags);
677        if (err_is_fail(err)) {
678            return err_push(err, LIB_ERR_PMAP_UNMAP);
679        }
680
681        // unmap full leaves
682        vaddr += c * BASE_PAGE_SIZE;
683        while (VMSAv8_64_L012_BASE(vaddr) < VMSAv8_64_L012_BASE(vend)) {
684            c = VMSAv8_64_PTABLE_NUM_ENTRIES;
685            err = do_single_modify_flags(pmap_aarch64, vaddr, c, flags);
686            if (err_is_fail(err)) {
687                return err_push(err, LIB_ERR_PMAP_UNMAP);
688            }
689            vaddr += c * BASE_PAGE_SIZE;
690        }
691
692        // unmap remaining part
693        c = VMSAv8_64_L3_BASE(vend) - VMSAv8_64_L3_BASE(vaddr);
694        if (c) {
695            err = do_single_modify_flags(pmap_aarch64, vaddr, c, flags);
696            if (err_is_fail(err)) {
697                return err_push(err, LIB_ERR_PMAP_UNMAP);
698            }
699        }
700    }
701
702    if (retsize) {
703        *retsize = size;
704    }
705
706    return ret;
707}
708
709/**
710 * \brief Query existing page mapping
711 *
712 * \param pmap     The pmap object
713 * \param vaddr    The virtual address to query
714 * \param retvaddr Returns the base virtual address of the mapping
715 * \param retsize  Returns the actual size of the mapping
716 * \param retcap   Returns the cap mapped at this address
717 * \param retoffset Returns the offset within the cap that is mapped
718 * \param retflags Returns the flags for this mapping
719 *
720 * All of the ret parameters are optional.
721 */
722static errval_t lookup(struct pmap *pmap, genvaddr_t vaddr,
723                       struct pmap_mapping_info *info)
724{
725    USER_PANIC("NYI");
726    return 0;
727}
728
729static struct pmap_funcs pmap_funcs = {
730    .determine_addr = determine_addr,
731    .map = map,
732    .unmap = unmap,
733    .modify_flags = modify_flags,
734    .lookup = lookup,
735    .serialise = pmap_serialise,
736    .deserialise = pmap_deserialise,
737};
738
739/**
740 * \brief Initialize the pmap object
741 */
742errval_t
743pmap_init(struct pmap   *pmap,
744          struct vspace *vspace,
745          struct capref  vnode,
746          struct slot_allocator *opt_slot_alloc)
747{
748    struct pmap_aarch64* pmap_aarch64 = (struct pmap_aarch64*)pmap;
749
750    /* Generic portion */
751    pmap->f = pmap_funcs;
752    pmap->vspace = vspace;
753
754    if (opt_slot_alloc != NULL) {
755        pmap->slot_alloc = opt_slot_alloc;
756    } else { /* use default allocator for this dispatcher */
757        pmap->slot_alloc = get_default_slot_allocator();
758    }
759
760    pmap_vnode_mgmt_init(pmap);
761
762    pmap_aarch64->root.v.is_vnode         = true;
763    pmap_aarch64->root.v.cap              = vnode;
764    pmap_aarch64->root.v.u.vnode.invokable = vnode;
765
766    if (get_croot_addr(vnode) != CPTR_ROOTCN) {
767        errval_t err = slot_alloc(&pmap_aarch64->root.v.u.vnode.invokable);
768        assert(err_is_ok(err));
769        err = cap_copy(pmap_aarch64->root.v.u.vnode.invokable, vnode);
770        assert(err_is_ok(err));
771    }
772    assert(!capref_is_null(pmap_aarch64->root.v.cap));
773    assert(!capref_is_null(pmap_aarch64->root.v.u.vnode.invokable));
774    pmap_vnode_init(pmap, &pmap_aarch64->root);
775
776#ifdef GLOBAL_MCN
777    /*
778     * Initialize root vnode mapping cnode
779     */
780    if (pmap == get_current_pmap()) {
781        /*
782         * for now, for our own pmap, we use the left over slot allocator cnode to
783         * provide the mapping cnode for the first half of the root page table as
784         * we cannot allocate CNodes before establishing a connection to the
785         * memory server!
786         */
787        pmap_aarch64->root.u.vnode.mcn[0].cnode = cnode_root;
788        pmap_aarch64->root.u.vnode.mcn[0].slot = ROOTCN_SLOT_ROOT_MAPPING;
789        pmap_aarch64->root.u.vnode.mcnode[0].croot = CPTR_ROOTCN;
790        pmap_aarch64->root.u.vnode.mcnode[0].cnode = ROOTCN_SLOT_ADDR(ROOTCN_SLOT_ROOT_MAPPING);
791        pmap_aarch64->root.u.vnode.mcnode[0].level = CNODE_TYPE_OTHER;
792    } else {
793        errval_t err;
794        err = cnode_create_l2(&pmap_aarch64->root.u.vnode.mcn[0], &pmap_aarch64->root.u.vnode.mcnode[0]);
795        if (err_is_fail(err)) {
796            return err_push(err, LIB_ERR_PMAP_ALLOC_CNODE);
797        }
798    }
799#endif
800
801    // choose a minimum mappable VA for most domains; enough to catch NULL
802    // pointer derefs with suitably large offsets
803    pmap_aarch64->min_mappable_va = 64 * 1024;
804
805    // maximum mappable VA is derived from X86_64_MEMORY_OFFSET in kernel
806    pmap_aarch64->max_mappable_va = (genvaddr_t)0xffffff8000000000;
807
808    return SYS_ERR_OK;
809}
810
811errval_t pmap_current_init(bool init_domain)
812{
813    struct pmap_aarch64 *pmap_aarch64 = (struct pmap_aarch64*)get_current_pmap();
814
815    pmap_vnode_mgmt_current_init((struct pmap *)pmap_aarch64);
816
817    return SYS_ERR_OK;
818}
819
820struct vnode_public *pmap_get_vroot(struct pmap *pmap)
821{
822    struct pmap_aarch64 *pa64 = (struct pmap_aarch64 *)pmap;
823    return &pa64->root.v;
824}
825
826void pmap_set_min_mappable_va(struct pmap *pmap, lvaddr_t minva)
827{
828    struct pmap_aarch64 *pa64 = (struct pmap_aarch64 *)pmap;
829    pa64->min_mappable_va = minva;
830}
831