Annotation of prex/sys/mem/vm_nommu.c, Revision 1.1
1.1 ! nbrk 1: /*-
! 2: * Copyright (c) 2005-2006, Kohsuke Ohtani
! 3: * All rights reserved.
! 4: *
! 5: * Redistribution and use in source and binary forms, with or without
! 6: * modification, are permitted provided that the following conditions
! 7: * are met:
! 8: * 1. Redistributions of source code must retain the above copyright
! 9: * notice, this list of conditions and the following disclaimer.
! 10: * 2. Redistributions in binary form must reproduce the above copyright
! 11: * notice, this list of conditions and the following disclaimer in the
! 12: * documentation and/or other materials provided with the distribution.
! 13: * 3. Neither the name of the author nor the names of any co-contributors
! 14: * may be used to endorse or promote products derived from this software
! 15: * without specific prior written permission.
! 16: *
! 17: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
! 18: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 19: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 20: * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
! 21: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 22: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 23: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 24: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 25: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 26: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 27: * SUCH DAMAGE.
! 28: */
! 29:
! 30: /*
! 31: * vm_nommu.c - virtual memory alloctor for no MMU systems
! 32: */
! 33:
! 34: /*
! 35: * When the platform does not support memory management unit (MMU)
! 36: * all virtual memories are mapped to the physical memory. So, the
! 37: * memory space is shared among all tasks and kernel.
! 38: *
! 39: * Important: The lists of regions are not sorted by address.
! 40: */
! 41:
! 42: #include <kernel.h>
! 43: #include <kmem.h>
! 44: #include <thread.h>
! 45: #include <page.h>
! 46: #include <task.h>
! 47: #include <sched.h>
! 48: #include <vm.h>
! 49:
! 50: /* forward declarations */
! 51: static struct region *region_create(struct region *, void *, size_t);
! 52: static void region_delete(struct region *, struct region *);
! 53: static struct region *region_find(struct region *, void *, size_t);
! 54: static void region_free(struct region *, struct region *);
! 55: static void region_init(struct region *);
! 56: static int do_allocate(vm_map_t, void **, size_t, int);
! 57: static int do_free(vm_map_t, void *);
! 58: static int do_attribute(vm_map_t, void *, int);
! 59: static int do_map(vm_map_t, void *, size_t, void **);
! 60:
! 61:
! 62: /* vm mapping for kernel task */
! 63: static struct vm_map kern_map;
! 64:
! 65: /**
! 66: * vm_allocate - allocate zero-filled memory for specified address
! 67: *
! 68: * If "anywhere" argument is true, the "addr" argument will be
! 69: * ignored. In this case, the address of free space will be
! 70: * found automatically.
! 71: *
! 72: * The allocated area has writable, user-access attribute by
! 73: * default. The "addr" and "size" argument will be adjusted
! 74: * to page boundary.
! 75: */
! 76: int
! 77: vm_allocate(task_t task, void **addr, size_t size, int anywhere)
! 78: {
! 79: int err;
! 80: void *uaddr;
! 81:
! 82: sched_lock();
! 83:
! 84: if (!task_valid(task)) {
! 85: err = ESRCH;
! 86: goto out;
! 87: }
! 88: if (task != cur_task() && !task_capable(CAP_MEMORY)) {
! 89: err = EPERM;
! 90: goto out;
! 91: }
! 92: if (umem_copyin(addr, &uaddr, sizeof(void *))) {
! 93: err = EFAULT;
! 94: goto out;
! 95: }
! 96: if (anywhere == 0 && !user_area(*addr)) {
! 97: err = EACCES;
! 98: goto out;
! 99: }
! 100:
! 101: err = do_allocate(task->map, &uaddr, size, anywhere);
! 102: if (err == 0) {
! 103: if (umem_copyout(&uaddr, addr, sizeof(void *)))
! 104: err = EFAULT;
! 105: }
! 106: out:
! 107: sched_unlock();
! 108: return err;
! 109: }
! 110:
! 111: static int
! 112: do_allocate(vm_map_t map, void **addr, size_t size, int anywhere)
! 113: {
! 114: struct region *reg;
! 115: char *start, *end;
! 116:
! 117: if (size == 0)
! 118: return EINVAL;
! 119:
! 120: /*
! 121: * Allocate region, and reserve pages for it.
! 122: */
! 123: if (anywhere) {
! 124: size = (size_t)PAGE_ALIGN(size);
! 125: if ((start = page_alloc(size)) == 0)
! 126: return ENOMEM;
! 127: } else {
! 128: start = (char *)PAGE_TRUNC(*addr);
! 129: end = (char *)PAGE_ALIGN(start + size);
! 130: size = (size_t)(end - start);
! 131:
! 132: if (page_reserve(start, size))
! 133: return EINVAL;
! 134: }
! 135: reg = region_create(&map->head, start, size);
! 136: if (reg == NULL) {
! 137: page_free(start, size);
! 138: return ENOMEM;
! 139: }
! 140: reg->flags = REG_READ | REG_WRITE;
! 141:
! 142: /* Zero fill */
! 143: memset(start, 0, size);
! 144: *addr = reg->addr;
! 145: return 0;
! 146: }
! 147:
! 148: /*
! 149: * Deallocate memory region for specified address.
! 150: *
! 151: * The "addr" argument points to a memory region previously
! 152: * allocated through a call to vm_allocate() or vm_map(). The
! 153: * number of bytes freed is the number of bytes of the
! 154: * allocated region. If one of the region of previous and
! 155: * next are free, it combines with them, and larger free
! 156: * region is created.
! 157: */
! 158: int
! 159: vm_free(task_t task, void *addr)
! 160: {
! 161: int err;
! 162:
! 163: sched_lock();
! 164: if (!task_valid(task)) {
! 165: err = ESRCH;
! 166: goto out;
! 167: }
! 168: if (task != cur_task() && !task_capable(CAP_MEMORY)) {
! 169: err = EPERM;
! 170: goto out;
! 171: }
! 172: if (!user_area(addr)) {
! 173: err = EFAULT;
! 174: goto out;
! 175: }
! 176:
! 177: err = do_free(task->map, addr);
! 178: out:
! 179: sched_unlock();
! 180: return err;
! 181: }
! 182:
! 183: static int
! 184: do_free(vm_map_t map, void *addr)
! 185: {
! 186: struct region *reg;
! 187:
! 188: addr = (void *)PAGE_TRUNC(addr);
! 189:
! 190: /*
! 191: * Find the target region.
! 192: */
! 193: reg = region_find(&map->head, addr, 1);
! 194: if (reg == NULL || reg->addr != addr || (reg->flags & REG_FREE))
! 195: return EINVAL; /* not allocated */
! 196:
! 197: /*
! 198: * Free pages if it is not shared and mapped.
! 199: */
! 200: if (!(reg->flags & REG_SHARED) && !(reg->flags & REG_MAPPED))
! 201: page_free(reg->addr, reg->size);
! 202:
! 203: region_free(&map->head, reg);
! 204: return 0;
! 205: }
! 206:
! 207: /*
! 208: * Change attribute of specified virtual address.
! 209: *
! 210: * The "addr" argument points to a memory region previously
! 211: * allocated through a call to vm_allocate(). The attribute
! 212: * type can be chosen a combination of VMA_READ, VMA_WRITE.
! 213: * Note: VMA_EXEC is not supported, yet.
! 214: */
! 215: int
! 216: vm_attribute(task_t task, void *addr, int attr)
! 217: {
! 218: int err;
! 219:
! 220: sched_lock();
! 221: if (attr == 0 || attr & ~(VMA_READ | VMA_WRITE)) {
! 222: err = EINVAL;
! 223: goto out;
! 224: }
! 225: if (!task_valid(task)) {
! 226: err = ESRCH;
! 227: goto out;
! 228: }
! 229: if (task != cur_task() && !task_capable(CAP_MEMORY)) {
! 230: err = EPERM;
! 231: goto out;
! 232: }
! 233: if (!user_area(addr)) {
! 234: err = EFAULT;
! 235: goto out;
! 236: }
! 237:
! 238: err = do_attribute(task->map, addr, attr);
! 239: out:
! 240: sched_unlock();
! 241: return err;
! 242: }
! 243:
! 244: static int
! 245: do_attribute(vm_map_t map, void *addr, int attr)
! 246: {
! 247: struct region *reg;
! 248: int new_flags = 0;
! 249:
! 250: addr = (void *)PAGE_TRUNC(addr);
! 251:
! 252: /*
! 253: * Find the target region.
! 254: */
! 255: reg = region_find(&map->head, addr, 1);
! 256: if (reg == NULL || reg->addr != addr || (reg->flags & REG_FREE)) {
! 257: return EINVAL; /* not allocated */
! 258: }
! 259: /*
! 260: * The attribute of the mapped or shared region can not be changed.
! 261: */
! 262: if ((reg->flags & REG_MAPPED) || (reg->flags & REG_SHARED))
! 263: return EINVAL;
! 264:
! 265: /*
! 266: * Check new and old flag.
! 267: */
! 268: if (reg->flags & REG_WRITE) {
! 269: if (!(attr & VMA_WRITE))
! 270: new_flags = REG_READ;
! 271: } else {
! 272: if (attr & VMA_WRITE)
! 273: new_flags = REG_READ | REG_WRITE;
! 274: }
! 275: if (new_flags == 0)
! 276: return 0; /* same attribute */
! 277: reg->flags = new_flags;
! 278: return 0;
! 279: }
! 280:
! 281: /**
! 282: * vm_map - map another task's memory to current task.
! 283: *
! 284: * Note: This routine does not support mapping to the specific
! 285: * address.
! 286: */
! 287: int
! 288: vm_map(task_t target, void *addr, size_t size, void **alloc)
! 289: {
! 290: int err;
! 291:
! 292: sched_lock();
! 293: if (!task_valid(target)) {
! 294: err = ESRCH;
! 295: goto out;
! 296: }
! 297: if (target == cur_task()) {
! 298: err = EINVAL;
! 299: goto out;
! 300: }
! 301: if (!task_capable(CAP_MEMORY)) {
! 302: err = EPERM;
! 303: goto out;
! 304: }
! 305: if (!user_area(addr)) {
! 306: err = EFAULT;
! 307: goto out;
! 308: }
! 309:
! 310: err = do_map(target->map, addr, size, alloc);
! 311: out:
! 312: sched_unlock();
! 313: return err;
! 314: }
! 315:
! 316: static int
! 317: do_map(vm_map_t map, void *addr, size_t size, void **alloc)
! 318: {
! 319: vm_map_t curmap;
! 320: task_t self;
! 321: char *start, *end;
! 322: struct region *reg, *tgt;
! 323: void *tmp;
! 324:
! 325: if (size == 0)
! 326: return EINVAL;
! 327:
! 328: /* check fault */
! 329: tmp = NULL;
! 330: if (umem_copyout(&tmp, alloc, sizeof(void *)))
! 331: return EFAULT;
! 332:
! 333: start = (char *)PAGE_TRUNC(addr);
! 334: end = (char *)PAGE_ALIGN((char *)addr + size);
! 335: size = (size_t)(end - start);
! 336:
! 337: /*
! 338: * Find the region that includes target address
! 339: */
! 340: reg = region_find(&map->head, start, size);
! 341: if (reg == NULL || (reg->flags & REG_FREE))
! 342: return EINVAL; /* not allocated */
! 343: tgt = reg;
! 344:
! 345: /*
! 346: * Create new region to map
! 347: */
! 348: self = cur_task();
! 349: curmap = self->map;
! 350: reg = region_create(&curmap->head, start, size);
! 351: if (reg == NULL)
! 352: return ENOMEM;
! 353: reg->flags = tgt->flags | REG_MAPPED;
! 354:
! 355: umem_copyout(&addr, alloc, sizeof(void *));
! 356: return 0;
! 357: }
! 358:
! 359: /*
! 360: * Create new virtual memory space.
! 361: * No memory is inherited.
! 362: * Must be called with scheduler locked.
! 363: */
! 364: vm_map_t
! 365: vm_create(void)
! 366: {
! 367: vm_map_t map;
! 368:
! 369: /* Allocate new map structure */
! 370: if ((map = kmem_alloc(sizeof(struct vm_map))) == NULL)
! 371: return NULL;
! 372:
! 373: map->refcnt = 1;
! 374: region_init(&map->head);
! 375: return map;
! 376: }
! 377:
! 378: /*
! 379: * Terminate specified virtual memory space.
! 380: * This is called when task is terminated.
! 381: */
! 382: void
! 383: vm_terminate(vm_map_t map)
! 384: {
! 385: struct region *reg, *tmp;
! 386:
! 387: if (--map->refcnt >= 1)
! 388: return;
! 389:
! 390: sched_lock();
! 391: reg = &map->head;
! 392: do {
! 393: if (reg->flags != REG_FREE) {
! 394: /* Free region if it is not shared and mapped */
! 395: if (!(reg->flags & REG_SHARED) &&
! 396: !(reg->flags & REG_MAPPED)) {
! 397: page_free(reg->addr, reg->size);
! 398: }
! 399: }
! 400: tmp = reg;
! 401: reg = reg->next;
! 402: region_delete(&map->head, tmp);
! 403: } while (reg != &map->head);
! 404:
! 405: kmem_free(map);
! 406: sched_unlock();
! 407: }
! 408:
! 409: /*
! 410: * Duplicate specified virtual memory space.
! 411: */
! 412: vm_map_t
! 413: vm_fork(vm_map_t org_map)
! 414: {
! 415: /*
! 416: * This function is not supported with no MMU system.
! 417: */
! 418: return NULL;
! 419: }
! 420:
! 421: /*
! 422: * Switch VM mapping.
! 423: */
! 424: void
! 425: vm_switch(vm_map_t map)
! 426: {
! 427: }
! 428:
! 429: /*
! 430: * Increment reference count of VM mapping.
! 431: */
! 432: int
! 433: vm_reference(vm_map_t map)
! 434: {
! 435:
! 436: map->refcnt++;
! 437: return 0;
! 438: }
! 439:
! 440: /*
! 441: * Translate virtual address of current task to physical address.
! 442: * Returns physical address on success, or NULL if no mapped memory.
! 443: */
! 444: void *
! 445: vm_translate(void *addr, size_t size)
! 446: {
! 447:
! 448: return addr;
! 449: }
! 450:
! 451: /*
! 452: * Reserve specific area for boot tasks.
! 453: */
! 454: static int
! 455: do_reserve(vm_map_t map, void **addr, size_t size)
! 456: {
! 457: struct region *reg;
! 458: char *start, *end;
! 459:
! 460: if (size == 0)
! 461: return EINVAL;
! 462:
! 463: start = (char *)PAGE_TRUNC(*addr);
! 464: end = (char *)PAGE_ALIGN(start + size);
! 465: size = (size_t)(end - start);
! 466:
! 467: reg = region_create(&map->head, start, size);
! 468: if (reg == NULL)
! 469: return ENOMEM;
! 470: reg->flags = REG_READ | REG_WRITE;
! 471: *addr = reg->addr;
! 472: return 0;
! 473: }
! 474:
! 475: /*
! 476: * Setup task image for boot task. (NOMMU version)
! 477: * Return 0 on success, -1 on failure.
! 478: *
! 479: * Note: We assume that the task images are already copied to
! 480: * the proper address by a boot loader.
! 481: */
! 482: int
! 483: vm_load(vm_map_t map, struct module *mod, void **stack)
! 484: {
! 485: void *base;
! 486: size_t size;
! 487:
! 488: DPRINTF(("Loading task:\'%s\'\n", mod->name));
! 489:
! 490: /*
! 491: * Reserve text & data area
! 492: */
! 493: base = (void *)mod->text;
! 494: size = mod->textsz + mod->datasz + mod->bsssz;
! 495: if (do_reserve(map, &base, size))
! 496: return -1;
! 497: if (mod->bsssz != 0)
! 498: memset((void *)(mod->data + mod->datasz), 0, mod->bsssz);
! 499:
! 500: /*
! 501: * Create stack
! 502: */
! 503: if (do_allocate(map, stack, USTACK_SIZE, 1))
! 504: return -1;
! 505: return 0;
! 506: }
! 507:
! 508: /*
! 509: * Create new free region after the specified region.
! 510: * Returns region on success, or NULL on failure.
! 511: */
! 512: static struct region *
! 513: region_create(struct region *prev, void *addr, size_t size)
! 514: {
! 515: struct region *reg;
! 516:
! 517: if ((reg = kmem_alloc(sizeof(struct region))) == NULL)
! 518: return NULL;
! 519:
! 520: reg->addr = addr;
! 521: reg->size = size;
! 522: reg->flags = REG_FREE;
! 523: reg->sh_next = reg->sh_prev = reg;
! 524:
! 525: reg->next = prev->next;
! 526: reg->prev = prev;
! 527: prev->next->prev = reg;
! 528: prev->next = reg;
! 529: return reg;
! 530: }
! 531:
! 532: /*
! 533: * Delete specified region
! 534: */
! 535: static void
! 536: region_delete(struct region *head, struct region *reg)
! 537: {
! 538:
! 539: /*
! 540: * If it is shared region, unlink from shared list.
! 541: */
! 542: if (reg->flags & REG_SHARED) {
! 543: reg->sh_prev->sh_next = reg->sh_next;
! 544: reg->sh_next->sh_prev = reg->sh_prev;
! 545: if (reg->sh_prev == reg->sh_next)
! 546: reg->sh_prev->flags &= ~REG_SHARED;
! 547: }
! 548: if (head != reg)
! 549: kmem_free(reg);
! 550: }
! 551:
! 552: /*
! 553: * Find the region at the specified area.
! 554: */
! 555: static struct region *
! 556: region_find(struct region *head, void *addr, size_t size)
! 557: {
! 558: struct region *reg;
! 559:
! 560: reg = head;
! 561: do {
! 562: if (reg->addr <= addr &&
! 563: (char *)reg->addr + reg->size >= (char *)addr + size) {
! 564: return reg;
! 565: }
! 566: reg = reg->next;
! 567: } while (reg != head);
! 568: return NULL;
! 569: }
! 570:
! 571: /*
! 572: * Free specified region
! 573: */
! 574: static void
! 575: region_free(struct region *head, struct region *reg)
! 576: {
! 577: ASSERT(reg->flags != REG_FREE);
! 578:
! 579: /*
! 580: * If it is shared region, unlink from shared list.
! 581: */
! 582: if (reg->flags & REG_SHARED) {
! 583: reg->sh_prev->sh_next = reg->sh_next;
! 584: reg->sh_next->sh_prev = reg->sh_prev;
! 585: if (reg->sh_prev == reg->sh_next)
! 586: reg->sh_prev->flags &= ~REG_SHARED;
! 587: }
! 588: reg->prev->next = reg->next;
! 589: reg->next->prev = reg->prev;
! 590: kmem_free(reg);
! 591: }
! 592:
! 593: /*
! 594: * Initialize region
! 595: */
! 596: static void
! 597: region_init(struct region *reg)
! 598: {
! 599:
! 600: reg->next = reg->prev = reg;
! 601: reg->sh_next = reg->sh_prev = reg;
! 602: reg->addr = NULL;
! 603: reg->size = 0;
! 604: reg->flags = REG_FREE;
! 605: }
! 606:
! 607: #ifdef DEBUG
! 608: static void
! 609: vm_dump_one(task_t task)
! 610: {
! 611: vm_map_t map;
! 612: struct region *reg;
! 613: char flags[6];
! 614: size_t total = 0;
! 615:
! 616: printf("task=%x map=%x name=%s\n", task, task->map,
! 617: task->name ? task->name : "no name");
! 618: printf(" region virtual size flags\n");
! 619: printf(" -------- -------- -------- -----\n");
! 620:
! 621: map = task->map;
! 622: reg = &map->head;
! 623: do {
! 624: if (reg->flags != REG_FREE) {
! 625: strlcpy(flags, "-----", 6);
! 626: if (reg->flags & REG_READ)
! 627: flags[0] = 'R';
! 628: if (reg->flags & REG_WRITE)
! 629: flags[1] = 'W';
! 630: if (reg->flags & REG_EXEC)
! 631: flags[2] = 'E';
! 632: if (reg->flags & REG_SHARED)
! 633: flags[3] = 'S';
! 634: if (reg->flags & REG_MAPPED)
! 635: flags[4] = 'M';
! 636:
! 637: printf(" %08x %08x %08x %s\n", reg,
! 638: reg->addr, reg->size, flags);
! 639: if ((reg->flags & REG_MAPPED) == 0)
! 640: total += reg->size;
! 641: }
! 642: reg = reg->next;
! 643: } while (reg != &map->head); /* Process all regions */
! 644: printf(" *total=%dK bytes\n\n", total / 1024);
! 645: }
! 646:
! 647: void
! 648: vm_dump(void)
! 649: {
! 650: list_t n;
! 651: task_t task;
! 652:
! 653: printf("\nVM dump:\n");
! 654: n = list_first(&kern_task.link);
! 655: while (n != &kern_task.link) {
! 656: task = list_entry(n, struct task, link);
! 657: vm_dump_one(task);
! 658: n = list_next(n);
! 659: }
! 660: }
! 661: #endif
! 662:
! 663: void
! 664: vm_init(void)
! 665: {
! 666:
! 667: region_init(&kern_map.head);
! 668: kern_task.map = &kern_map;
! 669: }
CVSweb