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