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