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 * All rights reserved.
18 *
19 * This file is distributed under the terms in the attached LICENSE file.
20 * If you do not find this file, copies can be found by writing to:
21 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
22 */
23
24#include <string.h>
25
26#include <barrelfish/barrelfish.h>
27#include "vspace_internal.h"
28
29/**
30 * \brief Map the memory object into a region
31 *
32 * \param memobj   The memory object
33 * \param region  The region to add
34 */
35static errval_t map_region(struct memobj *memobj, struct vregion *vregion)
36{
37    struct memobj_append *append = (struct memobj_append*) memobj;
38
39    /* make sure we are not overshooting the end */
40    assert(memobj->size >= (vregion->offset + vregion->size));
41
42    /* the vregion must start at the beginning of the memobj */
43    if (vregion->offset != 0) {
44        return LIB_ERR_MEMOBJ_MAP_REGION;
45    }
46
47    if (append->vregion) {
48        return LIB_ERR_MEMOBJ_VREGION_ALREADY_MAPPED;
49    }
50
51    append->vregion = vregion;
52
53    return SYS_ERR_OK;
54}
55
56/**
57 * \brief Unmap the memory object from a region
58 *
59 * \param memobj   The memory object
60 * \param region  The region to remove
61 */
62static errval_t unmap_region(struct memobj *memobj, struct vregion *vregion)
63{
64    struct memobj_append *append = (struct memobj_append*) memobj;
65    errval_t err;
66
67    if (append->vregion != vregion) {
68        return LIB_ERR_VSPACE_VREGION_NOT_FOUND;
69    }
70
71    struct vspace *vspace = vregion_get_vspace(vregion);
72    struct pmap *pmap = vspace_get_pmap(vspace);
73
74    genvaddr_t vregion_base = vregion_get_base_addr(vregion);
75    genvaddr_t vregion_offset = vregion_get_offset(vregion);
76
77    err = pmap->f.unmap(pmap, vregion_base + vregion_offset, vregion->size,
78                        NULL);
79    if (err_is_fail(err)) {
80        return err_push(err, LIB_ERR_PMAP_UNMAP);
81    }
82
83    append->vregion = NULL;
84
85    return SYS_ERR_OK;
86}
87
88/**
89 * \brief Set the protection on a range
90 *
91 * \param memobj  The memory object
92 * \param region  The vregion to modify the mappings on
93 * \param offset  Offset into the memory object
94 * \param range   The range of space to set the protection for
95 * \param flags   The protection flags
96 */
97static errval_t protect(struct memobj *memobj,
98                        struct vregion *vregion,
99                        genvaddr_t offset,
100                        size_t range,
101                        vs_prot_flags_t flags)
102{
103    struct memobj_append *append = (struct memobj_append *) memobj;
104
105    struct vspace *vspace = vregion_get_vspace(vregion);
106    struct pmap *pmap = vspace_get_pmap(vspace);
107    genvaddr_t base = vregion_get_base_addr(vregion);
108    assert(vregion_get_offset(vregion) == 0);
109    errval_t err;
110    size_t ret_size;
111
112    // short-circuit small requests
113    if (range <= BASE_PAGE_SIZE) {
114        err = pmap->f.modify_flags(pmap, base + offset, range,
115                                   flags, &ret_size);
116        if (err_is_fail(err)) {
117            return err_push(err, LIB_ERR_PMAP_MODIFY_FLAGS);
118        }
119        return SYS_ERR_OK;
120    } else if (offset == 0 && range > append->offsets[append->first_free_frame-1]) {
121        // need to call modify_flags on every frame
122        for (int i = 0; i < append->first_free_frame && range; i++) {
123            size_t fsize = append->frame_sizes[i];
124            size_t to_protect = fsize < range ? fsize : range;
125
126            assert(to_protect > 0);
127            err = pmap->f.modify_flags(pmap, base + offset, to_protect,
128                                       flags, &ret_size);
129            /*
130            err = invoke_frame_modify_flags(append->frames[i], 0,
131                    to_protect / BASE_PAGE_SIZE, flags, base + offset);
132                    */
133            if (err_is_fail(err)) {
134                return err_push(err, LIB_ERR_PMAP_MODIFY_FLAGS);
135            }
136            range -= ret_size;
137            offset += ret_size;
138        }
139        return SYS_ERR_OK;
140    } else {
141
142#if 1
143        // binary search
144        int imin = 0;
145        int imax = append->first_free_frame - 1;
146        int first_idx = -1;
147        int found = 0;
148        while (!found && (imin <= imax)) {
149            int imid = (imin + imax) / 2;
150//            debug_printf("imin=%d, imid=%d, imax=%d\n", imin, imid, imax);
151            genvaddr_t mid_offset = append->offsets[imid];
152            if (mid_offset < offset) {
153                imin = imid + 1;
154            } else if (mid_offset > offset) {
155                imax = imid - 1;
156            } else {
157                first_idx = imid;
158                found = 1;
159            }
160        }
161        if (!found) {
162            first_idx = imax;
163        }
164
165#else
166        // linear search
167        int first_idx = -1;
168        for (first_idx = 0;
169             first_idx < append->first_free_frame && append->offsets[first_idx] < offset;
170             first_idx++);
171        // XXX Bug: Do not adjust if we find last frame
172        first_idx -= 1;
173#endif
174
175#if 0
176        debug_printf("offset -> %zx range -> %zx flags -> %zu\n", offset, range, (size_t) flags);
177        for (int i = 0; i < append->first_free_frame; i++) {
178            debug_printf("offsets[%d] = %"PRIxGENVADDR"\n", i, append->offsets[i]);
179        }
180        debug_printf("offset = %zx --> index = %d -> %zx, %zx\n", offset, first_idx,
181                append->offsets[first_idx], first_idx < append->first_free_frame-1 ? append->offsets[first_idx+1] : 0x0);
182#endif
183
184        while(range) {
185            size_t fsize = append->frame_sizes[first_idx] - (offset - append->offsets[first_idx]);
186            size_t to_protect = fsize < range ? fsize : range;
187
188            /// XXX: offset % BASE_PAGE_SIZE != 0?
189            err = pmap->f.modify_flags(pmap, base + offset, to_protect,
190                                       flags, &ret_size);
191            /*
192            err = invoke_frame_modify_flags(append->frames[i], 0,
193                    to_protect / BASE_PAGE_SIZE, flags, base + offset);
194                    */
195            if (err_is_fail(err)) {
196                return err_push(err, LIB_ERR_PMAP_MODIFY_FLAGS);
197            }
198            range -= ret_size;
199            offset += ret_size;
200            first_idx++;
201        }
202        //USER_PANIC("memobj_append: protect: searching for frame NYI\n");
203    }
204
205    return SYS_ERR_OK;
206}
207
208/**
209 * \brief Pin a range
210 *
211 * \param memobj  The memory object
212 * \param region  The vregion to modify the state on
213 * \param offset  Offset into the memory object
214 * \param range   The range of space to pin
215 */
216static errval_t pin(struct memobj *memobj,
217                    struct vregion *vregion,
218                    genvaddr_t offset,
219                    size_t range)
220{
221    USER_PANIC("NYI");
222}
223
224/**
225 * \brief Unpin a range
226 *
227 * \param memobj  The memory object
228 * \param region  The vregion to modify the state on
229 * \param offset  Offset into the memory object
230 * \param range   The range of space to unpin
231 */
232static errval_t unpin(struct memobj *memobj,
233                      struct vregion *vregion,
234                      genvaddr_t offset,
235                      size_t range)
236{
237    USER_PANIC("NYI");
238}
239
240/**
241 * \brief Set a frame for an offset into the memobj
242 *
243 * \param memobj  The memory object
244 * \param offset  Offset into the memory object
245 * \param frame   The frame cap for the offset
246 * \param frame_offset  The offset into the frame cap
247 *
248 * Pagefault relies on frames inserted in order
249 */
250static errval_t fill(struct memobj *memobj,
251                     genvaddr_t offset,
252                     struct capref frame,
253                     size_t frame_offset)
254{
255    struct memobj_append *append = (struct memobj_append*) memobj;
256
257    size_t slot = append->first_free_frame;
258    assert(slot <= append->frame_count);
259
260    // XXX: better error checking
261    if (offset > 0 && offset < append->offsets[slot-1]) {
262        return LIB_ERR_MEMOBJ_FILL;
263    }
264
265    if (slot == append->frame_count) {
266        // grow arrays
267        size_t new_count = append->frame_count * 2;
268        void *f  = realloc(append->frames, new_count * sizeof(struct capref));
269        void *o  = realloc(append->offsets, new_count * sizeof(genvaddr_t));
270        void *s  = realloc(append->frame_sizes, new_count * sizeof(size_t));
271        void *fo = realloc(append->frame_offsets, new_count * sizeof(size_t));
272        if (!f || !o || !s || !fo) {
273            return LIB_ERR_MALLOC_FAIL;
274        }
275        append->frames = f;
276        append->offsets = o;
277        append->frame_sizes = s;
278        append->frame_offsets = fo;
279        memset(&append->frames[append->first_free_frame],
280                0, append->frame_count * sizeof(struct capref));
281        memset(&append->offsets[append->first_free_frame],
282                0, append->frame_count * sizeof(genvaddr_t));
283        memset(&append->frame_sizes[append->first_free_frame],
284                0, append->frame_count * sizeof(size_t));
285        memset(&append->frame_offsets[append->first_free_frame],
286                0, append->frame_count * sizeof(size_t));
287        append->frame_count = new_count;
288    }
289
290    if (!capref_is_null((append->frames[slot]))) {
291        return LIB_ERR_MEMOBJ_DUPLICATE_FILL;
292    }
293
294    struct frame_identity fi;
295    errval_t err = frame_identify(frame, &fi);
296    assert(err_is_ok(err));
297
298    append->frames[slot] = frame;
299    append->offsets[slot] = offset;
300    append->frame_sizes[slot] = fi.bytes;
301    append->frame_offsets[slot] = frame_offset;
302    append->first_free_frame++;
303
304    return SYS_ERR_OK;
305}
306
307/**
308 * \brief Unmap/remove one frame from the end of the memobj
309 *
310 * \param memobj     The memory object
311 * \param offset     The offset from which to remove a frame from
312 * \param ret_frame  Pointer to return the removed frame
313 *
314 */
315static errval_t unfill(struct memobj *memobj,
316                       genvaddr_t offset,
317                       struct capref *ret_frame,
318                       genvaddr_t *ret_offset)
319{
320    USER_PANIC("NYI\n");
321    // this might be implemented as removing the last frame in the obj
322}
323
324/**
325 * \brief Page fault handler
326 *
327 * \param memobj  The memory object
328 * \param region  The associated vregion
329 * \param offset  Offset into memory object of the page fault
330 * \param type    The fault type
331 *
332 * Locates the frame for the offset and maps it in.
333 * Relies on fill inserting frames in order.
334 */
335static errval_t pagefault(struct memobj *memobj,
336                          struct vregion *vregion,
337                          genvaddr_t offset,
338                          vm_fault_type_t type)
339{
340    errval_t err;
341    struct memobj_append *append = (struct memobj_append*) memobj;
342
343    assert(!(offset % BASE_PAGE_SIZE));
344    assert(offset == append->already_faulted);
345
346    size_t first_unfaulted = append->first_unfaulted;
347
348    if (capref_is_null(append->frames[first_unfaulted])) {
349        return LIB_ERR_MEMOBJ_PAGEFAULT_HANDLER;
350    }
351
352    struct vspace *vspace = vregion_get_vspace(vregion);
353    struct pmap *pmap = vspace_get_pmap(vspace);
354
355    genvaddr_t base = vregion_get_base_addr(vregion);
356    assert(vregion_get_offset(vregion) == 0);
357    vregion_flags_t flags = vregion_get_flags(vregion);
358
359    size_t retsize;
360    err = pmap->f.map(pmap, base + offset, append->frames[first_unfaulted],
361                      append->frame_offsets[first_unfaulted],
362                      append->frame_sizes[first_unfaulted], flags,
363                      NULL, &retsize);
364    if (err_is_fail(err)) {
365        return err_push(err, LIB_ERR_PMAP_MAP);
366    }
367    // update metadata
368    append->first_unfaulted++;
369    append->already_faulted += retsize;
370
371    return SYS_ERR_OK;
372}
373
374/**
375 * \brief Free up some pages by placing them in the backing storage
376 *
377 * \param memobj      The memory object
378 * \param size        The amount of space to free up
379 * \param frames      An array of capref frames to return the freed pages
380 * \param num_frames  The number of frames returned
381 *
382 * This will affect all the vregions that are associated with the object
383 */
384static errval_t pager_free(struct memobj *memobj,
385                           size_t size,
386                           struct capref *frames,
387                           size_t num_frames)
388{
389    USER_PANIC("NYI");
390}
391
392/**
393 * \brief Initialize
394 *
395 * \param memobj  The memory object
396 * \param size    Size of the memory region
397 * \param flags   Memory object specific flags
398 *
399 * This object handles multiple frames.
400 * The frames are mapped in on demand.
401 */
402errval_t memobj_create_append(struct memobj_append *append,
403                              size_t size,
404                              memobj_flags_t flags)
405{
406    struct memobj *memobj = &append->m;
407
408    /* Generic portion */
409    memobj->f.map_region = map_region;
410    memobj->f.unmap_region = unmap_region;
411    memobj->f.protect = protect;
412    memobj->f.pin = pin;
413    memobj->f.unpin = unpin;
414    memobj->f.fill = fill;
415    memobj->f.unfill = unfill;
416    memobj->f.pagefault = pagefault;
417    memobj->f.pager_free = pager_free;
418
419    memobj->size = size;
420    memobj->flags = flags;
421
422    memobj->type = MEMOBJ_APPEND;
423
424    /* specific portion */
425    append->vregion = NULL;
426
427#define INIT_FRAME_COUNT 8
428    size_t count = append->frame_count = INIT_FRAME_COUNT;
429    append->first_free_frame = 0;
430    append->first_unfaulted = 0;
431
432    append->frames = malloc(count * sizeof(struct capref));
433    if (!append->frames) {
434        return LIB_ERR_MALLOC_FAIL;
435    }
436    memset(append->frames, 0, count * sizeof(struct capref));
437
438    append->offsets = malloc(count * sizeof(genvaddr_t));
439    if (!append->offsets) {
440        free(append->frames);
441        return LIB_ERR_MALLOC_FAIL;
442    }
443    memset(append->offsets, 0, count * sizeof(genvaddr_t));
444
445    append->frame_sizes = malloc(count * sizeof(size_t));
446    if (!append->frame_sizes) {
447        free(append->frames);
448        free(append->offsets);
449        return LIB_ERR_MALLOC_FAIL;
450    }
451    memset(append->frame_sizes, 0, count * sizeof(size_t));
452
453    append->frame_offsets = malloc(count * sizeof(size_t));
454    if (!append->frame_offsets) {
455        free(append->frames);
456        free(append->offsets);
457        free(append->frame_sizes);
458        return LIB_ERR_MALLOC_FAIL;
459    }
460    memset(append->frame_offsets, 0, count * sizeof(size_t));
461
462    return SYS_ERR_OK;
463}
464
465/**
466 * \brief Destroy the object
467 *
468 */
469errval_t memobj_destroy_append(struct memobj *memobj)
470{
471    struct memobj_append *m = (struct memobj_append *) memobj;
472
473    errval_t err = SYS_ERR_OK;
474
475    err = vregion_destroy(m->vregion);
476    free(m->frames);
477    free(m->offsets);
478    free(m->frame_sizes);
479    return err;
480}
481