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#define CHECK_FRAME_SIZE 0
30
31
32/**
33 * \brief Map the memory object into a region
34 *
35 * \param memobj   The memory object
36 * \param region  The region to add
37 */
38static errval_t map_region(struct memobj *memobj, struct vregion *vregion)
39{
40    struct memobj_numa *mo_numa = (struct memobj_numa*) memobj;
41
42    /* make sure we are not overshooting the end */
43    assert(memobj->size >= (vregion->offset + vregion->size));
44
45    /* the vregion must start at one of the backed frames */
46    if (vregion->offset % mo_numa->stride) {
47        return LIB_ERR_MEMOBJ_MAP_REGION;
48    }
49
50    if (mo_numa->vregion) {
51        return LIB_ERR_MEMOBJ_VREGION_ALREADY_MAPPED;
52    }
53
54    mo_numa->vregion = vregion;
55
56    return SYS_ERR_OK;
57}
58
59/**
60 * \brief Unmap the memory object from a region
61 *
62 * \param memobj   The memory object
63 * \param region  The region to remove
64 */
65static errval_t unmap_region(struct memobj *memobj, struct vregion *vregion)
66{
67    struct memobj_numa *mo_numa = (struct memobj_numa*) memobj;
68    errval_t err;
69
70    if (mo_numa->vregion != vregion) {
71        return LIB_ERR_VSPACE_VREGION_NOT_FOUND;
72    }
73
74    struct vspace *vspace = vregion_get_vspace(vregion);
75    struct pmap *pmap = vspace_get_pmap(vspace);
76
77    genvaddr_t vreg_base = vregion_get_base_addr(vregion);
78    genvaddr_t vreg_offset = vregion_get_offset(vregion);
79
80    err = pmap->f.unmap(pmap, vreg_base + vreg_offset, vregion->size, NULL);
81    if (err_is_fail(err)) {
82        return err_push(err, LIB_ERR_PMAP_UNMAP);
83    }
84
85    mo_numa->vregion = NULL;
86
87    return SYS_ERR_OK;
88}
89
90/**
91 * \brief Set the protection on a range
92 *
93 * \param memobj  The memory object
94 * \param region  The vregion to modify the mappings on
95 * \param offset  Offset into the memory object
96 * \param range   The range of space to set the protection for
97 * \param flags   The protection flags
98 */
99static errval_t protect(struct memobj *memobj,
100                        struct vregion *vregion,
101                        genvaddr_t offset,
102                        size_t range,
103                        vs_prot_flags_t flags)
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    genvaddr_t vregion_offset = vregion_get_offset(vregion);
109    errval_t err;
110    size_t ret_size;
111    err = pmap->f.modify_flags(pmap, base + offset + vregion_offset, range,
112                               flags, &ret_size);
113    if (err_is_fail(err)) {
114        return err_push(err, LIB_ERR_PMAP_MODIFY_FLAGS);
115    }
116
117    return SYS_ERR_OK;
118}
119
120/**
121 * \brief Pin a range
122 *
123 * \param memobj  The memory object
124 * \param region  The vregion to modify the state on
125 * \param offset  Offset into the memory object
126 * \param range   The range of space to pin
127 */
128static errval_t pin(struct memobj *memobj,
129                    struct vregion *vregion,
130                    genvaddr_t offset,
131                    size_t range)
132{
133    USER_PANIC("NYI");
134
135    return SYS_ERR_OK;
136}
137
138/**
139 * \brief Unpin a range
140 *
141 * \param memobj  The memory object
142 * \param region  The vregion to modify the state on
143 * \param offset  Offset into the memory object
144 * \param range   The range of space to unpin
145 */
146static errval_t unpin(struct memobj *memobj,
147                      struct vregion *vregion,
148                      genvaddr_t offset,
149                      size_t range)
150{
151    USER_PANIC("NYI");
152
153    return SYS_ERR_OK;
154}
155
156/**
157 * \brief Set a frame for an offset into the memobj
158 *
159 * \param memobj  The memory object
160 * \param offset  Offset into the memory object
161 * \param frame   The frame cap for the offset
162 * \param offset  The offset into the frame cap
163 *
164 * Pagefault relies on frames inserted in order
165 */
166static errval_t fill(struct memobj *memobj,
167                     genvaddr_t offset,
168                     struct capref frame,
169                     size_t frame_offset)
170{
171    struct memobj_numa *mo_numa = (struct memobj_numa*) memobj;
172
173    /* we take a single capability per node using offset as the node ID */
174    if (offset >= mo_numa->node_count) {
175        return LIB_ERR_MEMOBJ_WRONG_OFFSET;
176    }
177
178    /* check if we already have a capability for that node */
179    if (!capref_is_null((mo_numa->frames[offset]))) {
180        return LIB_ERR_MEMOBJ_DUPLICATE_FILL;
181    }
182
183#if CHECK_FRAME_SIZE
184    errval_t err;
185
186    /* we must make sure the frame is big enough */
187    struct frame_identity fid;
188
189    err = frame_identify(frame, &fid);
190    if (err_is_fail(err)) {
191        return err_push(err, LIB_ERR_MEMOBJ_FILL);
192    }
193
194    if (fid.bytes < memobj->size / mo_numa->node_count) {\
195        return LIB_ERR_MEMOBJ_FILL;
196    }
197
198#endif
199
200    mo_numa->frames[offset] = frame;
201
202    return SYS_ERR_OK;
203}
204
205/**
206 * \brief Unmap/remove one frame from the end of the memobj
207 *
208 * \param memobj     The memory object
209 * \param offset     The offset from which to remove a frame from
210 * \param ret_frame  Pointer to return the removed frame
211 *
212 */
213static errval_t unfill(struct memobj *memobj,
214                       genvaddr_t offset,
215                       struct capref *ret_frame,
216                       genvaddr_t *ret_offset)
217{
218    struct memobj_numa *mo_numa = (struct memobj_numa*) memobj;
219
220    /* we take a single capability per node using offset as the node ID */
221    if (offset >= mo_numa->node_count) {
222        return LIB_ERR_MEMOBJ_WRONG_OFFSET;
223    }
224
225    /* check if we already have a capability for that node */
226    if (capref_is_null((mo_numa->frames[offset]))) {
227        return LIB_ERR_MEMOBJ_UNFILL_TOO_HIGH_OFFSET;
228    }
229
230    if (ret_frame) {
231        *ret_frame = mo_numa->frames[offset];
232    }
233    mo_numa->frames[offset] = NULL_CAP;
234
235    return SYS_ERR_OK;
236}
237
238
239/**
240 * \brief Page fault handler
241 *
242 * \param memobj  The memory object
243 * \param region  The associated vregion
244 * \param offset  Offset into memory object of the page fault
245 * \param type    The fault type
246 *
247 * Locates the frame for the offset and maps it in.
248 * Relies on fill inserting frames in order.
249 */
250static errval_t pagefault(struct memobj *memobj,
251                          struct vregion *vregion,
252                          genvaddr_t offset,
253                          vm_fault_type_t type)
254{
255    errval_t err;
256
257    struct memobj_numa *mo_numa = (struct memobj_numa*) memobj;
258
259    assert(mo_numa->vregion == vregion);
260
261    size_t size = memobj->size;
262
263    for (uint32_t i = 0; i < mo_numa->node_count; ++i) {
264        if (capref_is_null(mo_numa->frames[i])) {
265            return LIB_ERR_MEMOBJ_PAGEFAULT_HANDLER;
266        }
267    }
268
269    uint32_t node = 0;
270
271    struct vspace *vspace = vregion_get_vspace(vregion);
272    struct pmap *pmap = vspace_get_pmap(vspace);
273    genvaddr_t base = vregion_get_base_addr(vregion);
274    genvaddr_t vregion_offset = vregion_get_offset(vregion);
275    vregion_flags_t flags = vregion_get_flags(vregion);
276
277    lvaddr_t map_offset = 0;
278    lvaddr_t map_size = mo_numa->stride;
279
280    while(size) {
281        if (size < map_size) {
282            map_size = size;
283        }
284
285        //debug_printf("mapping: vaddr=%lx, node=%u, frameoffset=%lx\n",
286        //             base + vregion_offset + offset, node, map_offset);
287
288        /*
289         * create a copy of the capability to map it as a capability can only
290         * be mapped once
291         */
292        struct capref copy;
293        err = slot_alloc(&copy);
294        if (err_is_fail(err)) {
295            return err_push(err, LIB_ERR_MEMOBJ_PAGEFAULT_HANDLER);
296        }
297
298        err = cap_copy(copy, mo_numa->frames[node]);
299        if (err_is_fail(err)){
300            return err_push(err, LIB_ERR_MEMOBJ_PAGEFAULT_HANDLER);
301        }
302
303        /* map the copy */
304        err = pmap->f.map(pmap, base + vregion_offset + offset, copy,
305                          map_offset, map_size, flags, NULL, NULL);
306        if (err_is_fail(err)) {
307                return err_push(err, LIB_ERR_PMAP_MAP);
308        }
309
310        ++node;
311        size -= map_size;
312        offset += map_size;
313
314        if (node == mo_numa->node_count) {
315            node = 0;
316            map_offset += mo_numa->stride;
317        }
318    }
319
320    return SYS_ERR_OK;
321}
322
323/**
324 * \brief Free up some pages by placing them in the backing storage
325 *
326 * \param memobj      The memory object
327 * \param size        The amount of space to free up
328 * \param frames      An array of capref frames to return the freed pages
329 * \param num_frames  The number of frames returned
330 *
331 * This will affect all the vregions that are associated with the object
332 */
333static errval_t pager_free(struct memobj *memobj,
334                           size_t size,
335                           struct capref *frames,
336                           size_t num_frames)
337{
338    USER_PANIC("NYI");
339
340    return SYS_ERR_OK;
341}
342
343/**
344 * \brief Initialize
345 *
346 * \param memobj  The memory object
347 * \param size    Size of the memory region
348 * \param flags   Memory object specific flags
349 *
350 * This object handles multiple frames.
351 * The frames are mapped in on demand.
352 */
353errval_t memobj_create_numa(struct memobj_numa *mo_numa,
354                            size_t size,
355                            memobj_flags_t flags,
356                            size_t node_count,
357                            size_t stride)
358{
359    struct memobj *memobj = &mo_numa->m;
360
361    assert((stride % BASE_PAGE_SIZE)==0);
362
363    /* Generic portion */
364    memobj->f.map_region = map_region;
365    memobj->f.unmap_region = unmap_region;
366    memobj->f.protect = protect;
367    memobj->f.pin = pin;
368    memobj->f.unpin = unpin;
369    memobj->f.fill = fill;
370    memobj->f.unfill = unfill;
371    memobj->f.pagefault = pagefault;
372    memobj->f.pager_free = pager_free;
373
374    memobj->size = size;
375    memobj->flags = flags;
376    memobj->type = MEMOBJ_NUMA;
377
378    /* specific portion */
379    mo_numa->node_count = node_count;
380    mo_numa->stride = stride;
381    mo_numa->vregion = NULL;
382
383    mo_numa->frames = calloc(node_count, sizeof(struct capref));
384    if (mo_numa->frames == NULL) {
385        return LIB_ERR_MALLOC_FAIL;
386    }
387
388    return SYS_ERR_OK;
389}
390
391/**
392 * \brief Destroy the object
393 *
394 */
395errval_t memobj_destroy_numa(struct memobj *memobj)
396{
397    struct memobj_numa *mo_numa = (struct memobj_numa *) memobj;
398
399    errval_t err = SYS_ERR_OK;
400
401    struct vregion *vregion = mo_numa->vregion;
402
403    err = vregion_destroy(vregion);
404    if (mo_numa->frames) {
405        free(mo_numa->frames);
406    }
407
408    return err;
409}
410