[BACK]Return to vm.c CVS log [TXT][DIR] Up to [local] / prex-old / sys / mem

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