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