version 1.1.1.1, 2008/06/03 10:38:46 |
version 1.1.1.1.2.1, 2008/08/13 17:12:32 |
|
|
*/ |
*/ |
|
|
/* |
/* |
* vm.c - virtual memory manager |
* vm.c - virtual memory allocator |
*/ |
*/ |
|
|
/* |
/* |
|
|
#include <sched.h> |
#include <sched.h> |
#include <vm.h> |
#include <vm.h> |
|
|
#ifdef CONFIG_VMTRACE |
|
static void vm_error(const char *, int); |
|
#define LOG(x) printk x |
|
#define CHK(fn,x) do { if (x) vm_error(fn, x); } while (0) |
|
#else |
|
#define LOG(x) |
|
#define CHK(fn,x) |
|
#endif |
|
|
|
/* forward declarations */ |
/* forward declarations */ |
static struct region *region_create(struct region *, u_long, size_t); |
static struct region *region_create(struct region *, void *, size_t); |
static void region_delete(struct region *, struct region *); |
static void region_delete(struct region *, struct region *); |
static struct region *region_find(struct region *, u_long, size_t); |
static struct region *region_find(struct region *, void *, size_t); |
static struct region *region_alloc(struct region *, size_t); |
static struct region *region_alloc(struct region *, size_t); |
static void region_free(struct region *, struct region *); |
static void region_free(struct region *, struct region *); |
static struct region *region_split(struct region *, struct region *, |
static struct region *region_split(struct region *, struct region *, |
u_long, size_t); |
void *, size_t); |
static void region_init(struct region *); |
static void region_init(struct region *); |
static int do_allocate(vm_map_t, void **, size_t, int); |
static int do_allocate(vm_map_t, void **, size_t, int); |
static int do_free(vm_map_t, void *); |
static int do_free(vm_map_t, void *); |
static int do_attribute(vm_map_t, void *, int); |
static int do_attribute(vm_map_t, void *, int); |
static int do_map(vm_map_t, void *, size_t, void **); |
static int do_map(vm_map_t, void *, size_t, void **); |
static vm_map_t do_fork(vm_map_t); |
static vm_map_t do_fork(vm_map_t); |
|
|
|
|
/* vm mapping for kernel task */ |
/* vm mapping for kernel task */ |
static struct vm_map kern_map; |
static struct vm_map kern_map; |
|
|
/** |
/** |
* vm_allocate - allocate zero-filled memory for specified address |
* vm_allocate - allocate zero-filled memory for specified address |
* @task: task id to allocate memory |
|
* @addr: required address. set an allocated address in return. |
|
* @size: allocation size |
|
* @anywhere: if it is true, the "addr" argument will be ignored. |
|
* In this case, the address of free space will be found |
|
* automatically. |
|
* |
* |
* The allocated area has writable, user-access attribute by default. |
* If "anywhere" argument is true, the "addr" argument will be |
* The "addr" and "size" argument will be adjusted to page boundary. |
* ignored. In this case, the address of free space will be |
|
* found automatically. |
|
* |
|
* The allocated area has writable, user-access attribute by |
|
* default. The "addr" and "size" argument will be adjusted |
|
* to page boundary. |
*/ |
*/ |
int |
int |
vm_allocate(task_t task, void **addr, size_t size, int anywhere) |
vm_allocate(task_t task, void **addr, size_t size, int anywhere) |
|
|
int err; |
int err; |
void *uaddr; |
void *uaddr; |
|
|
LOG(("vm_aloc: task=%s addr=%x size=%x anywhere=%d\n", |
|
task->name ? task->name : "no name", *addr, size, anywhere)); |
|
|
|
sched_lock(); |
sched_lock(); |
|
|
if (!task_valid(task)) { |
if (!task_valid(task)) { |
err = ESRCH; |
err = ESRCH; |
} else if (task != cur_task() && !task_capable(CAP_MEMORY)) { |
goto out; |
|
} |
|
if (task != cur_task() && !task_capable(CAP_MEMORY)) { |
err = EPERM; |
err = EPERM; |
} else if (umem_copyin(addr, &uaddr, sizeof(void *))) { |
goto out; |
|
} |
|
if (umem_copyin(addr, &uaddr, sizeof(void *))) { |
err = EFAULT; |
err = EFAULT; |
} else if (anywhere == 0 && !user_area(*addr)) { |
goto out; |
|
} |
|
if (anywhere == 0 && !user_area(*addr)) { |
err = EACCES; |
err = EACCES; |
} else { |
goto out; |
err = do_allocate(task->map, &uaddr, size, anywhere); |
|
if (err == 0) { |
|
if (umem_copyout(&uaddr, addr, sizeof(void *))) |
|
err = EFAULT; |
|
} |
|
} |
} |
|
|
|
err = do_allocate(task->map, &uaddr, size, anywhere); |
|
if (err == 0) { |
|
if (umem_copyout(&uaddr, addr, sizeof(void *))) |
|
err = EFAULT; |
|
} |
|
out: |
sched_unlock(); |
sched_unlock(); |
CHK("vm_allocate", err); |
|
return err; |
return err; |
} |
} |
|
|
|
|
do_allocate(vm_map_t map, void **addr, size_t size, int anywhere) |
do_allocate(vm_map_t map, void **addr, size_t size, int anywhere) |
{ |
{ |
struct region *reg; |
struct region *reg; |
u_long start, end, phys; |
char *start, *end, *phys; |
|
|
if (size == 0) |
if (size == 0) |
return EINVAL; |
return EINVAL; |
|
|
if ((reg = region_alloc(&map->head, size)) == NULL) |
if ((reg = region_alloc(&map->head, size)) == NULL) |
return ENOMEM; |
return ENOMEM; |
} else { |
} else { |
start = PAGE_TRUNC(*addr); |
start = (char *)PAGE_TRUNC(*addr); |
end = PAGE_ALIGN(start + size); |
end = (char *)PAGE_ALIGN(start + size); |
size = (size_t)(end - start); |
size = (size_t)(end - start); |
|
|
reg = region_find(&map->head, start, size); |
reg = region_find(&map->head, start, size); |
|
|
if (reg == NULL) |
if (reg == NULL) |
return ENOMEM; |
return ENOMEM; |
} |
} |
|
|
reg->flags = REG_READ | REG_WRITE; |
reg->flags = REG_READ | REG_WRITE; |
|
|
/* |
/* |
* Allocate physical pages, and map them into virtual address |
* Allocate physical pages, and map them into virtual address |
*/ |
*/ |
if ((phys = (u_long)page_alloc(size)) == 0) |
if ((phys = page_alloc(size)) == 0) |
goto err1; |
goto err1; |
|
|
if (mmu_map(map->pgd, (void *)phys, (void *)reg->addr, |
if (mmu_map(map->pgd, phys, reg->addr, size, PG_WRITE)) |
size, PG_WRITE)) |
|
goto err2; |
goto err2; |
|
|
reg->phys = phys; |
reg->phys = phys; |
|
|
/* Zero fill */ |
/* Zero fill */ |
memset(phys_to_virt(phys), 0, reg->size); |
memset(phys_to_virt(phys), 0, reg->size); |
*addr = (void *)reg->addr; |
*addr = reg->addr; |
return 0; |
return 0; |
|
|
err2: |
err2: |
page_free((void *)phys, size); |
page_free(phys, size); |
err1: |
err1: |
region_free(&map->head, reg); |
region_free(&map->head, reg); |
return ENOMEM; |
return ENOMEM; |
|
|
* Deallocate memory region for specified address. |
* Deallocate memory region for specified address. |
* |
* |
* The "addr" argument points to a memory region previously |
* The "addr" argument points to a memory region previously |
* allocated through a call to vm_allocate() or vm_map(). The number |
* allocated through a call to vm_allocate() or vm_map(). The |
* of bytes freed is the number of bytes of the allocated region. |
* number of bytes freed is the number of bytes of the |
* If one of the region of previous and next are free, it combines |
* allocated region. If one of the region of previous and next |
* with them, and larger free region is created. |
* are free, it combines with them, and larger free region is |
|
* created. |
*/ |
*/ |
int |
int |
vm_free(task_t task, void *addr) |
vm_free(task_t task, void *addr) |
{ |
{ |
int err; |
int err; |
|
|
LOG(("vm_free: task=%s addr=%x\n", |
|
task->name ? task->name : "no name", addr)); |
|
|
|
sched_lock(); |
sched_lock(); |
if (!task_valid(task)) { |
if (!task_valid(task)) { |
err = ESRCH; |
err = ESRCH; |
} else if (task != cur_task() && !task_capable(CAP_MEMORY)) { |
goto out; |
|
} |
|
if (task != cur_task() && !task_capable(CAP_MEMORY)) { |
err = EPERM; |
err = EPERM; |
} else if (!user_area(addr)) { |
goto out; |
|
} |
|
if (!user_area(addr)) { |
err = EFAULT; |
err = EFAULT; |
} else { |
goto out; |
err = do_free(task->map, addr); |
|
} |
} |
|
|
|
err = do_free(task->map, addr); |
|
out: |
sched_unlock(); |
sched_unlock(); |
CHK("vm_free", err); |
|
return err; |
return err; |
} |
} |
|
|
|
|
/* |
/* |
* Find the target region. |
* Find the target region. |
*/ |
*/ |
reg = region_find(&map->head, (u_long)addr, 1); |
reg = region_find(&map->head, addr, 1); |
if (reg == NULL || reg->addr != (u_long)addr || |
if (reg == NULL || reg->addr != addr || (reg->flags & REG_FREE)) |
(reg->flags & REG_FREE)) |
|
return EINVAL; |
return EINVAL; |
|
|
/* |
/* |
* Unmap pages of the region. |
* Unmap pages of the region. |
*/ |
*/ |
mmu_map(map->pgd, (void *)reg->phys, (void *)reg->addr, |
mmu_map(map->pgd, reg->phys, reg->addr, reg->size, PG_UNMAP); |
reg->size, PG_UNMAP); |
|
|
|
/* |
/* |
* Free pages if it is not shared and mapped. |
* Relinquish use of the page if it is not shared and mapped. |
*/ |
*/ |
if (!(reg->flags & REG_SHARED) && !(reg->flags & REG_MAPPED)) |
if (!(reg->flags & REG_SHARED) && !(reg->flags & REG_MAPPED)) |
page_free((void *)reg->phys, reg->size); |
page_free(reg->phys, reg->size); |
|
|
region_free(&map->head, reg); |
region_free(&map->head, reg); |
return 0; |
return 0; |
|
|
/* |
/* |
* Change attribute of specified virtual address. |
* Change attribute of specified virtual address. |
* |
* |
* The "addr" argument points to a memory region previously allocated |
* The "addr" argument points to a memory region previously |
* through a call to vm_allocate(). The attribute type can be chosen |
* allocated through a call to vm_allocate(). The attribute |
* a combination of VMA_READ, VMA_WRITE. |
* type can be chosen a combination of VMA_READ, VMA_WRITE. |
* Note: VMA_EXEC is not supported, yet. |
* Note: VMA_EXEC is not supported, yet. |
*/ |
*/ |
int |
int |
|
|
{ |
{ |
int err; |
int err; |
|
|
LOG(("vm_attribute: task=%s addr=%x attr=%x\n", |
|
task->name ? task->name : "no name", addr, attr)); |
|
|
|
sched_lock(); |
sched_lock(); |
if (attr == 0 || attr & ~(VMA_READ | VMA_WRITE)) { |
if (attr == 0 || attr & ~(VMA_READ | VMA_WRITE)) { |
err = EINVAL; |
err = EINVAL; |
} else if (!task_valid(task)) { |
goto out; |
|
} |
|
if (!task_valid(task)) { |
err = ESRCH; |
err = ESRCH; |
} else if (task != cur_task() && !task_capable(CAP_MEMORY)) { |
goto out; |
|
} |
|
if (task != cur_task() && !task_capable(CAP_MEMORY)) { |
err = EPERM; |
err = EPERM; |
} else if (!user_area(addr)) { |
goto out; |
|
} |
|
if (!user_area(addr)) { |
err = EFAULT; |
err = EFAULT; |
} else { |
goto out; |
err = do_attribute(task->map, addr, attr); |
|
} |
} |
|
|
|
err = do_attribute(task->map, addr, attr); |
|
out: |
sched_unlock(); |
sched_unlock(); |
CHK("vm_attribute", err); |
|
return err; |
return err; |
} |
} |
|
|
|
|
{ |
{ |
struct region *reg; |
struct region *reg; |
int new_flags = 0; |
int new_flags = 0; |
u_long old_addr, new_addr = 0; |
void *old_addr, *new_addr = NULL; |
int map_type; |
int map_type; |
|
|
addr = (void *)PAGE_TRUNC(addr); |
addr = (void *)PAGE_TRUNC(addr); |
|
|
/* |
/* |
* Find the target region. |
* Find the target region. |
*/ |
*/ |
reg = region_find(&map->head, (u_long)addr, 1); |
reg = region_find(&map->head, addr, 1); |
if (reg == NULL || reg->addr != (u_long)addr || |
if (reg == NULL || reg->addr != addr || (reg->flags & REG_FREE)) { |
(reg->flags & REG_FREE)) { |
|
return EINVAL; /* not allocated */ |
return EINVAL; /* not allocated */ |
} |
} |
/* |
/* |
|
|
old_addr = reg->phys; |
old_addr = reg->phys; |
|
|
/* Allocate new physical page. */ |
/* Allocate new physical page. */ |
if ((new_addr = (u_long)page_alloc(reg->size)) == 0) |
if ((new_addr = page_alloc(reg->size)) == 0) |
return ENOMEM; |
return ENOMEM; |
|
|
/* Copy source page */ |
/* Copy source page */ |
|
|
reg->size); |
reg->size); |
|
|
/* Map new region */ |
/* Map new region */ |
if (mmu_map(map->pgd, (void *)new_addr, (void *)reg->addr, |
if (mmu_map(map->pgd, new_addr, reg->addr, reg->size, |
reg->size, map_type)) { |
map_type)) { |
page_free((void *)new_addr, reg->size); |
page_free(new_addr, reg->size); |
return ENOMEM; |
return ENOMEM; |
} |
} |
reg->phys = new_addr; |
reg->phys = new_addr; |
|
|
reg->sh_prev->flags &= ~REG_SHARED; |
reg->sh_prev->flags &= ~REG_SHARED; |
reg->sh_next = reg->sh_prev = reg; |
reg->sh_next = reg->sh_prev = reg; |
} else { |
} else { |
if (mmu_map(map->pgd, (void *)reg->phys, (void *)reg->addr, |
if (mmu_map(map->pgd, reg->phys, reg->addr, reg->size, |
reg->size, map_type)) |
map_type)) |
return ENOMEM; |
return ENOMEM; |
} |
} |
reg->flags = new_flags; |
reg->flags = new_flags; |
|
|
|
|
/** |
/** |
* vm_map - map another task's memory to current task. |
* vm_map - map another task's memory to current task. |
* @target: memory owner |
|
* @addr: target address |
|
* @size: map size |
|
* @alloc: map address returned |
|
* |
* |
* Note: This routine does not support mapping to the specific address. |
* Note: This routine does not support mapping to the specific address. |
*/ |
*/ |
|
|
{ |
{ |
int err; |
int err; |
|
|
LOG(("vm_map : task=%s addr=%x size=%x\n", |
|
target->name ? target->name : "no name", addr, size)); |
|
|
|
sched_lock(); |
sched_lock(); |
if (!task_valid(target)) { |
if (!task_valid(target)) { |
err = ESRCH; |
err = ESRCH; |
} else if (target == cur_task()) { |
goto out; |
|
} |
|
if (target == cur_task()) { |
err = EINVAL; |
err = EINVAL; |
} else if (!task_capable(CAP_MEMORY)) { |
goto out; |
|
} |
|
if (!task_capable(CAP_MEMORY)) { |
err = EPERM; |
err = EPERM; |
} else if (!user_area(addr)) { |
goto out; |
|
} |
|
if (!user_area(addr)) { |
err = EFAULT; |
err = EFAULT; |
} else { |
goto out; |
err = do_map(target->map, addr, size, alloc); |
|
} |
} |
|
err = do_map(target->map, addr, size, alloc); |
|
out: |
sched_unlock(); |
sched_unlock(); |
CHK("vm_map", err); |
|
return err; |
return err; |
} |
} |
|
|
|
|
do_map(vm_map_t map, void *addr, size_t size, void **alloc) |
do_map(vm_map_t map, void *addr, size_t size, void **alloc) |
{ |
{ |
vm_map_t curmap; |
vm_map_t curmap; |
u_long start, end, offset, phys; |
char *start, *end, *phys; |
|
size_t offset; |
struct region *reg, *cur, *tgt; |
struct region *reg, *cur, *tgt; |
|
task_t self; |
int map_type; |
int map_type; |
void *tmp; |
void *tmp; |
|
|
|
|
if (umem_copyout(&tmp, alloc, sizeof(void *))) |
if (umem_copyout(&tmp, alloc, sizeof(void *))) |
return EFAULT; |
return EFAULT; |
|
|
start = PAGE_TRUNC(addr); |
start = (char *)PAGE_TRUNC(addr); |
end = PAGE_ALIGN((u_long)addr + size); |
end = (char *)PAGE_ALIGN((char *)addr + size); |
size = (size_t)(end - start); |
size = (size_t)(end - start); |
offset = (u_long)addr - start; |
offset = (size_t)((char *)addr - start); |
|
|
/* |
/* |
* Find the region that includes target address |
* Find the region that includes target address |
|
|
/* |
/* |
* Find the free region in current task |
* Find the free region in current task |
*/ |
*/ |
curmap = cur_task()->map; |
self = cur_task(); |
|
curmap = self->map; |
if ((reg = region_alloc(&curmap->head, size)) == NULL) |
if ((reg = region_alloc(&curmap->head, size)) == NULL) |
return ENOMEM; |
return ENOMEM; |
cur = reg; |
cur = reg; |
|
|
else |
else |
map_type = PG_READ; |
map_type = PG_READ; |
|
|
phys = tgt->phys + (start - tgt->addr); |
phys = (char *)tgt->phys + (start - (char *)tgt->addr); |
if (mmu_map(curmap->pgd, (void *)phys, (void *)cur->addr, |
if (mmu_map(curmap->pgd, phys, cur->addr, size, map_type)) { |
size, map_type)) { |
|
region_free(&curmap->head, reg); |
region_free(&curmap->head, reg); |
return ENOMEM; |
return ENOMEM; |
} |
} |
|
|
cur->flags = tgt->flags | REG_MAPPED; |
cur->flags = tgt->flags | REG_MAPPED; |
cur->phys = phys; |
cur->phys = phys; |
|
|
tmp = (void *)((u_long)cur->addr + offset); |
tmp = (char *)cur->addr + offset; |
umem_copyout(&tmp, alloc, sizeof(void *)); |
umem_copyout(&tmp, alloc, sizeof(void *)); |
return 0; |
return 0; |
} |
} |
|
|
if ((map = kmem_alloc(sizeof(struct vm_map))) == NULL) |
if ((map = kmem_alloc(sizeof(struct vm_map))) == NULL) |
return NULL; |
return NULL; |
|
|
map->ref_count = 1; |
map->refcnt = 1; |
|
|
/* Allocate new page directory */ |
/* Allocate new page directory */ |
if ((map->pgd = mmu_newmap()) == NULL) { |
if ((map->pgd = mmu_newmap()) == NULL) { |
|
|
{ |
{ |
struct region *reg, *tmp; |
struct region *reg, *tmp; |
|
|
if (--map->ref_count >= 1) |
if (--map->refcnt >= 1) |
return; |
return; |
|
|
sched_lock(); |
sched_lock(); |
|
|
do { |
do { |
if (reg->flags != REG_FREE) { |
if (reg->flags != REG_FREE) { |
/* Unmap region */ |
/* Unmap region */ |
mmu_map(map->pgd, (void *)reg->phys, |
mmu_map(map->pgd, reg->phys, reg->addr, |
(void *)reg->addr, reg->size, PG_UNMAP); |
reg->size, PG_UNMAP); |
|
|
/* Free region if it is not shared and mapped */ |
/* Free region if it is not shared and mapped */ |
if (!(reg->flags & REG_SHARED) && |
if (!(reg->flags & REG_SHARED) && |
!(reg->flags & REG_MAPPED)) { |
!(reg->flags & REG_MAPPED)) { |
page_free((void *)reg->phys, reg->size); |
page_free(reg->phys, reg->size); |
} |
} |
} |
} |
tmp = reg; |
tmp = reg; |
|
|
tmp->next = dest; |
tmp->next = dest; |
tmp = dest; |
tmp = dest; |
} |
} |
/* Skip free region */ |
|
if (src->flags == REG_FREE) { |
if (src->flags == REG_FREE) { |
src = src->next; |
/* |
continue; |
* Skip free region |
} |
*/ |
/* Check if the region can be shared */ |
} else { |
if (!(src->flags & REG_WRITE) && !(src->flags & REG_MAPPED)) |
/* Check if the region can be shared */ |
dest->flags |= REG_SHARED; |
if (!(src->flags & REG_WRITE) && |
|
!(src->flags & REG_MAPPED)) { |
|
dest->flags |= REG_SHARED; |
|
} |
|
|
if (!(dest->flags & REG_SHARED)) { |
if (!(dest->flags & REG_SHARED)) { |
/* Allocate new physical page. */ |
/* Allocate new physical page. */ |
dest->phys = (u_long)page_alloc(src->size); |
dest->phys = page_alloc(src->size); |
if (dest->phys == 0) |
if (dest->phys == 0) |
return NULL; |
return NULL; |
|
|
/* Copy source page */ |
/* Copy source page */ |
memcpy(phys_to_virt(dest->phys), |
memcpy(phys_to_virt(dest->phys), |
phys_to_virt(src->phys), src->size); |
phys_to_virt(src->phys), src->size); |
} |
} |
/* Map the region to virtual address */ |
/* Map the region to virtual address */ |
if (dest->flags & REG_WRITE) |
if (dest->flags & REG_WRITE) |
map_type = PG_WRITE; |
map_type = PG_WRITE; |
else |
else |
map_type = PG_READ; |
map_type = PG_READ; |
|
|
if (mmu_map(new_map->pgd, (void *)dest->phys, |
if (mmu_map(new_map->pgd, dest->phys, dest->addr, |
(void *)dest->addr, dest->size, map_type)) |
dest->size, map_type)) |
return NULL; |
return NULL; |
|
} |
src = src->next; |
src = src->next; |
} while (src != &org_map->head); |
} while (src != &org_map->head); |
|
|
|
|
} |
} |
|
|
/* |
/* |
* SWitch VM mapping. |
* Switch VM mapping. |
* |
* |
* Since a kernel task does not have user mode memory image, we |
* Since a kernel task does not have user mode memory image, we |
* don't have to setup the page directory for it. Thus, an idle |
* don't have to setup the page directory for it. Thus, an idle |
|
|
vm_reference(vm_map_t map) |
vm_reference(vm_map_t map) |
{ |
{ |
|
|
map->ref_count++; |
map->refcnt++; |
return 0; |
return 0; |
} |
} |
|
|
|
|
* Return 0 on success, -1 on failure. |
* Return 0 on success, -1 on failure. |
*/ |
*/ |
int |
int |
vm_load(vm_map_t map, struct module *m, void **stack) |
vm_load(vm_map_t map, struct module *mod, void **stack) |
{ |
{ |
u_long src; |
char *src; |
void *text, *data; |
void *text, *data; |
|
|
printk("Loading task: %s\n", m->name); |
DPRINTF(("Loading task: %s\n", mod->name)); |
|
|
/* |
/* |
* We have to switch VM mapping to touch the virtual |
* We have to switch VM mapping to touch the virtual |
|
|
*/ |
*/ |
vm_switch(map); |
vm_switch(map); |
|
|
src = (u_long)phys_to_virt(m->phys); |
src = phys_to_virt(mod->phys); |
text = (void *)m->text; |
text = (void *)mod->text; |
data = (void *)m->data; |
data = (void *)mod->data; |
|
|
/* |
/* |
* Create text segment |
* Create text segment |
*/ |
*/ |
if (do_allocate(map, &text, m->textsz, 0)) |
if (do_allocate(map, &text, mod->textsz, 0)) |
return -1; |
return -1; |
memcpy(text, (void *)src, m->textsz); |
memcpy(text, src, mod->textsz); |
if (do_attribute(map, text, VMA_READ)) |
if (do_attribute(map, text, VMA_READ)) |
return -1; |
return -1; |
|
|
/* |
/* |
* Create data & BSS segment |
* Create data & BSS segment |
*/ |
*/ |
if (m->datasz + m->bsssz != 0) { |
if (mod->datasz + mod->bsssz != 0) { |
if (do_allocate(map, &data, m->datasz + m->bsssz, 0)) |
if (do_allocate(map, &data, mod->datasz + mod->bsssz, 0)) |
return -1; |
return -1; |
src = src + (m->data - m->text); |
src = src + (mod->data - mod->text); |
memcpy(data, (void *)src, m->datasz); |
memcpy(data, src, mod->datasz); |
} |
} |
/* |
/* |
* Create stack |
* Create stack |
|
|
return -1; |
return -1; |
|
|
/* Free original pages */ |
/* Free original pages */ |
page_free((void *)m->phys, m->size); |
page_free((void *)mod->phys, mod->size); |
return 0; |
return 0; |
} |
} |
|
|
|
|
void * |
void * |
vm_translate(void *addr, size_t size) |
vm_translate(void *addr, size_t size) |
{ |
{ |
|
task_t self = cur_task(); |
|
|
return mmu_extract(cur_task()->map->pgd, addr, size); |
return mmu_extract(self->map->pgd, addr, size); |
} |
} |
|
|
/* |
/* |
* Check if specified access can be allowed. |
|
* return 0 on success, or EFAULT on failure. |
|
*/ |
|
int |
|
vm_access(void *addr, size_t size, int type) |
|
{ |
|
u_long pg, end; |
|
int err; |
|
char tmp; |
|
|
|
ASSERT(size); |
|
pg = PAGE_TRUNC(addr); |
|
end = PAGE_TRUNC((u_long)addr + size - 1); |
|
do { |
|
if ((err = umem_copyin((void *)pg, &tmp, 1))) |
|
return EFAULT; |
|
if (type == VMA_WRITE) { |
|
if ((err = umem_copyout(&tmp, (void *)pg, 1))) |
|
return EFAULT; |
|
} |
|
pg += PAGE_SIZE; |
|
} while (pg <= end); |
|
return 0; |
|
} |
|
|
|
/* |
|
* Initialize region |
* Initialize region |
*/ |
*/ |
static void |
static void |
|
|
|
|
reg->next = reg->prev = reg; |
reg->next = reg->prev = reg; |
reg->sh_next = reg->sh_prev = reg; |
reg->sh_next = reg->sh_prev = reg; |
reg->addr = PAGE_SIZE; |
reg->addr = (void *)PAGE_SIZE; |
reg->phys = 0; |
reg->phys = 0; |
reg->size = USER_MAX - PAGE_SIZE; |
reg->size = USER_MAX - PAGE_SIZE; |
reg->flags = REG_FREE; |
reg->flags = REG_FREE; |
|
|
* Returns region on success, or NULL on failure. |
* Returns region on success, or NULL on failure. |
*/ |
*/ |
static struct region * |
static struct region * |
region_create(struct region *prev, u_long addr, size_t size) |
region_create(struct region *prev, void *addr, size_t size) |
{ |
{ |
struct region *reg; |
struct region *reg; |
|
|
|
|
* Find the region at the specified area. |
* Find the region at the specified area. |
*/ |
*/ |
static struct region * |
static struct region * |
region_find(struct region *head, u_long addr, size_t size) |
region_find(struct region *head, void *addr, size_t size) |
{ |
{ |
struct region *reg; |
struct region *reg; |
|
|
reg = head; |
reg = head; |
do { |
do { |
if (reg->addr <= addr && |
if (reg->addr <= addr && |
reg->addr + reg->size >= addr + size) { |
(char *)reg->addr + reg->size >= (char *)addr + size) { |
return reg; |
return reg; |
} |
} |
reg = reg->next; |
reg = reg->next; |
|
|
if ((reg->flags & REG_FREE) && reg->size >= size) { |
if ((reg->flags & REG_FREE) && reg->size >= size) { |
if (reg->size != size) { |
if (reg->size != size) { |
/* Split this region and return its head */ |
/* Split this region and return its head */ |
if (region_create(reg, reg->addr + size, |
if (region_create(reg, |
|
(char *)reg->addr + size, |
reg->size - size) == NULL) |
reg->size - size) == NULL) |
return NULL; |
return NULL; |
} |
} |
|
|
* Sprit region for the specified address/size. |
* Sprit region for the specified address/size. |
*/ |
*/ |
static struct region * |
static struct region * |
region_split(struct region *head, struct region *reg, u_long addr, |
region_split(struct region *head, struct region *reg, void *addr, |
size_t size) |
size_t size) |
{ |
{ |
struct region *prev, *next; |
struct region *prev, *next; |
|
|
prev = NULL; |
prev = NULL; |
if (reg->addr != addr) { |
if (reg->addr != addr) { |
prev = reg; |
prev = reg; |
diff = (size_t)(addr - reg->addr); |
diff = (size_t)((char *)addr - (char *)reg->addr); |
reg = region_create(prev, addr, prev->size - diff); |
reg = region_create(prev, addr, prev->size - diff); |
if (reg == NULL) |
if (reg == NULL) |
return NULL; |
return NULL; |
|
|
* Check next region to split region. |
* Check next region to split region. |
*/ |
*/ |
if (reg->size != size) { |
if (reg->size != size) { |
next = region_create(reg, reg->addr + size, |
next = region_create(reg, (char *)reg->addr + size, |
reg->size - size); |
reg->size - size); |
if (next == NULL) { |
if (next == NULL) { |
if (prev) { |
if (prev) { |
|
|
return reg; |
return reg; |
} |
} |
|
|
#if defined(DEBUG) && defined(CONFIG_KDUMP) |
#ifdef DEBUG |
void |
static void |
vm_dump_one(task_t task) |
vm_dump_one(task_t task) |
{ |
{ |
vm_map_t map; |
vm_map_t map; |
struct region *reg; |
struct region *reg; |
char flags[6]; |
char flags[6]; |
u_long total = 0; |
size_t total = 0; |
|
|
printk("task=%x map=%x name=%s\n", task, task->map, |
printf("task=%x map=%x name=%s\n", task, task->map, |
task->name ? task->name : "no name"); |
task->name != NULL ? task->name : "no name"); |
printk(" region virtual physical size flags\n"); |
printf(" region virtual physical size flags\n"); |
printk(" -------- -------- -------- -------- -----\n"); |
printf(" -------- -------- -------- -------- -----\n"); |
|
|
map = task->map; |
map = task->map; |
reg = &map->head; |
reg = &map->head; |
|
|
if (reg->flags & REG_MAPPED) |
if (reg->flags & REG_MAPPED) |
flags[4] = 'M'; |
flags[4] = 'M'; |
|
|
printk(" %08x %08x %08x %8x %s\n", reg, |
printf(" %08x %08x %08x %8x %s\n", reg, |
reg->addr, reg->phys, reg->size, flags); |
reg->addr, reg->phys, reg->size, flags); |
total += reg->size; |
total += reg->size; |
} |
} |
reg = reg->next; |
reg = reg->next; |
} while (reg != &map->head); /* Process all regions */ |
} while (reg != &map->head); /* Process all regions */ |
printk(" *total=%dK bytes\n\n", total / 1024); |
printf(" *total=%dK bytes\n\n", total / 1024); |
} |
} |
|
|
void |
void |
|
|
list_t n; |
list_t n; |
task_t task; |
task_t task; |
|
|
printk("\nVM dump:\n"); |
printf("\nVM dump:\n"); |
n = list_first(&kern_task.link); |
n = list_first(&kern_task.link); |
while (n != &kern_task.link) { |
while (n != &kern_task.link) { |
task = list_entry(n, struct task, link); |
task = list_entry(n, struct task, link); |
vm_dump_one(task); |
vm_dump_one(task); |
n = list_next(n); |
n = list_next(n); |
} |
} |
} |
|
#endif |
|
|
|
#ifdef CONFIG_VMTRACE |
|
static void |
|
vm_error(const char *func, int err) |
|
{ |
|
|
|
printk("VM error: %s returns err=%x\n", func, err); |
|
} |
} |
#endif |
#endif |
|
|