Annotation of prex-old/sys/mem/vm.c, Revision 1.1
1.1 ! nbrk 1: /*-
! 2: * Copyright (c) 2005-2007, 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.c - virtual memory manager
! 32: */
! 33:
! 34: /*
! 35: * A task owns its private virtual address space. All threads in
! 36: * a task share one same memory space.
! 37: * When new task is made, the address mapping of the parent task
! 38: * is copied to child task's. In this time, the read-only space
! 39: * is shared with old map.
! 40: *
! 41: * Since this kernel does not do page out to the physical storage,
! 42: * it is guaranteed that the allocated memory is always continuing
! 43: * and existing. Thereby, a kernel and drivers can be constructed
! 44: * very simply.
! 45: */
! 46:
! 47: #include <kernel.h>
! 48: #include <kmem.h>
! 49: #include <thread.h>
! 50: #include <page.h>
! 51: #include <task.h>
! 52: #include <sched.h>
! 53: #include <vm.h>
! 54:
! 55: #ifdef CONFIG_VMTRACE
! 56: static void vm_error(const char *, int);
! 57: #define LOG(x) printk x
! 58: #define CHK(fn,x) do { if (x) vm_error(fn, x); } while (0)
! 59: #else
! 60: #define LOG(x)
! 61: #define CHK(fn,x)
! 62: #endif
! 63:
! 64: /* forward declarations */
! 65: static struct region *region_create(struct region *, u_long, size_t);
! 66: static void region_delete(struct region *, struct region *);
! 67: static struct region *region_find(struct region *, u_long, size_t);
! 68: static struct region *region_alloc(struct region *, size_t);
! 69: static void region_free(struct region *, struct region *);
! 70: static struct region *region_split(struct region *, struct region *,
! 71: u_long, size_t);
! 72: static void region_init(struct region *);
! 73: static int do_allocate(vm_map_t, void **, size_t, int);
! 74: static int do_free(vm_map_t, void *);
! 75: static int do_attribute(vm_map_t, void *, int);
! 76: static int do_map(vm_map_t, void *, size_t, void **);
! 77: static vm_map_t do_fork(vm_map_t);
! 78:
! 79: /* vm mapping for kernel task */
! 80: static struct vm_map kern_map;
! 81:
! 82: /**
! 83: * vm_allocate - allocate zero-filled memory for specified address
! 84: * @task: task id to allocate memory
! 85: * @addr: required address. set an allocated address in return.
! 86: * @size: allocation size
! 87: * @anywhere: if it is true, the "addr" argument will be ignored.
! 88: * In this case, the address of free space will be found
! 89: * automatically.
! 90: *
! 91: * The allocated area has writable, user-access attribute by default.
! 92: * The "addr" and "size" argument will be adjusted to page boundary.
! 93: */
! 94: int
! 95: vm_allocate(task_t task, void **addr, size_t size, int anywhere)
! 96: {
! 97: int err;
! 98: void *uaddr;
! 99:
! 100: LOG(("vm_aloc: task=%s addr=%x size=%x anywhere=%d\n",
! 101: task->name ? task->name : "no name", *addr, size, anywhere));
! 102:
! 103: sched_lock();
! 104:
! 105: if (!task_valid(task)) {
! 106: err = ESRCH;
! 107: } else if (task != cur_task() && !task_capable(CAP_MEMORY)) {
! 108: err = EPERM;
! 109: } else if (umem_copyin(addr, &uaddr, sizeof(void *))) {
! 110: err = EFAULT;
! 111: } else if (anywhere == 0 && !user_area(*addr)) {
! 112: err = EACCES;
! 113: } else {
! 114: err = do_allocate(task->map, &uaddr, size, anywhere);
! 115: if (err == 0) {
! 116: if (umem_copyout(&uaddr, addr, sizeof(void *)))
! 117: err = EFAULT;
! 118: }
! 119: }
! 120: sched_unlock();
! 121: CHK("vm_allocate", err);
! 122: return err;
! 123: }
! 124:
! 125: static int
! 126: do_allocate(vm_map_t map, void **addr, size_t size, int anywhere)
! 127: {
! 128: struct region *reg;
! 129: u_long start, end, phys;
! 130:
! 131: if (size == 0)
! 132: return EINVAL;
! 133:
! 134: /*
! 135: * Allocate region
! 136: */
! 137: if (anywhere) {
! 138: size = (size_t)PAGE_ALIGN(size);
! 139: if ((reg = region_alloc(&map->head, size)) == NULL)
! 140: return ENOMEM;
! 141: } else {
! 142: start = PAGE_TRUNC(*addr);
! 143: end = PAGE_ALIGN(start + size);
! 144: size = (size_t)(end - start);
! 145:
! 146: reg = region_find(&map->head, start, size);
! 147: if (reg == NULL || !(reg->flags & REG_FREE))
! 148: return EINVAL;
! 149:
! 150: reg = region_split(&map->head, reg, start, size);
! 151: if (reg == NULL)
! 152: return ENOMEM;
! 153: }
! 154:
! 155: reg->flags = REG_READ | REG_WRITE;
! 156:
! 157: /*
! 158: * Allocate physical pages, and map them into virtual address
! 159: */
! 160: if ((phys = (u_long)page_alloc(size)) == 0)
! 161: goto err1;
! 162:
! 163: if (mmu_map(map->pgd, (void *)phys, (void *)reg->addr,
! 164: size, PG_WRITE))
! 165: goto err2;
! 166:
! 167: reg->phys = phys;
! 168:
! 169: /* Zero fill */
! 170: memset(phys_to_virt(phys), 0, reg->size);
! 171: *addr = (void *)reg->addr;
! 172: return 0;
! 173:
! 174: err2:
! 175: page_free((void *)phys, size);
! 176: err1:
! 177: region_free(&map->head, reg);
! 178: return ENOMEM;
! 179: }
! 180:
! 181: /*
! 182: * Deallocate memory region for specified address.
! 183: *
! 184: * The "addr" argument points to a memory region previously
! 185: * allocated through a call to vm_allocate() or vm_map(). The number
! 186: * of bytes freed is the number of bytes of the allocated region.
! 187: * If one of the region of previous and next are free, it combines
! 188: * with them, and larger free region is created.
! 189: */
! 190: int
! 191: vm_free(task_t task, void *addr)
! 192: {
! 193: int err;
! 194:
! 195: LOG(("vm_free: task=%s addr=%x\n",
! 196: task->name ? task->name : "no name", addr));
! 197:
! 198: sched_lock();
! 199: if (!task_valid(task)) {
! 200: err = ESRCH;
! 201: } else if (task != cur_task() && !task_capable(CAP_MEMORY)) {
! 202: err = EPERM;
! 203: } else if (!user_area(addr)) {
! 204: err = EFAULT;
! 205: } else {
! 206: err = do_free(task->map, addr);
! 207: }
! 208: sched_unlock();
! 209: CHK("vm_free", err);
! 210: return err;
! 211: }
! 212:
! 213: static int
! 214: do_free(vm_map_t map, void *addr)
! 215: {
! 216: struct region *reg;
! 217:
! 218: addr = (void *)PAGE_TRUNC(addr);
! 219:
! 220: /*
! 221: * Find the target region.
! 222: */
! 223: reg = region_find(&map->head, (u_long)addr, 1);
! 224: if (reg == NULL || reg->addr != (u_long)addr ||
! 225: (reg->flags & REG_FREE))
! 226: return EINVAL;
! 227:
! 228: /*
! 229: * Unmap pages of the region.
! 230: */
! 231: mmu_map(map->pgd, (void *)reg->phys, (void *)reg->addr,
! 232: reg->size, PG_UNMAP);
! 233:
! 234: /*
! 235: * Free pages if it is not shared and mapped.
! 236: */
! 237: if (!(reg->flags & REG_SHARED) && !(reg->flags & REG_MAPPED))
! 238: page_free((void *)reg->phys, reg->size);
! 239:
! 240: region_free(&map->head, reg);
! 241: return 0;
! 242: }
! 243:
! 244: /*
! 245: * Change attribute of specified virtual address.
! 246: *
! 247: * The "addr" argument points to a memory region previously allocated
! 248: * through a call to vm_allocate(). The attribute type can be chosen
! 249: * a combination of VMA_READ, VMA_WRITE.
! 250: * Note: VMA_EXEC is not supported, yet.
! 251: */
! 252: int
! 253: vm_attribute(task_t task, void *addr, int attr)
! 254: {
! 255: int err;
! 256:
! 257: LOG(("vm_attribute: task=%s addr=%x attr=%x\n",
! 258: task->name ? task->name : "no name", addr, attr));
! 259:
! 260: sched_lock();
! 261: if (attr == 0 || attr & ~(VMA_READ | VMA_WRITE)) {
! 262: err = EINVAL;
! 263: } else if (!task_valid(task)) {
! 264: err = ESRCH;
! 265: } else if (task != cur_task() && !task_capable(CAP_MEMORY)) {
! 266: err = EPERM;
! 267: } else if (!user_area(addr)) {
! 268: err = EFAULT;
! 269: } else {
! 270: err = do_attribute(task->map, addr, attr);
! 271: }
! 272: sched_unlock();
! 273: CHK("vm_attribute", err);
! 274: return err;
! 275: }
! 276:
! 277: static int
! 278: do_attribute(vm_map_t map, void *addr, int attr)
! 279: {
! 280: struct region *reg;
! 281: int new_flags = 0;
! 282: u_long old_addr, new_addr = 0;
! 283: int map_type;
! 284:
! 285: addr = (void *)PAGE_TRUNC(addr);
! 286:
! 287: /*
! 288: * Find the target region.
! 289: */
! 290: reg = region_find(&map->head, (u_long)addr, 1);
! 291: if (reg == NULL || reg->addr != (u_long)addr ||
! 292: (reg->flags & REG_FREE)) {
! 293: return EINVAL; /* not allocated */
! 294: }
! 295: /*
! 296: * The attribute of the mapped region can not be changed.
! 297: */
! 298: if (reg->flags & REG_MAPPED)
! 299: return EINVAL;
! 300:
! 301: /*
! 302: * Check new and old flag.
! 303: */
! 304: if (reg->flags & REG_WRITE) {
! 305: if (!(attr & VMA_WRITE))
! 306: new_flags = REG_READ;
! 307: } else {
! 308: if (attr & VMA_WRITE)
! 309: new_flags = REG_READ | REG_WRITE;
! 310: }
! 311: if (new_flags == 0)
! 312: return 0; /* same attribute */
! 313:
! 314: map_type = (new_flags & REG_WRITE) ? PG_WRITE : PG_READ;
! 315:
! 316: /*
! 317: * If it is shared region, duplicate it.
! 318: */
! 319: if (reg->flags & REG_SHARED) {
! 320:
! 321: old_addr = reg->phys;
! 322:
! 323: /* Allocate new physical page. */
! 324: if ((new_addr = (u_long)page_alloc(reg->size)) == 0)
! 325: return ENOMEM;
! 326:
! 327: /* Copy source page */
! 328: memcpy(phys_to_virt(new_addr), phys_to_virt(old_addr),
! 329: reg->size);
! 330:
! 331: /* Map new region */
! 332: if (mmu_map(map->pgd, (void *)new_addr, (void *)reg->addr,
! 333: reg->size, map_type)) {
! 334: page_free((void *)new_addr, reg->size);
! 335: return ENOMEM;
! 336: }
! 337: reg->phys = new_addr;
! 338:
! 339: /* Unlink from shared list */
! 340: reg->sh_prev->sh_next = reg->sh_next;
! 341: reg->sh_next->sh_prev = reg->sh_prev;
! 342: if (reg->sh_prev == reg->sh_next)
! 343: reg->sh_prev->flags &= ~REG_SHARED;
! 344: reg->sh_next = reg->sh_prev = reg;
! 345: } else {
! 346: if (mmu_map(map->pgd, (void *)reg->phys, (void *)reg->addr,
! 347: reg->size, map_type))
! 348: return ENOMEM;
! 349: }
! 350: reg->flags = new_flags;
! 351: return 0;
! 352: }
! 353:
! 354: /**
! 355: * vm_map - map another task's memory to current task.
! 356: * @target: memory owner
! 357: * @addr: target address
! 358: * @size: map size
! 359: * @alloc: map address returned
! 360: *
! 361: * Note: This routine does not support mapping to the specific address.
! 362: */
! 363: int
! 364: vm_map(task_t target, void *addr, size_t size, void **alloc)
! 365: {
! 366: int err;
! 367:
! 368: LOG(("vm_map : task=%s addr=%x size=%x\n",
! 369: target->name ? target->name : "no name", addr, size));
! 370:
! 371: sched_lock();
! 372: if (!task_valid(target)) {
! 373: err = ESRCH;
! 374: } else if (target == cur_task()) {
! 375: err = EINVAL;
! 376: } else if (!task_capable(CAP_MEMORY)) {
! 377: err = EPERM;
! 378: } else if (!user_area(addr)) {
! 379: err = EFAULT;
! 380: } else {
! 381: err = do_map(target->map, addr, size, alloc);
! 382: }
! 383: sched_unlock();
! 384: CHK("vm_map", err);
! 385: return err;
! 386: }
! 387:
! 388: static int
! 389: do_map(vm_map_t map, void *addr, size_t size, void **alloc)
! 390: {
! 391: vm_map_t curmap;
! 392: u_long start, end, offset, phys;
! 393: struct region *reg, *cur, *tgt;
! 394: int map_type;
! 395: void *tmp;
! 396:
! 397: if (size == 0)
! 398: return EINVAL;
! 399:
! 400: /* check fault */
! 401: tmp = NULL;
! 402: if (umem_copyout(&tmp, alloc, sizeof(void *)))
! 403: return EFAULT;
! 404:
! 405: start = PAGE_TRUNC(addr);
! 406: end = PAGE_ALIGN((u_long)addr + size);
! 407: size = (size_t)(end - start);
! 408: offset = (u_long)addr - start;
! 409:
! 410: /*
! 411: * Find the region that includes target address
! 412: */
! 413: reg = region_find(&map->head, start, size);
! 414: if (reg == NULL || (reg->flags & REG_FREE))
! 415: return EINVAL; /* not allocated */
! 416: tgt = reg;
! 417:
! 418: /*
! 419: * Find the free region in current task
! 420: */
! 421: curmap = cur_task()->map;
! 422: if ((reg = region_alloc(&curmap->head, size)) == NULL)
! 423: return ENOMEM;
! 424: cur = reg;
! 425:
! 426: /*
! 427: * Try to map into current memory
! 428: */
! 429: if (tgt->flags & REG_WRITE)
! 430: map_type = PG_WRITE;
! 431: else
! 432: map_type = PG_READ;
! 433:
! 434: phys = tgt->phys + (start - tgt->addr);
! 435: if (mmu_map(curmap->pgd, (void *)phys, (void *)cur->addr,
! 436: size, map_type)) {
! 437: region_free(&curmap->head, reg);
! 438: return ENOMEM;
! 439: }
! 440:
! 441: cur->flags = tgt->flags | REG_MAPPED;
! 442: cur->phys = phys;
! 443:
! 444: tmp = (void *)((u_long)cur->addr + offset);
! 445: umem_copyout(&tmp, alloc, sizeof(void *));
! 446: return 0;
! 447: }
! 448:
! 449: /*
! 450: * Create new virtual memory space.
! 451: * No memory is inherited.
! 452: *
! 453: * Must be called with scheduler locked.
! 454: */
! 455: vm_map_t
! 456: vm_create(void)
! 457: {
! 458: vm_map_t map;
! 459:
! 460: /* Allocate new map structure */
! 461: if ((map = kmem_alloc(sizeof(struct vm_map))) == NULL)
! 462: return NULL;
! 463:
! 464: map->ref_count = 1;
! 465:
! 466: /* Allocate new page directory */
! 467: if ((map->pgd = mmu_newmap()) == NULL) {
! 468: kmem_free(map);
! 469: return NULL;
! 470: }
! 471: region_init(&map->head);
! 472: return map;
! 473: }
! 474:
! 475: /*
! 476: * Terminate specified virtual memory space.
! 477: * This is called when task is terminated.
! 478: */
! 479: void
! 480: vm_terminate(vm_map_t map)
! 481: {
! 482: struct region *reg, *tmp;
! 483:
! 484: if (--map->ref_count >= 1)
! 485: return;
! 486:
! 487: sched_lock();
! 488: reg = &map->head;
! 489: do {
! 490: if (reg->flags != REG_FREE) {
! 491: /* Unmap region */
! 492: mmu_map(map->pgd, (void *)reg->phys,
! 493: (void *)reg->addr, reg->size, PG_UNMAP);
! 494:
! 495: /* Free region if it is not shared and mapped */
! 496: if (!(reg->flags & REG_SHARED) &&
! 497: !(reg->flags & REG_MAPPED)) {
! 498: page_free((void *)reg->phys, reg->size);
! 499: }
! 500: }
! 501: tmp = reg;
! 502: reg = reg->next;
! 503: region_delete(&map->head, tmp);
! 504: } while (reg != &map->head);
! 505:
! 506: mmu_delmap(map->pgd);
! 507: kmem_free(map);
! 508: sched_unlock();
! 509: }
! 510:
! 511: /*
! 512: * Duplicate specified virtual memory space.
! 513: * This is called when new task is created.
! 514: *
! 515: * Returns new map id, NULL if it fails.
! 516: *
! 517: * All regions of original memory map are copied to new memory map.
! 518: * If the region is read-only, executable, or shared region, it is
! 519: * no need to copy. These regions are physically shared with the
! 520: * original map.
! 521: */
! 522: vm_map_t
! 523: vm_fork(vm_map_t org_map)
! 524: {
! 525: vm_map_t new_map;
! 526:
! 527: sched_lock();
! 528: new_map = do_fork(org_map);
! 529: sched_unlock();
! 530: return new_map;
! 531: }
! 532:
! 533: static vm_map_t
! 534: do_fork(vm_map_t org_map)
! 535: {
! 536: vm_map_t new_map;
! 537: struct region *tmp, *src, *dest;
! 538: int map_type;
! 539:
! 540: if ((new_map = vm_create()) == NULL)
! 541: return NULL;
! 542: /*
! 543: * Copy all regions
! 544: */
! 545: tmp = &new_map->head;
! 546: src = &org_map->head;
! 547:
! 548: /*
! 549: * Copy top region
! 550: */
! 551: *tmp = *src;
! 552: tmp->next = tmp->prev = tmp;
! 553:
! 554: if (src == src->next) /* Blank memory ? */
! 555: return new_map;
! 556:
! 557: do {
! 558: ASSERT(src != NULL);
! 559: ASSERT(src->next != NULL);
! 560:
! 561: if (src == &org_map->head) {
! 562: dest = tmp;
! 563: } else {
! 564: /* Create new region struct */
! 565: dest = kmem_alloc(sizeof(struct region));
! 566: if (dest == NULL)
! 567: return NULL;
! 568:
! 569: *dest = *src; /* memcpy */
! 570:
! 571: dest->prev = tmp;
! 572: dest->next = tmp->next;
! 573: tmp->next->prev = dest;
! 574: tmp->next = dest;
! 575: tmp = dest;
! 576: }
! 577: /* Skip free region */
! 578: if (src->flags == REG_FREE) {
! 579: src = src->next;
! 580: continue;
! 581: }
! 582: /* Check if the region can be shared */
! 583: if (!(src->flags & REG_WRITE) && !(src->flags & REG_MAPPED))
! 584: dest->flags |= REG_SHARED;
! 585:
! 586: if (!(dest->flags & REG_SHARED)) {
! 587: /* Allocate new physical page. */
! 588: dest->phys = (u_long)page_alloc(src->size);
! 589: if (dest->phys == 0)
! 590: return NULL;
! 591:
! 592: /* Copy source page */
! 593: memcpy(phys_to_virt(dest->phys),
! 594: phys_to_virt(src->phys), src->size);
! 595: }
! 596: /* Map the region to virtual address */
! 597: if (dest->flags & REG_WRITE)
! 598: map_type = PG_WRITE;
! 599: else
! 600: map_type = PG_READ;
! 601:
! 602: if (mmu_map(new_map->pgd, (void *)dest->phys,
! 603: (void *)dest->addr, dest->size, map_type))
! 604: return NULL;
! 605:
! 606: src = src->next;
! 607: } while (src != &org_map->head);
! 608:
! 609: /*
! 610: * No error. Now, link all shared regions
! 611: */
! 612: dest = &new_map->head;
! 613: src = &org_map->head;
! 614: do {
! 615: if (dest->flags & REG_SHARED) {
! 616: src->flags |= REG_SHARED;
! 617: dest->sh_prev = src;
! 618: dest->sh_next = src->sh_next;
! 619: src->sh_next->sh_prev = dest;
! 620: src->sh_next = dest;
! 621: }
! 622: dest = dest->next;
! 623: src = src->next;
! 624: } while (src != &org_map->head);
! 625: return new_map;
! 626: }
! 627:
! 628: /*
! 629: * SWitch VM mapping.
! 630: *
! 631: * Since a kernel task does not have user mode memory image, we
! 632: * don't have to setup the page directory for it. Thus, an idle
! 633: * thread and interrupt threads can be switched quickly.
! 634: */
! 635: void
! 636: vm_switch(vm_map_t map)
! 637: {
! 638:
! 639: if (map != &kern_map)
! 640: mmu_switch(map->pgd);
! 641: }
! 642:
! 643: /*
! 644: * Increment reference count of VM mapping.
! 645: */
! 646: int
! 647: vm_reference(vm_map_t map)
! 648: {
! 649:
! 650: map->ref_count++;
! 651: return 0;
! 652: }
! 653:
! 654: /*
! 655: * Load task image for boot task.
! 656: * Return 0 on success, -1 on failure.
! 657: */
! 658: int
! 659: vm_load(vm_map_t map, struct module *m, void **stack)
! 660: {
! 661: u_long src;
! 662: void *text, *data;
! 663:
! 664: printk("Loading task: %s\n", m->name);
! 665:
! 666: /*
! 667: * We have to switch VM mapping to touch the virtual
! 668: * memory space of a target task without page fault.
! 669: */
! 670: vm_switch(map);
! 671:
! 672: src = (u_long)phys_to_virt(m->phys);
! 673: text = (void *)m->text;
! 674: data = (void *)m->data;
! 675:
! 676: /*
! 677: * Create text segment
! 678: */
! 679: if (do_allocate(map, &text, m->textsz, 0))
! 680: return -1;
! 681: memcpy(text, (void *)src, m->textsz);
! 682: if (do_attribute(map, text, VMA_READ))
! 683: return -1;
! 684:
! 685: /*
! 686: * Create data & BSS segment
! 687: */
! 688: if (m->datasz + m->bsssz != 0) {
! 689: if (do_allocate(map, &data, m->datasz + m->bsssz, 0))
! 690: return -1;
! 691: src = src + (m->data - m->text);
! 692: memcpy(data, (void *)src, m->datasz);
! 693: }
! 694: /*
! 695: * Create stack
! 696: */
! 697: *stack = (void *)(USER_MAX - USTACK_SIZE);
! 698: if (do_allocate(map, stack, USTACK_SIZE, 0))
! 699: return -1;
! 700:
! 701: /* Free original pages */
! 702: page_free((void *)m->phys, m->size);
! 703: return 0;
! 704: }
! 705:
! 706: /*
! 707: * Translate virtual address of current task to physical address.
! 708: * Returns physical address on success, or NULL if no mapped memory.
! 709: */
! 710: void *
! 711: vm_translate(void *addr, size_t size)
! 712: {
! 713:
! 714: return mmu_extract(cur_task()->map->pgd, addr, size);
! 715: }
! 716:
! 717: /*
! 718: * Check if specified access can be allowed.
! 719: * return 0 on success, or EFAULT on failure.
! 720: */
! 721: int
! 722: vm_access(void *addr, size_t size, int type)
! 723: {
! 724: u_long pg, end;
! 725: int err;
! 726: char tmp;
! 727:
! 728: ASSERT(size);
! 729: pg = PAGE_TRUNC(addr);
! 730: end = PAGE_TRUNC((u_long)addr + size - 1);
! 731: do {
! 732: if ((err = umem_copyin((void *)pg, &tmp, 1)))
! 733: return EFAULT;
! 734: if (type == VMA_WRITE) {
! 735: if ((err = umem_copyout(&tmp, (void *)pg, 1)))
! 736: return EFAULT;
! 737: }
! 738: pg += PAGE_SIZE;
! 739: } while (pg <= end);
! 740: return 0;
! 741: }
! 742:
! 743: /*
! 744: * Initialize region
! 745: */
! 746: static void
! 747: region_init(struct region *reg)
! 748: {
! 749:
! 750: reg->next = reg->prev = reg;
! 751: reg->sh_next = reg->sh_prev = reg;
! 752: reg->addr = PAGE_SIZE;
! 753: reg->phys = 0;
! 754: reg->size = USER_MAX - PAGE_SIZE;
! 755: reg->flags = REG_FREE;
! 756: }
! 757:
! 758: /*
! 759: * Create new free region after the specified region.
! 760: * Returns region on success, or NULL on failure.
! 761: */
! 762: static struct region *
! 763: region_create(struct region *prev, u_long addr, size_t size)
! 764: {
! 765: struct region *reg;
! 766:
! 767: if ((reg = kmem_alloc(sizeof(*reg))) == NULL)
! 768: return NULL;
! 769:
! 770: reg->addr = addr;
! 771: reg->size = size;
! 772: reg->phys = 0;
! 773: reg->flags = REG_FREE;
! 774: reg->sh_next = reg->sh_prev = reg;
! 775:
! 776: reg->next = prev->next;
! 777: reg->prev = prev;
! 778: prev->next->prev = reg;
! 779: prev->next = reg;
! 780: return reg;
! 781: }
! 782:
! 783: /*
! 784: * Delete specified region
! 785: */
! 786: static void
! 787: region_delete(struct region *head, struct region *reg)
! 788: {
! 789:
! 790: /* If it is shared region, unlink from shared list */
! 791: if (reg->flags & REG_SHARED) {
! 792: reg->sh_prev->sh_next = reg->sh_next;
! 793: reg->sh_next->sh_prev = reg->sh_prev;
! 794: if (reg->sh_prev == reg->sh_next)
! 795: reg->sh_prev->flags &= ~REG_SHARED;
! 796: }
! 797: if (head != reg)
! 798: kmem_free(reg);
! 799: }
! 800:
! 801: /*
! 802: * Find the region at the specified area.
! 803: */
! 804: static struct region *
! 805: region_find(struct region *head, u_long addr, size_t size)
! 806: {
! 807: struct region *reg;
! 808:
! 809: reg = head;
! 810: do {
! 811: if (reg->addr <= addr &&
! 812: reg->addr + reg->size >= addr + size) {
! 813: return reg;
! 814: }
! 815: reg = reg->next;
! 816: } while (reg != head);
! 817: return NULL;
! 818: }
! 819:
! 820: /*
! 821: * Allocate free region for specified size.
! 822: */
! 823: static struct region *
! 824: region_alloc(struct region *head, size_t size)
! 825: {
! 826: struct region *reg;
! 827:
! 828: reg = head;
! 829: do {
! 830: if ((reg->flags & REG_FREE) && reg->size >= size) {
! 831: if (reg->size != size) {
! 832: /* Split this region and return its head */
! 833: if (region_create(reg, reg->addr + size,
! 834: reg->size - size) == NULL)
! 835: return NULL;
! 836: }
! 837: reg->size = size;
! 838: return reg;
! 839: }
! 840: reg = reg->next;
! 841: } while (reg != head);
! 842: return NULL;
! 843: }
! 844:
! 845: /*
! 846: * Delete specified free region
! 847: */
! 848: static void
! 849: region_free(struct region *head, struct region *reg)
! 850: {
! 851: struct region *prev, *next;
! 852:
! 853: ASSERT(reg->flags != REG_FREE);
! 854:
! 855: reg->flags = REG_FREE;
! 856:
! 857: /* If it is shared region, unlink from shared list */
! 858: if (reg->flags & REG_SHARED) {
! 859: reg->sh_prev->sh_next = reg->sh_next;
! 860: reg->sh_next->sh_prev = reg->sh_prev;
! 861: if (reg->sh_prev == reg->sh_next)
! 862: reg->sh_prev->flags &= ~REG_SHARED;
! 863: }
! 864:
! 865: /* If next region is free, merge with it. */
! 866: next = reg->next;
! 867: if (next != head && (next->flags & REG_FREE)) {
! 868: reg->next = next->next;
! 869: next->next->prev = reg;
! 870: reg->size += next->size;
! 871: kmem_free(next);
! 872: }
! 873:
! 874: /* If previous region is free, merge with it. */
! 875: prev = reg->prev;
! 876: if (reg != head && (prev->flags & REG_FREE)) {
! 877: prev->next = reg->next;
! 878: reg->next->prev = prev;
! 879: prev->size += reg->size;
! 880: kmem_free(reg);
! 881: }
! 882: }
! 883:
! 884: /*
! 885: * Sprit region for the specified address/size.
! 886: */
! 887: static struct region *
! 888: region_split(struct region *head, struct region *reg, u_long addr,
! 889: size_t size)
! 890: {
! 891: struct region *prev, *next;
! 892: size_t diff;
! 893:
! 894: /*
! 895: * Check previous region to split region.
! 896: */
! 897: prev = NULL;
! 898: if (reg->addr != addr) {
! 899: prev = reg;
! 900: diff = (size_t)(addr - reg->addr);
! 901: reg = region_create(prev, addr, prev->size - diff);
! 902: if (reg == NULL)
! 903: return NULL;
! 904: prev->size = diff;
! 905: }
! 906:
! 907: /*
! 908: * Check next region to split region.
! 909: */
! 910: if (reg->size != size) {
! 911: next = region_create(reg, reg->addr + size,
! 912: reg->size - size);
! 913: if (next == NULL) {
! 914: if (prev) {
! 915: /* Undo previous region_create() */
! 916: region_free(head, reg);
! 917: }
! 918: return NULL;
! 919: }
! 920: reg->size = size;
! 921: }
! 922: reg->flags = 0;
! 923: return reg;
! 924: }
! 925:
! 926: #if defined(DEBUG) && defined(CONFIG_KDUMP)
! 927: void
! 928: vm_dump_one(task_t task)
! 929: {
! 930: vm_map_t map;
! 931: struct region *reg;
! 932: char flags[6];
! 933: u_long total = 0;
! 934:
! 935: printk("task=%x map=%x name=%s\n", task, task->map,
! 936: task->name ? task->name : "no name");
! 937: printk(" region virtual physical size flags\n");
! 938: printk(" -------- -------- -------- -------- -----\n");
! 939:
! 940: map = task->map;
! 941: reg = &map->head;
! 942: do {
! 943: if (reg->flags != REG_FREE) {
! 944: strlcpy(flags, "-----", 6);
! 945: if (reg->flags & REG_READ)
! 946: flags[0] = 'R';
! 947: if (reg->flags & REG_WRITE)
! 948: flags[1] = 'W';
! 949: if (reg->flags & REG_EXEC)
! 950: flags[2] = 'E';
! 951: if (reg->flags & REG_SHARED)
! 952: flags[3] = 'S';
! 953: if (reg->flags & REG_MAPPED)
! 954: flags[4] = 'M';
! 955:
! 956: printk(" %08x %08x %08x %8x %s\n", reg,
! 957: reg->addr, reg->phys, reg->size, flags);
! 958: total += reg->size;
! 959: }
! 960: reg = reg->next;
! 961: } while (reg != &map->head); /* Process all regions */
! 962: printk(" *total=%dK bytes\n\n", total / 1024);
! 963: }
! 964:
! 965: void
! 966: vm_dump(void)
! 967: {
! 968: list_t n;
! 969: task_t task;
! 970:
! 971: printk("\nVM dump:\n");
! 972: n = list_first(&kern_task.link);
! 973: while (n != &kern_task.link) {
! 974: task = list_entry(n, struct task, link);
! 975: vm_dump_one(task);
! 976: n = list_next(n);
! 977: }
! 978: }
! 979: #endif
! 980:
! 981: #ifdef CONFIG_VMTRACE
! 982: static void
! 983: vm_error(const char *func, int err)
! 984: {
! 985:
! 986: printk("VM error: %s returns err=%x\n", func, err);
! 987: }
! 988: #endif
! 989:
! 990: void
! 991: vm_init(void)
! 992: {
! 993: pgd_t pgd;
! 994:
! 995: /*
! 996: * Setup vm mapping for kernel task.
! 997: */
! 998: pgd = mmu_newmap();
! 999: ASSERT(pgd != NULL);
! 1000: kern_map.pgd = pgd;
! 1001: mmu_switch(pgd);
! 1002: region_init(&kern_map.head);
! 1003: kern_task.map = &kern_map;
! 1004: }
CVSweb