1/**
2 * \file
3 * \brief Pmap code common for x86 archs
4 */
5
6/*
7 * Copyright (c) 2011, ETH Zurich.
8 * Copyright (c) 2014, HP Labs.
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#include <barrelfish/barrelfish.h>
17#include <barrelfish/pmap.h>
18#include "target/x86/pmap_x86.h"
19#include <pmap_priv.h> // for set_mapping_cap
20#include <pmap_ds.h> // for selected pmap datastructure
21
22// For tracing
23#include <trace/trace.h>
24#include <trace_definitions/trace_defs.h>
25
26/**
27 * has_vnode() checks whether there exists any vnodes in `len` slots
28 * starting from `entry` in vnode `root`, if `only_pages` is set, page table
29 * vnodes are ignored
30 */
31bool has_vnode(struct vnode *root, uint32_t entry, size_t len,
32               bool only_pages)
33{
34    assert(root != NULL);
35    assert(root->v.is_vnode);
36    struct vnode *n;
37
38    uint32_t end_entry = entry + len;
39
40    // region we check [entry .. end_entry)
41    pmap_foreach_child(root, n) {
42        assert(n);
43        // n is page table, we need to check if it's anywhere inside the
44        // region to check [entry .. end_entry)
45        // this amounts to n->entry == entry for len = 1
46        if (n->v.is_vnode && n->v.entry >= entry && n->v.entry < end_entry) {
47            if (only_pages) {
48                return has_vnode(n, 0, PTABLE_ENTRIES, true);
49            }
50#ifdef LIBBARRELFISH_DEBUG_PMAP
51            debug_printf("1: found page table inside our region\n");
52#endif
53            return true;
54        } else if (n->v.is_vnode) {
55            // all other vnodes do not overlap with us, so go to next
56            assert(n->v.entry < entry || n->v.entry >= end_entry);
57            continue;
58        }
59        // this remains the same regardless of `only_pages`.
60        // n is frame [n->v.entry .. end)
61        // 3 cases:
62        // 1) entry < n->v.entry && end_entry >= end --> n is a strict subset of
63        // our region
64        // 2) entry inside n (entry >= n->v.entry && entry < end)
65        // 3) end_entry inside n (end_entry >= n->v.entry && end_entry < end)
66        uint32_t end = n->v.entry + n->v.u.frame.pte_count;
67        if (entry < n->v.entry && end_entry >= end) {
68#ifdef LIBBARRELFISH_DEBUG_PMAP
69            debug_printf("2: found a strict subset of our region: (%"
70                    PRIu32"--%"PRIu32") < (%"PRIu32"--%"PRIu32")\n",
71                    n->v.entry, end, entry, end_entry);
72#endif
73            return true;
74        }
75        if (entry >= n->v.entry && entry < end) {
76#ifdef LIBBARRELFISH_DEBUG_PMAP
77            debug_printf("3: found a region starting inside our region: (%"
78                    PRIu32"--%"PRIu32") <> (%"PRIu32"--%"PRIu32")\n",
79                    n->v.entry, end, entry, end_entry);
80#endif
81            return true;
82        }
83        if (end_entry > n->v.entry && end_entry < end) {
84#ifdef LIBBARRELFISH_DEBUG_PMAP
85            debug_printf("4: found a region ending inside our region: (%"
86                    PRIu32"--%"PRIu32") <> (%"PRIu32"--%"PRIu32")\n",
87                    n->v.entry, end, entry, end_entry);
88#endif
89            return true;
90        }
91    }
92
93    return false;
94}
95
96
97/**
98 * \brief Allocates a new VNode, adding it to the page table and our metadata
99 */
100errval_t alloc_vnode(struct pmap_x86 *pmap, struct vnode *root,
101                     enum objtype type, uint32_t entry,
102                     struct vnode **retvnode, genvaddr_t base)
103{
104    errval_t err;
105
106    struct vnode *newvnode = slab_alloc(&pmap->p.m.slab);
107    if (newvnode == NULL) {
108        return LIB_ERR_SLAB_ALLOC_FAIL;
109    }
110
111    // The VNode capability
112    err = pmap->p.slot_alloc->alloc(pmap->p.slot_alloc, &newvnode->v.cap);
113    if (err_is_fail(err)) {
114        return err_push(err, LIB_ERR_SLOT_ALLOC);
115    }
116    pmap->used_cap_slots ++;
117
118    err = vnode_create(newvnode->v.cap, type);
119    if (err_is_fail(err)) {
120        return err_push(err, LIB_ERR_VNODE_CREATE);
121    }
122
123    // XXX: need to make sure that vnode cap that we will invoke is in our cspace!
124    if (get_croot_addr(newvnode->v.cap) != CPTR_ROOTCN) {
125        // 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__);
126        err = slot_alloc(&newvnode->v.u.vnode.invokable);
127        assert(err_is_ok(err));
128        pmap->used_cap_slots ++;
129        err = cap_copy(newvnode->v.u.vnode.invokable, newvnode->v.cap);
130        assert(err_is_ok(err));
131    } else {
132        // debug_printf("vnode in our cspace: copying capref to invokable\n");
133        newvnode->v.u.vnode.invokable = newvnode->v.cap;
134    }
135    assert(!capref_is_null(newvnode->v.cap));
136    assert(!capref_is_null(newvnode->v.u.vnode.invokable));
137
138    set_mapping_cap(&pmap->p, newvnode, root, entry);
139    pmap->used_cap_slots ++;
140    assert(!capref_is_null(newvnode->v.mapping));
141
142    // Map it
143    err = vnode_map(root->v.u.vnode.invokable, newvnode->v.cap, entry,
144                    PTABLE_ACCESS_DEFAULT, 0, 1, newvnode->v.mapping);
145    if (err_is_fail(err)) {
146        DEBUG_ERR(err, "pmap_x86: vnode_map\n");
147        return err_push(err, LIB_ERR_VNODE_MAP);
148    }
149
150    // The VNode meta data
151    newvnode->v.is_vnode  = true;
152    newvnode->is_cloned = false;
153    newvnode->is_pinned = false;
154    newvnode->v.entry     = entry;
155    newvnode->v.type      = type;
156    pmap_vnode_init(&pmap->p, newvnode);
157    pmap_vnode_insert_child(root, newvnode);
158    newvnode->u.vnode.virt_base = 0;
159    newvnode->u.vnode.page_table_frame  = NULL_CAP;
160    newvnode->u.vnode.base = base;
161
162#if GLOBAL_MCN
163    /* allocate mapping cnodes */
164    for (int i = 0; i < MCN_COUNT; i++) {
165        err = cnode_create_l2(&newvnode->u.vnode.mcn[i], &newvnode->u.vnode.mcnode[i]);
166        if (err_is_fail(err)) {
167            return err_push(err, LIB_ERR_PMAP_ALLOC_CNODE);
168        }
169    }
170#endif
171
172    *retvnode = newvnode;
173    return SYS_ERR_OK;
174}
175
176void remove_empty_vnodes(struct pmap_x86 *pmap, struct vnode *root,
177                         uint32_t entry, size_t len)
178{
179    errval_t err;
180    uint32_t end_entry = entry + len;
181    struct vnode *n;
182    pmap_foreach_child(root, n) {
183        assert(n);
184        if (n->v.entry >= entry && n->v.entry < end_entry) {
185            // sanity check and skip leaf entries
186            if (!n->v.is_vnode || n->is_pinned) {
187                continue;
188            }
189            // here we know that all vnodes we're interested in are
190            // page tables
191            assert(n->v.is_vnode);
192            if (n->v.u.vnode.children) {
193                remove_empty_vnodes(pmap, n, 0, PTABLE_ENTRIES);
194            }
195
196            // unmap
197            err = vnode_unmap(root->v.cap, n->v.mapping);
198            if (err_is_fail(err)) {
199                debug_printf("remove_empty_vnodes: vnode_unmap: %s\n",
200                        err_getstring(err));
201            }
202
203            // delete mapping cap first: underlying cap needs to exist for
204            // this to work properly!
205            err = cap_delete(n->v.mapping);
206            if (err_is_fail(err)) {
207                debug_printf("remove_empty_vnodes: cap_delete (mapping): %s\n",
208                        err_getstring(err));
209            }
210#ifndef GLOBAL_MCN
211            err = pmap->p.slot_alloc->free(pmap->p.slot_alloc, n->v.mapping);
212            if (err_is_fail(err)) {
213                debug_printf("remove_empty_vnodes: slot_free (mapping): %s\n",
214                        err_getstring(err));
215            }
216#endif
217            assert(pmap->used_cap_slots > 0);
218            pmap->used_cap_slots --;
219            // delete capability
220            err = cap_delete(n->v.cap);
221            if (err_is_fail(err)) {
222                debug_printf("remove_empty_vnodes: cap_delete (vnode): %s\n",
223                        err_getstring(err));
224            }
225            if (!capcmp(n->v.cap, n->v.u.vnode.invokable)) {
226                // invokable is always allocated in our cspace
227                err = cap_destroy(n->v.u.vnode.invokable);
228                if (err_is_fail(err)) {
229                    debug_printf("remove_empty_vnodes: cap_delete (vnode.invokable): %s\n",
230                        err_getstring(err));
231
232                }
233            }
234            err = pmap->p.slot_alloc->free(pmap->p.slot_alloc, n->v.cap);
235            if (err_is_fail(err)) {
236                debug_printf("remove_empty_vnodes: slot_free (vnode): %s\n",
237                        err_getstring(err));
238            }
239            assert(pmap->used_cap_slots > 0);
240            pmap->used_cap_slots --;
241
242            // remove vnode from list
243            pmap_remove_vnode(root, n);
244
245#if GLOBAL_MCN
246            /* delete mapping cap cnodes */
247            for (int x = 0; x < MCN_COUNT; x++) {
248                err = cap_destroy(n->u.vnode.mcn[x]);
249                if (err_is_fail(err)) {
250                    debug_printf("%s: cap_destroy(mapping cn %d): %s\n",
251                            __FUNCTION__, x, err_getcode(err));
252                }
253            }
254#endif
255            pmap_vnode_free(&pmap->p, n);
256        }
257    }
258}
259
260
261
262/**
263 * \brief Determine a suitable address for a given memory object
264 *
265 * \param pmap    The pmap object
266 * \param memobj  The memory object to determine the address for
267 * \param alignment Minimum alignment
268 * \param retvaddr Pointer to return the determined address
269 *
270 * Relies on vspace.c code maintaining an ordered list of vregions
271 */
272errval_t pmap_x86_determine_addr(struct pmap *pmap, struct memobj *memobj,
273                                 size_t alignment, genvaddr_t *retvaddr)
274{
275    trace_event(TRACE_SUBSYS_MEMORY, TRACE_EVENT_MEMORY_DETADDR, 0);
276    struct pmap_x86 *pmapx = (struct pmap_x86 *)pmap;
277    genvaddr_t vaddr;
278
279    struct vregion *walk = pmap->vspace->head;
280    assert(walk != NULL); // assume there's always at least one existing entry
281
282    if (alignment == 0) {
283        alignment = BASE_PAGE_SIZE;
284    } else {
285        alignment = ROUND_UP(alignment, BASE_PAGE_SIZE);
286    }
287    size_t size = ROUND_UP(memobj->size, alignment);
288
289    // if there's space before the first object, map it there
290    genvaddr_t minva = ROUND_UP(pmapx->min_mappable_va, alignment);
291    if (minva + size <= vregion_get_base_addr(walk)) {
292        vaddr = minva;
293        goto out;
294    }
295
296    while (walk->next) { // Try to insert between existing mappings
297        genvaddr_t walk_base = vregion_get_base_addr(walk);
298        genvaddr_t walk_size = ROUND_UP(vregion_get_size(walk), BASE_PAGE_SIZE);
299        genvaddr_t walk_end = ROUND_UP(walk_base + walk_size, alignment);
300        genvaddr_t next_base = vregion_get_base_addr(walk->next);
301
302        // sanity-check for page alignment
303        assert(walk_base % BASE_PAGE_SIZE == 0);
304        assert(next_base % BASE_PAGE_SIZE == 0);
305
306        if (next_base > walk_end + size) {
307            vaddr = walk_end;
308            goto out;
309        }
310
311        walk = walk->next;
312    }
313
314    // Place beyond last mapping, with alignment
315    vaddr = ROUND_UP((vregion_get_base_addr(walk)
316                      + ROUND_UP(vregion_get_size(walk), BASE_PAGE_SIZE)),
317                     alignment);
318
319 out:
320    // Ensure that we haven't run out of address space
321    if (vaddr + memobj->size > pmapx->max_mappable_va) {
322        trace_event(TRACE_SUBSYS_MEMORY, TRACE_EVENT_MEMORY_DETADDR, 1);
323        return LIB_ERR_OUT_OF_VIRTUAL_ADDR;
324    }
325
326    assert(retvaddr != NULL);
327    *retvaddr = vaddr;
328
329    trace_event(TRACE_SUBSYS_MEMORY, TRACE_EVENT_MEMORY_DETADDR, 1);
330    return SYS_ERR_OK;
331}
332
333errval_t pmap_x86_measure_res(struct pmap *pmap, struct pmap_res_info *buf)
334{
335    assert(buf);
336    struct pmap_x86 *x86 = (struct pmap_x86 *)pmap;
337
338    size_t free_slabs = 0;
339    size_t used_slabs = 0;
340    // Count allocated and free slabs in bytes; this accounts for all vnodes
341    for (struct slab_head *sh = pmap->m.slab.slabs; sh != NULL; sh = sh->next) {
342        free_slabs += sh->free;
343        used_slabs += sh->total - sh->free;
344    }
345    buf->vnode_used = used_slabs * pmap->m.slab.blocksize;
346    buf->vnode_free = free_slabs * pmap->m.slab.blocksize;
347
348    // Report capability slots in use by pmap
349    buf->slots_used = x86->used_cap_slots;
350
351    return SYS_ERR_OK;
352}
353
354struct vnode_public *pmap_get_vroot(struct pmap *pmap) {
355    struct pmap_x86 *pmapx = (struct pmap_x86 *)pmap;
356    return &pmapx->root.v;
357}
358
359void pmap_set_min_mappable_va(struct pmap *pmap, lvaddr_t minva)
360{
361    struct pmap_x86 *pmapx = (struct pmap_x86 *)pmap;
362    pmapx->min_mappable_va = minva;
363}
364