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