libbarrelfish: memobj: add optional frame offset to anonymous memobjs.
[barrelfish] / lib / barrelfish / vspace / memobj_anon.c
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, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
22  */
23
24 #include <barrelfish/barrelfish.h>
25 #include "vspace_internal.h"
26
27 /**
28  * \brief Map the memory object into a region
29  *
30  * \param memobj   The memory object
31  * \param region  The region to add
32  */
33 static errval_t map_region(struct memobj *memobj, struct vregion *vregion)
34 {
35     errval_t err;
36     struct memobj_anon *anon = (struct memobj_anon*)memobj;
37
38     // Allocate space
39     struct vregion_list *data = slab_alloc(&anon->vregion_slab);
40     if (!data) { // Grow
41         void *buf;
42         err = vspace_pinned_alloc(&buf, VREGION_LIST);
43         if (err_is_fail(err)) {
44             return err_push(err, LIB_ERR_VSPACE_PINNED_ALLOC);
45         }
46         slab_grow(&anon->vregion_slab, buf,
47                   VSPACE_PINNED_UNIT * sizeof(struct vregion_list));
48         data = slab_alloc(&anon->vregion_slab);
49         if (!data) {
50             return LIB_ERR_SLAB_ALLOC_FAIL;
51         }
52     }
53     data->region = vregion;
54
55     // Insert into the list
56     struct vregion_list *walk = anon->vregion_list;
57     anon->vregion_list = data;
58     data->next = walk;
59
60     return SYS_ERR_OK;
61 }
62
63 /**
64  * \brief Unmap the memory object from a region
65  *
66  * \param memobj   The memory object
67  * \param region  The region to remove
68  */
69 static errval_t unmap_region(struct memobj *memobj, struct vregion *vregion)
70 {
71     struct memobj_anon *anon = (struct memobj_anon*)memobj;
72     errval_t err;
73
74     /* Unmap the affected area in the pmap */
75     struct vspace *vspace = vregion_get_vspace(vregion);
76     struct pmap *pmap     = vspace_get_pmap(vspace);
77     genvaddr_t vregion_base  = vregion_get_base_addr(vregion);
78     genvaddr_t vregion_off   = vregion_get_offset(vregion);
79     size_t vregion_size = vregion_get_size(vregion);
80     genvaddr_t vregion_end = vregion_off + vregion_size;
81
82     //printf("(%s:%d) unmap(0x%"PRIxGENVADDR", memobj->size = %zd) vregion size = %zd\n", __FILE__, __LINE__, vregion_base + vregion_off, memobj->size, vregion_size);
83
84     // unmap all affected frames
85     struct memobj_frame_list *fwalk = anon->frame_list;
86     struct memobj_frame_list *fprev = NULL;
87     //printf("vregion_off = 0x%"PRIxGENVADDR"\n", vregion_off);
88     //printf("vregion_end = 0x%"PRIxGENVADDR"\n", vregion_end);
89     err = LIB_ERR_VSPACE_VREGION_NOT_FOUND;
90     while (fwalk) {
91         //printf("fwalk->offset = %zd\n", fwalk->offset);
92         //printf("fwalk->next   = %p\n", fwalk->next);
93         if (fwalk->offset < vregion_off) {
94             fprev = fwalk;
95             fwalk = fwalk->next;
96             continue;
97         }
98         else if (fwalk->offset < vregion_end) {
99             err = pmap->f.unmap(pmap, vregion_base + vregion_off, fwalk->size, NULL);
100             if (err_is_fail(err)) {
101                 return err_push(err, LIB_ERR_PMAP_UNMAP);
102             }
103
104             /* Remove the vregion from the list */
105             struct vregion_list *prev = NULL;
106             for (struct vregion_list *elt = anon->vregion_list; elt != NULL;
107                  elt = elt->next) {
108                 if (elt->region == vregion) {
109                     if (prev == NULL) {
110                         assert(elt == anon->vregion_list);
111                         anon->vregion_list = elt->next;
112                     } else {
113                         assert(prev->next == elt);
114                         prev->next = elt->next;
115                     }
116                     slab_free(&anon->vregion_slab, elt);
117                     err = SYS_ERR_OK;
118                 }
119             }
120             vregion_off += fwalk->size;
121             fprev = fwalk;
122             fwalk = fwalk->next;
123         }
124     }
125
126     return err; // XXX: not quite the right error
127 }
128
129 /**
130  * \brief Set the protection on a range
131  *
132  * \param memobj  The memory object
133  * \param region  The vregion to modify the mappings on
134  * \param offset  Offset into the memory object
135  * \param range   The range of space to set the protection for
136  * \param flags   The protection flags
137  */
138 static errval_t protect(struct memobj *memobj, struct vregion *vregion,
139                         genvaddr_t offset, size_t range, vs_prot_flags_t flags)
140 {
141     struct vspace *vspace  = vregion_get_vspace(vregion);
142     struct pmap *pmap      = vspace_get_pmap(vspace);
143     genvaddr_t base        = vregion_get_base_addr(vregion);
144     genvaddr_t vregion_off = vregion_get_offset(vregion);
145     errval_t err;
146
147     err = pmap->f.modify_flags(pmap, base + vregion_off + offset, range,
148                                flags, &range);
149     if (err_is_fail(err)) {
150         return err_push(err, LIB_ERR_PMAP_MODIFY_FLAGS);
151     }
152
153     return SYS_ERR_OK;
154 }
155
156 /**
157  * \brief Pin a range
158  *
159  * \param memobj  The memory object
160  * \param region  The vregion to modify the state on
161  * \param offset  Offset into the memory object
162  * \param range   The range of space to pin
163  */
164 static errval_t pin(struct memobj *memobj, struct vregion *vregion,
165                     genvaddr_t offset, size_t range)
166 {
167     USER_PANIC("NYI");
168 }
169
170 /**
171  * \brief Unpin a range
172  *
173  * \param memobj  The memory object
174  * \param region  The vregion to modify the state on
175  * \param offset  Offset into the memory object
176  * \param range   The range of space to unpin
177  */
178 static errval_t unpin(struct memobj *memobj, struct vregion *vregion,
179                       genvaddr_t offset, size_t range)
180 {
181     USER_PANIC("NYI");
182 }
183
184 /**
185  * \brief Set a frame for an offset into the memobj
186  *
187  * \param memobj  The memory object
188  * \param offset  Offset into the memory object
189  * \param frame   The frame cap for the offset
190  * \param size    The size of frame cap
191  *
192  * Pagefault relies on frames inserted in order
193  */
194 static errval_t fill_foff(struct memobj *memobj, genvaddr_t offset, struct capref frame,
195                      size_t size, genpaddr_t foffset)
196 {
197     errval_t err;
198     struct memobj_anon *anon = (struct memobj_anon*)memobj;
199
200     assert((offset & BASE_PAGE_MASK) == 0);
201
202     // AB: allow frame to overlap end of memobj; that might have been the most
203     // efficient allocation size (even if the end of the frame will be unusable)
204     if (offset >= memobj->size) {
205         return LIB_ERR_MEMOBJ_WRONG_OFFSET;
206     }
207
208     // Allocate
209     struct memobj_frame_list *new = slab_alloc(&anon->frame_slab);
210     if (!new) { // Grow
211         void *buf;
212         err = vspace_pinned_alloc(&buf, FRAME_LIST);
213         if (err_is_fail(err)) {
214             return err_push(err, LIB_ERR_VSPACE_PINNED_ALLOC);
215         }
216         slab_grow(&anon->frame_slab, buf,
217                   VSPACE_PINNED_UNIT * sizeof(struct memobj_frame_list));
218         new = slab_alloc(&anon->frame_slab);
219         if (!new) {
220             return LIB_ERR_SLAB_ALLOC_FAIL;
221         }
222     }
223     new->offset  = offset;
224     new->frame   = frame;
225     new->size    = size;
226     new->foffset = foffset;
227
228     {
229         struct frame_identity id;
230         err = invoke_frame_identify(frame, &id);
231         assert(err_is_ok(err));
232         new->pa = id.base;
233     }
234
235     // Insert in order
236     struct memobj_frame_list *walk = anon->frame_list;
237     struct memobj_frame_list *prev = NULL;
238     while(walk) {
239         if (new->offset < walk->offset) {
240             if ((prev != NULL && new->offset < prev->offset + prev->size)
241                 || new->offset + new->size > walk->offset) {
242                 slab_free(&anon->frame_slab, new);
243                 return LIB_ERR_MEMOBJ_DUPLICATE_FILL;
244             }
245             new->next  = walk;
246             if (prev != NULL) {
247                 prev->next = new;
248             } else {
249                 assert(walk == anon->frame_list);
250                 anon->frame_list = new;
251             }
252             return SYS_ERR_OK;
253         }
254         prev = walk;
255         walk = walk->next;
256     }
257     if (prev != NULL) {
258         if (new->offset < prev->offset + prev->size) {
259             slab_free(&anon->frame_slab, new);
260             return LIB_ERR_MEMOBJ_DUPLICATE_FILL;
261         }
262         prev->next = new;
263         new->next = NULL;
264     } else {
265         assert(anon->frame_list == NULL);
266         anon->frame_list = new;
267         new->next = NULL;
268     }
269     return SYS_ERR_OK;
270 }
271 static errval_t fill(struct memobj *memobj, genvaddr_t offset, struct capref frame,
272                      size_t size)
273 {
274     return fill_foff(memobj, offset, frame, size, 0);
275 }
276
277 /**
278  * \brief Unmap/remove one frame from the end of the memobj
279  *
280  * \param memobj     The memory object
281  * \param offset     The offset from which to remove a frame from
282  * \param ret_frame  Pointer to return the removed frame
283  *
284  * This will try to remove one frame at an offset greater than the one
285  * specified. Call this function again and again till it returns the
286  * LIB_ERR_MEMOBJ_UNFILL_TOO_HIGH_OFFSET error to get all frames.
287  */
288 static errval_t unfill(struct memobj *memobj, genvaddr_t offset,
289                        struct capref *ret_frame, genvaddr_t *ret_offset)
290 {
291     errval_t err;
292     struct memobj_anon *anon = (struct memobj_anon*)memobj;
293
294     // Walk the ordered list of frames to find one right frame
295     struct memobj_frame_list *fwalk = anon->frame_list;
296     struct memobj_frame_list *fprev = NULL;
297     while (fwalk) {
298         if (fwalk->offset < offset) {
299             fprev = fwalk;
300             fwalk = fwalk->next;
301             continue;
302         }
303         goto cont;
304     }
305
306     // The specified offset is too high.
307     return LIB_ERR_MEMOBJ_UNFILL_TOO_HIGH_OFFSET;
308
309  cont:
310
311     { // Unmap the frame from all vregions
312         struct vregion_list *vwalk = anon->vregion_list;
313         while (vwalk) {
314             struct vspace *vspace = vregion_get_vspace(vwalk->region);
315             struct pmap *pmap     = vspace_get_pmap(vspace);
316             genvaddr_t vregion_base  = vregion_get_base_addr(vwalk->region);
317             size_t retsize;
318
319             assert((vregion_base + fwalk->offset) % BASE_PAGE_SIZE == 0);
320             //printf("(%s:%d) unmap(0x%"PRIxGENVADDR", %zd)\n", __FILE__, __LINE__, vregion_base + fwalk->offset, fwalk->size);
321             err = pmap->f.unmap(pmap, vregion_base + fwalk->offset, fwalk->size,
322                                 &retsize);
323             if (err_is_fail(err)) {
324                 return err_push(err, LIB_ERR_PMAP_UNMAP);
325             }
326             assert(retsize == fwalk->size);
327             vwalk = vwalk->next;
328         }
329     }
330
331     // Return the frame
332     *ret_offset = fwalk->offset;
333     *ret_frame = fwalk->frame;
334     if (fprev) {
335         fprev->next = fwalk->next;
336     } else {
337         anon->frame_list = fwalk->next;
338     }
339     slab_free(&anon->frame_slab, fwalk);
340     return SYS_ERR_OK;
341 }
342
343 /**
344  * \brief Page fault handler
345  *
346  * \param memobj  The memory object
347  * \param region  The associated vregion
348  * \param offset  Offset into memory object of the page fault
349  * \param type    The fault type
350  *
351  * Locates the frame for the offset and maps it in.
352  * Relies on fill inserting frames in order.
353  */
354 static errval_t pagefault(struct memobj *memobj, struct vregion *vregion,
355                           genvaddr_t offset, vm_fault_type_t type)
356 {
357     errval_t err;
358     struct memobj_anon *anon = (struct memobj_anon*)memobj;
359
360     // Walk the ordered list for the frame and map it in
361     struct memobj_frame_list *walk = anon->frame_list;
362     while (walk) {
363         if (offset >= walk->offset && offset < walk->offset + walk->size) {
364             struct vspace *vspace = vregion_get_vspace(vregion);
365             struct pmap *pmap     = vspace_get_pmap(vspace);
366             genvaddr_t base          = vregion_get_base_addr(vregion);
367             genvaddr_t vregion_off   = vregion_get_offset(vregion);
368             vregion_flags_t flags = vregion_get_flags(vregion);
369             err = pmap->f.map(pmap, base + vregion_off + walk->offset,
370                               walk->frame, walk->foffset, walk->size, flags,
371                               NULL, NULL);
372             if (err_is_fail(err)) {
373                 return err_push(err, LIB_ERR_PMAP_MAP);
374             }
375             return SYS_ERR_OK;
376         }
377         walk = walk->next;
378     }
379
380     return LIB_ERR_MEMOBJ_WRONG_OFFSET;
381 }
382
383 /**
384  * \brief Free up some pages by placing them in the backing storage
385  *
386  * \param memobj      The memory object
387  * \param size        The amount of space to free up
388  * \param frames      An array of capref frames to return the freed pages
389  * \param num_frames  The number of frames returned
390  *
391  * This will affect all the vregions that are associated with the object
392  */
393 static errval_t pager_free(struct memobj *memobj, size_t size,
394                            struct capref *frames, size_t num_frames)
395 {
396     USER_PANIC("NYI");
397 }
398
399 /**
400  * \brief Initialize
401  *
402  * \param memobj  The memory object
403  * \param size    Size of the memory region
404  * \param flags   Memory object specific flags
405  *
406  * This object handles multiple frames.
407  * The frames are mapped in on demand.
408  */
409 errval_t memobj_create_anon(struct memobj_anon *anon, size_t size,
410                             memobj_flags_t flags)
411 {
412     struct memobj *memobj = &anon->m;
413
414     /* Generic portion */
415     memobj->f.map_region   = map_region;
416     memobj->f.unmap_region = unmap_region;
417     memobj->f.protect      = protect;
418     memobj->f.pin          = pin;
419     memobj->f.unpin        = unpin;
420     memobj->f.fill         = fill;
421     memobj->f.fill_foff    = fill_foff;
422     memobj->f.unfill       = unfill;
423     memobj->f.pagefault    = pagefault;
424     memobj->f.pager_free   = pager_free;
425
426     memobj->size  = size;
427     memobj->flags = flags;
428
429     memobj->type = ANONYMOUS;
430
431     /* anon specific portion */
432     slab_init(&anon->vregion_slab, sizeof(struct vregion_list), NULL);
433     slab_init(&anon->frame_slab, sizeof(struct memobj_frame_list), NULL);
434
435     anon->vregion_list = NULL;
436     anon->frame_list = NULL;
437     return SYS_ERR_OK;
438 }
439
440 /**
441  * \brief Destroy the object
442  *
443  */
444 errval_t memobj_destroy_anon(struct memobj *memobj)
445 {
446     struct memobj_anon *m = (struct memobj_anon *)memobj;
447
448     errval_t err = SYS_ERR_OK;
449
450     struct vregion_list *vwalk = m->vregion_list;
451     while (vwalk) {
452         err = vregion_destroy(vwalk->region);
453         if (err_is_fail(err)) {
454             return err;
455         }
456         struct vregion_list *old = vwalk;
457         vwalk = vwalk->next;
458         slab_free(&m->vregion_slab, old);
459     }
460
461     struct memobj_frame_list *fwalk = m->frame_list;
462     while (fwalk) {
463         err = cap_delete(fwalk->frame);
464         if (err_is_fail(err)) {
465             return err;
466         }
467         struct memobj_frame_list *old = fwalk;
468         fwalk = fwalk->next;
469         slab_free(&m->frame_slab, old);
470     }
471     return err;
472 }