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