remove support for Beehive
[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
80     err = pmap->f.unmap(pmap, vregion_base + vregion_off, memobj->size, NULL);
81     if (err_is_fail(err)) {
82         return err_push(err, LIB_ERR_PMAP_UNMAP);
83     }
84
85     /* Remove the vregion from the list */
86     struct vregion_list *prev = NULL;
87     for (struct vregion_list *elt = anon->vregion_list; elt != NULL;
88          elt = elt->next) {
89         if (elt->region == vregion) {
90             if (prev == NULL) {
91                 assert(elt == anon->vregion_list);
92                 anon->vregion_list = elt->next;
93             } else {
94                 assert(prev->next == elt);
95                 prev->next = elt->next;
96             }
97             slab_free(&anon->vregion_slab, elt);
98             return SYS_ERR_OK;
99         }
100     }
101
102     return LIB_ERR_VSPACE_VREGION_NOT_FOUND; // XXX: not quite the right error
103 }
104
105 /**
106  * \brief Set the protection on a range
107  *
108  * \param memobj  The memory object
109  * \param region  The vregion to modify the mappings on
110  * \param offset  Offset into the memory object
111  * \param range   The range of space to set the protection for
112  * \param flags   The protection flags
113  */
114 static errval_t protect(struct memobj *memobj, struct vregion *vregion,
115                         genvaddr_t offset, size_t range, vs_prot_flags_t flags)
116 {
117     struct vspace *vspace  = vregion_get_vspace(vregion);
118     struct pmap *pmap      = vspace_get_pmap(vspace);
119     genvaddr_t base        = vregion_get_base_addr(vregion);
120     genvaddr_t vregion_off = vregion_get_offset(vregion);
121     errval_t err;
122
123     err = pmap->f.modify_flags(pmap, base + vregion_off + offset, range,
124                                flags, &range);
125     if (err_is_fail(err)) {
126         return err_push(err, LIB_ERR_PMAP_MODIFY_FLAGS);
127     }
128
129     return SYS_ERR_OK;
130 }
131
132 /**
133  * \brief Pin a range
134  *
135  * \param memobj  The memory object
136  * \param region  The vregion to modify the state on
137  * \param offset  Offset into the memory object
138  * \param range   The range of space to pin
139  */
140 static errval_t pin(struct memobj *memobj, struct vregion *vregion,
141                     genvaddr_t offset, size_t range)
142 {
143     USER_PANIC("NYI");
144 }
145
146 /**
147  * \brief Unpin a range
148  *
149  * \param memobj  The memory object
150  * \param region  The vregion to modify the state on
151  * \param offset  Offset into the memory object
152  * \param range   The range of space to unpin
153  */
154 static errval_t unpin(struct memobj *memobj, struct vregion *vregion,
155                       genvaddr_t offset, size_t range)
156 {
157     USER_PANIC("NYI");
158 }
159
160 /**
161  * \brief Set a frame for an offset into the memobj
162  *
163  * \param memobj  The memory object
164  * \param offset  Offset into the memory object
165  * \param frame   The frame cap for the offset
166  * \param size    The size of frame cap
167  *
168  * Pagefault relies on frames inserted in order
169  */
170 static errval_t fill(struct memobj *memobj, genvaddr_t offset, struct capref frame,
171                      size_t size)
172 {
173     errval_t err;
174     struct memobj_anon *anon = (struct memobj_anon*)memobj;
175
176     assert((offset & BASE_PAGE_MASK) == 0);
177
178     // AB: allow frame to overlap end of memobj; that might have been the most
179     // efficient allocation size (even if the end of the frame will be unusable)
180     if (offset >= memobj->size) {
181         return LIB_ERR_MEMOBJ_WRONG_OFFSET;
182     }
183
184     // Allocate
185     struct memobj_frame_list *new = slab_alloc(&anon->frame_slab);
186     if (!new) { // Grow
187         void *buf;
188         err = vspace_pinned_alloc(&buf, FRAME_LIST);
189         if (err_is_fail(err)) {
190             return err_push(err, LIB_ERR_VSPACE_PINNED_ALLOC);
191         }
192         slab_grow(&anon->frame_slab, buf,
193                   VSPACE_PINNED_UNIT * sizeof(struct memobj_frame_list));
194         new = slab_alloc(&anon->frame_slab);
195         if (!new) {
196             return LIB_ERR_SLAB_ALLOC_FAIL;
197         }
198     }
199     new->offset = offset;
200     new->frame  = frame;
201     new->size   = size;
202
203     // Insert in order
204     struct memobj_frame_list *walk = anon->frame_list;
205     struct memobj_frame_list *prev = NULL;
206     while(walk) {
207         if (new->offset < walk->offset) {
208             if ((prev != NULL && new->offset < prev->offset + prev->size)
209                 || new->offset + new->size > walk->offset) {
210                 slab_free(&anon->frame_slab, new);
211                 return LIB_ERR_MEMOBJ_DUPLICATE_FILL;
212             }
213             new->next  = walk;
214             if (prev != NULL) {
215                 prev->next = new;
216             } else {
217                 assert(walk == anon->frame_list);
218                 anon->frame_list = new;
219             }
220             return SYS_ERR_OK;
221         }
222         prev = walk;
223         walk = walk->next;
224     }
225     if (prev != NULL) {
226         if (new->offset < prev->offset + prev->size) {
227             slab_free(&anon->frame_slab, new);
228             return LIB_ERR_MEMOBJ_DUPLICATE_FILL;
229         }
230         prev->next = new;
231         new->next = NULL;
232     } else {
233         assert(anon->frame_list == NULL);
234         anon->frame_list = new;
235         new->next = NULL;
236     }
237     return SYS_ERR_OK;
238 }
239
240 /**
241  * \brief Unmap/remove one frame from the end of the memobj
242  *
243  * \param memobj     The memory object
244  * \param offset     The offset from which to remove a frame from
245  * \param ret_frame  Pointer to return the removed frame
246  *
247  * This will try to remove one frame at an offset greater than the one
248  * specified. Call this function again and again till it returns the
249  * LIB_ERR_MEMOBJ_UNFILL_TOO_HIGH_OFFSET error to get all frames.
250  */
251 static errval_t unfill(struct memobj *memobj, genvaddr_t offset,
252                        struct capref *ret_frame, genvaddr_t *ret_offset)
253 {
254     errval_t err;
255     struct memobj_anon *anon = (struct memobj_anon*)memobj;
256
257     // Walk the ordered list of frames to find one right frame
258     struct memobj_frame_list *fwalk = anon->frame_list;
259     struct memobj_frame_list *fprev = NULL;
260     while (fwalk) {
261         if (fwalk->offset < offset) {
262             fprev = fwalk;
263             fwalk = fwalk->next;
264             continue;
265         }
266         goto cont;
267     }
268
269     // The specified offset is too high.
270     return LIB_ERR_MEMOBJ_UNFILL_TOO_HIGH_OFFSET;
271
272  cont:
273
274     { // Unmap the frame from all vregions
275         struct vregion_list *vwalk = anon->vregion_list;
276         while (vwalk) {
277             struct vspace *vspace = vregion_get_vspace(vwalk->region);
278             struct pmap *pmap     = vspace_get_pmap(vspace);
279             genvaddr_t vregion_base  = vregion_get_base_addr(vwalk->region);
280             size_t retsize;
281
282             assert((vregion_base + fwalk->offset) % BASE_PAGE_SIZE == 0);
283             err = pmap->f.unmap(pmap, vregion_base + fwalk->offset, fwalk->size,
284                                 &retsize);
285             if (err_is_fail(err)) {
286                 return err_push(err, LIB_ERR_PMAP_UNMAP);
287             }
288             assert(retsize == fwalk->size);
289             vwalk = vwalk->next;
290         }
291     }
292
293     // Return the frame
294     *ret_offset = fwalk->offset;
295     *ret_frame = fwalk->frame;
296     if (fprev) {
297         fprev->next = fwalk->next;
298     } else {
299         anon->frame_list = fwalk->next;
300     }
301     slab_free(&anon->frame_slab, fwalk);
302     return SYS_ERR_OK;
303 }
304
305 /**
306  * \brief Page fault handler
307  *
308  * \param memobj  The memory object
309  * \param region  The associated vregion
310  * \param offset  Offset into memory object of the page fault
311  * \param type    The fault type
312  *
313  * Locates the frame for the offset and maps it in.
314  * Relies on fill inserting frames in order.
315  */
316 static errval_t pagefault(struct memobj *memobj, struct vregion *vregion,
317                           genvaddr_t offset, vm_fault_type_t type)
318 {
319     errval_t err;
320     struct memobj_anon *anon = (struct memobj_anon*)memobj;
321
322     // Walk the ordered list for the frame and map it in
323     struct memobj_frame_list *walk = anon->frame_list;
324     while (walk) {
325         if (offset >= walk->offset && offset < walk->offset + walk->size) {
326             struct vspace *vspace = vregion_get_vspace(vregion);
327             struct pmap *pmap     = vspace_get_pmap(vspace);
328             genvaddr_t base          = vregion_get_base_addr(vregion);
329             genvaddr_t vregion_off   = vregion_get_offset(vregion);
330             vregion_flags_t flags = vregion_get_flags(vregion);
331             err = pmap->f.map(pmap, base + vregion_off + walk->offset,
332                               walk->frame, 0, walk->size, flags, NULL, NULL);
333             if (err_is_fail(err)) {
334                 return err_push(err, LIB_ERR_PMAP_MAP);
335             }
336             return SYS_ERR_OK;
337         }
338         walk = walk->next;
339     }
340
341     return LIB_ERR_MEMOBJ_WRONG_OFFSET;
342 }
343
344 /**
345  * \brief Free up some pages by placing them in the backing storage
346  *
347  * \param memobj      The memory object
348  * \param size        The amount of space to free up
349  * \param frames      An array of capref frames to return the freed pages
350  * \param num_frames  The number of frames returned
351  *
352  * This will affect all the vregions that are associated with the object
353  */
354 static errval_t pager_free(struct memobj *memobj, size_t size,
355                            struct capref *frames, size_t num_frames)
356 {
357     USER_PANIC("NYI");
358 }
359
360 /**
361  * \brief Initialize
362  *
363  * \param memobj  The memory object
364  * \param size    Size of the memory region
365  * \param flags   Memory object specific flags
366  *
367  * This object handles multiple frames.
368  * The frames are mapped in on demand.
369  */
370 errval_t memobj_create_anon(struct memobj_anon *anon, size_t size,
371                             memobj_flags_t flags)
372 {
373     struct memobj *memobj = &anon->m;
374
375     /* Generic portion */
376     memobj->f.map_region   = map_region;
377     memobj->f.unmap_region = unmap_region;
378     memobj->f.protect      = protect;
379     memobj->f.pin          = pin;
380     memobj->f.unpin        = unpin;
381     memobj->f.fill         = fill;
382     memobj->f.unfill       = unfill;
383     memobj->f.pagefault    = pagefault;
384     memobj->f.pager_free   = pager_free;
385
386     memobj->size  = size;
387     memobj->flags = flags;
388
389     memobj->type = ANONYMOUS;
390
391     /* anon specific portion */
392     slab_init(&anon->vregion_slab, sizeof(struct vregion_list), NULL);
393     slab_init(&anon->frame_slab, sizeof(struct memobj_frame_list), NULL);
394
395     anon->vregion_list = NULL;
396     anon->frame_list = NULL;
397     return SYS_ERR_OK;
398 }
399
400 /**
401  * \brief Destroy the object
402  *
403  * \bug NYI
404  */
405 errval_t memobj_destroy_anon(struct memobj *memobj)
406 {
407     return SYS_ERR_OK;
408 }