1/**
2 * \file
3 * \brief memory object of anonymous type.
4 * The object maintains a list of frames.
5 *
6 * The object maintains a list of frames and a list of vregions.
7 * The lists are backed by slabs.
8 * The slabs may have to be grown,
9 * in which case the object will use #vspace_pinned_alloc.
10 *
11 * morecore uses this memory object so it cannot use malloc for its lists.
12 * Therefore, this uses slabs and grows them using the pinned memory.
13 */
14
15/*
16 * Copyright (c) 2009, 2010, 2011, ETH Zurich.
17 * Copyright (c) 2014, HP Labs.
18 * All rights reserved.
19 *
20 * This file is distributed under the terms in the attached LICENSE file.
21 * If you do not find this file, copies can be found by writing to:
22 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
23 */
24
25#include <barrelfish/barrelfish.h>
26#include "vspace_internal.h"
27
28/**
29 * \brief Map the memory object into a region
30 *
31 * \param memobj   The memory object
32 * \param region  The region to add
33 */
34static errval_t map_region(struct memobj *memobj, struct vregion *vregion)
35{
36    errval_t err;
37    struct memobj_anon *anon = (struct memobj_anon*)memobj;
38
39    // Allocate space
40    struct vregion_list *data = slab_alloc(&anon->vregion_slab);
41    if (slab_freecount(&anon->vregion_slab) <= 1 && !anon->vregion_slab_refilling) { // Grow
42        anon->vregion_slab_refilling = true;
43        void *buf;
44        err = vspace_pinned_alloc(&buf, VREGION_LIST);
45        if (err_is_fail(err)) {
46            return err_push(err, LIB_ERR_VSPACE_PINNED_ALLOC);
47        }
48        slab_grow(&anon->vregion_slab, buf,
49                  VSPACE_PINNED_UNIT * sizeof(struct vregion_list));
50        if (data == NULL) {
51            data = slab_alloc(&anon->vregion_slab);
52        }
53        anon->vregion_slab_refilling = false;
54    }
55    if (!data) {
56        return LIB_ERR_SLAB_ALLOC_FAIL;
57    }
58    data->region = vregion;
59
60    // Insert into the list
61    struct vregion_list *walk = anon->vregion_list;
62    anon->vregion_list = data;
63    data->next = walk;
64
65    return SYS_ERR_OK;
66}
67
68/**
69 * \brief Unmap the memory object from a region
70 *
71 * \param memobj   The memory object
72 * \param region  The region to remove
73 */
74static errval_t unmap_region(struct memobj *memobj, struct vregion *vregion)
75{
76    struct memobj_anon *anon = (struct memobj_anon*)memobj;
77    errval_t err;
78
79    /* Unmap the affected area in the pmap */
80    struct vspace *vspace = vregion_get_vspace(vregion);
81    struct pmap *pmap     = vspace_get_pmap(vspace);
82    genvaddr_t vregion_base  = vregion_get_base_addr(vregion);
83    genvaddr_t vregion_off   = vregion_get_offset(vregion);
84    size_t vregion_size = vregion_get_size(vregion);
85    genvaddr_t vregion_end = vregion_off + vregion_size;
86
87    //printf("(%s:%d) unmap(0x%"PRIxGENVADDR", memobj->size = %zd) vregion size = %zd\n", __FILE__, __LINE__, vregion_base + vregion_off, memobj->size, vregion_size);
88
89    // unmap all affected frames
90    struct memobj_frame_list *fwalk = anon->frame_list;
91    struct memobj_frame_list *fprev = NULL;
92    //printf("vregion_off = 0x%"PRIxGENVADDR"\n", vregion_off);
93    //printf("vregion_end = 0x%"PRIxGENVADDR"\n", vregion_end);
94    err = LIB_ERR_VSPACE_VREGION_NOT_FOUND;
95    while (fwalk) {
96        //printf("fwalk->offset = %zd\n", fwalk->offset);
97        //printf("fwalk->next   = %p\n", fwalk->next);
98        if (fwalk->offset < vregion_off) {
99            fprev = fwalk;
100            fwalk = fwalk->next;
101            continue;
102        }
103        else if (fwalk->offset < vregion_end) {
104            err = pmap->f.unmap(pmap, vregion_base + vregion_off, fwalk->size, NULL);
105            if (err_is_fail(err)) {
106                return err_push(err, LIB_ERR_PMAP_UNMAP);
107            }
108
109            /* Remove the vregion from the list */
110            struct vregion_list *prev = NULL;
111            for (struct vregion_list *elt = anon->vregion_list; elt != NULL;
112                 elt = elt->next) {
113                if (elt->region == vregion) {
114                    if (prev == NULL) {
115                        assert(elt == anon->vregion_list);
116                        anon->vregion_list = elt->next;
117                    } else {
118                        assert(prev->next == elt);
119                        prev->next = elt->next;
120                    }
121                    slab_free(&anon->vregion_slab, elt);
122                    err = SYS_ERR_OK;
123                }
124            }
125            vregion_off += fwalk->size;
126            fprev = fwalk;
127            fwalk = fwalk->next;
128        }
129    }
130
131    return err; // XXX: not quite the right error
132}
133
134/**
135 * \brief Set the protection on a range
136 *
137 * \param memobj  The memory object
138 * \param region  The vregion to modify the mappings on
139 * \param offset  Offset into the memory object
140 * \param range   The range of space to set the protection for
141 * \param flags   The protection flags
142 */
143static errval_t protect(struct memobj *memobj, struct vregion *vregion,
144                        genvaddr_t offset, size_t range, vs_prot_flags_t flags)
145{
146    struct memobj_anon *anon = (struct memobj_anon*)memobj;
147    errval_t err;
148
149    /* protect the affected area in the pmap */
150    struct vspace *vspace = vregion_get_vspace(vregion);
151    struct pmap *pmap     = vspace_get_pmap(vspace);
152    genvaddr_t vregion_base  = vregion_get_base_addr(vregion);
153    genvaddr_t vregion_off   = vregion_get_offset(vregion);
154    size_t vregion_size = vregion_get_size(vregion);
155    genvaddr_t vregion_end = vregion_off + vregion_size;
156
157    //printf("(%s:%d) protect(0x%"PRIxGENVADDR", memobj->size = %zd) vregion size = %zd offset=%zd range=%zd\n", __FILE__, __LINE__, vregion_base + vregion_off, memobj->size, vregion_size, offset, range);
158
159    if (offset + range > vregion_end) {
160        return LIB_ERR_MEMOBJ_WRONG_OFFSET;
161    }
162
163    offset += vregion_off;
164
165    // Special handling if the range cannot span frames
166    if (range <= BASE_PAGE_SIZE) {
167        err = pmap->f.modify_flags(pmap, vregion_base + offset, range, flags, NULL);
168        if (err_is_fail(err)) {
169            return err_push(err, LIB_ERR_PMAP_MODIFY_FLAGS);
170        }
171        return SYS_ERR_OK;
172    }
173
174    // protect all affected frames
175    struct memobj_frame_list *fwalk = anon->frame_list;
176    //printf("vregion_off = 0x%"PRIxGENVADDR"\n", vregion_off);
177    //printf("vregion_end = 0x%"PRIxGENVADDR"\n", vregion_end);
178    while (fwalk && range) {
179        //printf("fwalk->offset = %zd\n", fwalk->offset);
180        //printf("fwalk->next   = %p\n", fwalk->next);
181        if (offset >= fwalk->offset && offset < fwalk->offset + fwalk->size) {
182
183            size_t range_in_frame = fwalk->offset + fwalk->size - offset;
184            size_t size = range_in_frame < range ? range_in_frame : range;
185
186            size_t retsize;
187            err = pmap->f.modify_flags(pmap, vregion_base + offset, size, flags, &retsize);
188            if (err_is_fail(err)) {
189                return err_push(err, LIB_ERR_PMAP_MODIFY_FLAGS);
190            }
191            range -= retsize;
192            offset += retsize;
193        }
194        fwalk = fwalk->next;
195    }
196    if (range > 0) {
197        return LIB_ERR_VSPACE_VREGION_NOT_FOUND;
198    }
199
200    return SYS_ERR_OK;
201}
202
203/**
204 * \brief Pin a range
205 *
206 * \param memobj  The memory object
207 * \param region  The vregion to modify the state on
208 * \param offset  Offset into the memory object
209 * \param range   The range of space to pin
210 */
211static errval_t pin(struct memobj *memobj, struct vregion *vregion,
212                    genvaddr_t offset, size_t range)
213{
214    USER_PANIC("NYI");
215}
216
217/**
218 * \brief Unpin a range
219 *
220 * \param memobj  The memory object
221 * \param region  The vregion to modify the state on
222 * \param offset  Offset into the memory object
223 * \param range   The range of space to unpin
224 */
225static errval_t unpin(struct memobj *memobj, struct vregion *vregion,
226                      genvaddr_t offset, size_t range)
227{
228    USER_PANIC("NYI");
229}
230
231/**
232 * \brief Set a frame for an offset into the memobj
233 *
234 * \param memobj  The memory object
235 * \param offset  Offset into the memory object
236 * \param frame   The frame cap for the offset
237 * \param size    The size of frame cap
238 *
239 * Pagefault relies on frames inserted in order
240 */
241static errval_t fill_foff(struct memobj *memobj, genvaddr_t offset, struct capref frame,
242                     size_t size, genpaddr_t foffset)
243{
244    errval_t err;
245    struct memobj_anon *anon = (struct memobj_anon*)memobj;
246
247    assert((offset & BASE_PAGE_MASK) == 0);
248
249    // AB: allow frame to overlap end of memobj; that might have been the most
250    // efficient allocation size (even if the end of the frame will be unusable)
251    if (offset >= memobj->size) {
252        return LIB_ERR_MEMOBJ_WRONG_OFFSET;
253    }
254
255    // Allocate
256    struct memobj_frame_list *new = slab_alloc(&anon->frame_slab);
257    // We have to grow our slab allocator when there's still one slab left as
258    // we otherwise might run out of slabs when calling memobj->fill() from
259    // vspace_pinned_alloc(). The is_refilling flag allows us to hand out the
260    // last slab when coming back here from vspace_pinned_alloc().
261    // -SG, 2016-12-15.
262    if (slab_freecount(&anon->frame_slab) <= 1 && !anon->frame_slab_refilling) {
263        anon->frame_slab_refilling = true;
264        void *buf;
265        err = vspace_pinned_alloc(&buf, FRAME_LIST);
266        if (err_is_fail(err)) {
267            return err_push(err, LIB_ERR_VSPACE_PINNED_ALLOC);
268        }
269        slab_grow(&anon->frame_slab, buf,
270                  VSPACE_PINNED_UNIT * sizeof(struct memobj_frame_list));
271        if (new == NULL) {
272            new = slab_alloc(&anon->frame_slab);
273        }
274        anon->frame_slab_refilling = false;
275    }
276    if (!new) {
277        return LIB_ERR_SLAB_ALLOC_FAIL;
278    }
279    assert(new != NULL);
280    new->offset  = offset;
281    new->frame   = frame;
282    new->size    = size;
283    new->foffset = foffset;
284
285    struct frame_identity fi;
286    err = frame_identify(frame, &fi);
287    if (err_is_fail(err)) {
288        return err_push(err, LIB_ERR_FRAME_IDENTIFY);
289    }
290    assert(err_is_ok(err));
291    new->pa = fi.base;
292
293    // Insert in order
294    struct memobj_frame_list *walk = anon->frame_list;
295    struct memobj_frame_list *prev = NULL;
296    while(walk) {
297        if (new->offset < walk->offset) {
298            if ((prev != NULL && new->offset < prev->offset + prev->size)
299                || new->offset + new->size > walk->offset) {
300                slab_free(&anon->frame_slab, new);
301                return LIB_ERR_MEMOBJ_DUPLICATE_FILL;
302            }
303            new->next  = walk;
304            if (prev != NULL) {
305                prev->next = new;
306            } else {
307                assert(walk == anon->frame_list);
308                anon->frame_list = new;
309            }
310            return SYS_ERR_OK;
311        }
312        prev = walk;
313        walk = walk->next;
314    }
315    if (prev != NULL) {
316        if (new->offset < prev->offset + prev->size) {
317            slab_free(&anon->frame_slab, new);
318            return LIB_ERR_MEMOBJ_DUPLICATE_FILL;
319        }
320        prev->next = new;
321        new->next = NULL;
322    } else {
323        assert(anon->frame_list == NULL);
324        anon->frame_list = new;
325        new->next = NULL;
326    }
327    return SYS_ERR_OK;
328}
329static errval_t fill(struct memobj *memobj, genvaddr_t offset, struct capref frame,
330                     size_t size)
331{
332    return fill_foff(memobj, offset, frame, size, 0);
333}
334
335/**
336 * \brief Unmap/remove one frame from the end of the memobj
337 *
338 * \param memobj     The memory object
339 * \param offset     The offset from which to remove a frame from
340 * \param ret_frame  Pointer to return the removed frame
341 *
342 * This will try to remove one frame at an offset greater than the one
343 * specified. Call this function again and again till it returns the
344 * LIB_ERR_MEMOBJ_UNFILL_TOO_HIGH_OFFSET error to get all frames.
345 */
346static errval_t unfill(struct memobj *memobj, genvaddr_t offset,
347                       struct capref *ret_frame, genvaddr_t *ret_offset)
348{
349    errval_t err;
350    struct memobj_anon *anon = (struct memobj_anon*)memobj;
351
352    // Walk the ordered list of frames to find one right frame
353    struct memobj_frame_list *fwalk = anon->frame_list;
354    struct memobj_frame_list *fprev = NULL;
355    while (fwalk) {
356        if (fwalk->offset < offset) {
357            fprev = fwalk;
358            fwalk = fwalk->next;
359            continue;
360        }
361        goto cont;
362    }
363
364    // The specified offset is too high.
365    return LIB_ERR_MEMOBJ_UNFILL_TOO_HIGH_OFFSET;
366
367 cont:
368
369    { // Unmap the frame from all vregions
370        struct vregion_list *vwalk = anon->vregion_list;
371        while (vwalk) {
372            struct vspace *vspace = vregion_get_vspace(vwalk->region);
373            struct pmap *pmap     = vspace_get_pmap(vspace);
374            genvaddr_t vregion_base  = vregion_get_base_addr(vwalk->region);
375            size_t retsize;
376
377            assert((vregion_base + fwalk->offset) % BASE_PAGE_SIZE == 0);
378            //printf("(%s:%d) unmap(0x%"PRIxGENVADDR", %zd)\n", __FILE__, __LINE__, vregion_base + fwalk->offset, fwalk->size);
379            err = pmap->f.unmap(pmap, vregion_base + fwalk->offset, fwalk->size,
380                                &retsize);
381            if (err_is_fail(err)) {
382                return err_push(err, LIB_ERR_PMAP_UNMAP);
383            }
384            assert(retsize == fwalk->size);
385            vwalk = vwalk->next;
386        }
387    }
388
389    // Return the frame
390    if (ret_offset) {
391        *ret_offset = fwalk->offset;
392    }
393    if (ret_frame) {
394        *ret_frame = fwalk->frame;
395    }
396    if (fprev) {
397        fprev->next = fwalk->next;
398    } else {
399        anon->frame_list = fwalk->next;
400    }
401    slab_free(&anon->frame_slab, fwalk);
402    return SYS_ERR_OK;
403}
404
405/**
406 * \brief Page fault handler
407 *
408 * \param memobj  The memory object
409 * \param region  The associated vregion
410 * \param offset  Offset into memory object of the page fault
411 * \param type    The fault type
412 *
413 * Locates the frame for the offset and maps it in.
414 * Relies on fill inserting frames in order.
415 */
416static errval_t pagefault(struct memobj *memobj, struct vregion *vregion,
417                          genvaddr_t offset, vm_fault_type_t type)
418{
419    errval_t err;
420    struct memobj_anon *anon = (struct memobj_anon*)memobj;
421
422    // Walk the ordered list for the frame and map it in
423    struct memobj_frame_list *walk = anon->frame_list;
424    while (walk) {
425        if (offset >= walk->offset && offset < walk->offset + walk->size) {
426            struct vspace *vspace = vregion_get_vspace(vregion);
427            struct pmap *pmap     = vspace_get_pmap(vspace);
428            genvaddr_t base          = vregion_get_base_addr(vregion);
429            genvaddr_t vregion_off   = vregion_get_offset(vregion);
430            vregion_flags_t flags = vregion_get_flags(vregion);
431            err = pmap->f.map(pmap, base + vregion_off + walk->offset,
432                              walk->frame, walk->foffset, walk->size, flags,
433                              NULL, NULL);
434            if (err_is_fail(err)) {
435                return err_push(err, LIB_ERR_PMAP_MAP);
436            }
437            return SYS_ERR_OK;
438        }
439        walk = walk->next;
440    }
441
442    return LIB_ERR_MEMOBJ_WRONG_OFFSET;
443}
444
445/**
446 * \brief Free up some pages by placing them in the backing storage
447 *
448 * \param memobj      The memory object
449 * \param size        The amount of space to free up
450 * \param frames      An array of capref frames to return the freed pages
451 * \param num_frames  The number of frames returned
452 *
453 * This will affect all the vregions that are associated with the object
454 */
455static errval_t pager_free(struct memobj *memobj, size_t size,
456                           struct capref *frames, size_t num_frames)
457{
458    USER_PANIC("NYI");
459}
460
461/**
462 * \brief Initialize
463 *
464 * \param memobj  The memory object
465 * \param size    Size of the memory region
466 * \param flags   Memory object specific flags
467 *
468 * This object handles multiple frames.
469 * The frames are mapped in on demand.
470 */
471errval_t memobj_create_anon(struct memobj_anon *anon, size_t size,
472                            memobj_flags_t flags)
473{
474    struct memobj *memobj = &anon->m;
475
476    /* Generic portion */
477    memobj->f.map_region   = map_region;
478    memobj->f.unmap_region = unmap_region;
479    memobj->f.protect      = protect;
480    memobj->f.pin          = pin;
481    memobj->f.unpin        = unpin;
482    memobj->f.fill         = fill;
483    memobj->f.fill_foff    = fill_foff;
484    memobj->f.unfill       = unfill;
485    memobj->f.pagefault    = pagefault;
486    memobj->f.pager_free   = pager_free;
487
488    memobj->size  = size;
489    memobj->flags = flags;
490
491    memobj->type = ANONYMOUS;
492
493    /* anon specific portion */
494    slab_init(&anon->vregion_slab, sizeof(struct vregion_list), NULL);
495    slab_init(&anon->frame_slab, sizeof(struct memobj_frame_list), NULL);
496
497    anon->vregion_slab_refilling = false;
498    anon->frame_slab_refilling = false;
499
500    anon->vregion_list = NULL;
501    anon->frame_list = NULL;
502    return SYS_ERR_OK;
503}
504
505/**
506 * \brief Destroy the object
507 *
508 */
509errval_t memobj_destroy_anon(struct memobj *memobj, bool delete_caps)
510{
511    struct memobj_anon *m = (struct memobj_anon *)memobj;
512
513    errval_t err = SYS_ERR_OK;
514
515    struct vregion_list *vwalk = m->vregion_list;
516    while (vwalk) {
517        err = vregion_destroy(vwalk->region);
518        if (err_is_fail(err)) {
519            return err;
520        }
521        struct vregion_list *old = vwalk;
522        vwalk = vwalk->next;
523        slab_free(&m->vregion_slab, old);
524    }
525
526    struct memobj_frame_list *fwalk = m->frame_list;
527    while (fwalk) {
528        if (delete_caps) {
529            err = cap_delete(fwalk->frame);
530            if (err_is_fail(err)) {
531                return err;
532            }
533        }
534        struct memobj_frame_list *old = fwalk;
535        fwalk = fwalk->next;
536        slab_free(&m->frame_slab, old);
537    }
538    return err;
539}
540