Added posix code

This commit is contained in:
Bahadir Balban
2009-09-29 21:55:59 +03:00
parent 54272ccb63
commit f0bb0a4657
478 changed files with 63161 additions and 0 deletions

Binary file not shown.

1
conts/posix/mm0/src/arch Symbolic link
View File

@@ -0,0 +1 @@
arch-arm

View File

@@ -0,0 +1,94 @@
/*
* Australian Public Licence B (OZPLB)
*
* Version 1-0
*
* Copyright (c) 2004 National ICT Australia
*
* All rights reserved.
*
* Developed by: Embedded, Real-time and Operating Systems Program (ERTOS)
* National ICT Australia
* http://www.ertos.nicta.com.au
*
* Permission is granted by National ICT Australia, free of charge, to
* any person obtaining a copy of this software and any associated
* documentation files (the "Software") to deal with the Software without
* restriction, including (without limitation) the rights to use, copy,
* modify, adapt, merge, publish, distribute, communicate to the public,
* sublicense, and/or sell, lend or rent out copies of the Software, and
* to permit persons to whom the Software is furnished to do so, subject
* to the following conditions:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimers.
*
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimers in the documentation and/or other materials provided
* with the distribution.
*
* * Neither the name of National ICT Australia, nor the names of its
* contributors, may be used to endorse or promote products derived
* from this Software without specific prior written permission.
*
* EXCEPT AS EXPRESSLY STATED IN THIS LICENCE AND TO THE FULL EXTENT
* PERMITTED BY APPLICABLE LAW, THE SOFTWARE IS PROVIDED "AS-IS", AND
* NATIONAL ICT AUSTRALIA AND ITS CONTRIBUTORS MAKE NO REPRESENTATIONS,
* WARRANTIES OR CONDITIONS OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO ANY REPRESENTATIONS, WARRANTIES OR CONDITIONS
* REGARDING THE CONTENTS OR ACCURACY OF THE SOFTWARE, OR OF TITLE,
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT,
* THE ABSENCE OF LATENT OR OTHER DEFECTS, OR THE PRESENCE OR ABSENCE OF
* ERRORS, WHETHER OR NOT DISCOVERABLE.
*
* TO THE FULL EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL
* NATIONAL ICT AUSTRALIA OR ITS CONTRIBUTORS BE LIABLE ON ANY LEGAL
* THEORY (INCLUDING, WITHOUT LIMITATION, IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHERWISE) FOR ANY CLAIM, LOSS, DAMAGES OR OTHER
* LIABILITY, INCLUDING (WITHOUT LIMITATION) LOSS OF PRODUCTION OR
* OPERATION TIME, LOSS, DAMAGE OR CORRUPTION OF DATA OR RECORDS; OR LOSS
* OF ANTICIPATED SAVINGS, OPPORTUNITY, REVENUE, PROFIT OR GOODWILL, OR
* OTHER ECONOMIC LOSS; OR ANY SPECIAL, INCIDENTAL, INDIRECT,
* CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES, ARISING OUT OF OR IN
* CONNECTION WITH THIS LICENCE, THE SOFTWARE OR THE USE OF OR OTHER
* DEALINGS WITH THE SOFTWARE, EVEN IF NATIONAL ICT AUSTRALIA OR ITS
* CONTRIBUTORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH CLAIM, LOSS,
* DAMAGES OR OTHER LIABILITY.
*
* If applicable legislation implies representations, warranties, or
* conditions, or imposes obligations or liability on National ICT
* Australia or one of its contributors in respect of the Software that
* cannot be wholly or partly excluded, restricted or modified, the
* liability of National ICT Australia or the contributor is limited, to
* the full extent permitted by the applicable legislation, at its
* option, to:
* a. in the case of goods, any one or more of the following:
* i. the replacement of the goods or the supply of equivalent goods;
* ii. the repair of the goods;
* iii. the payment of the cost of replacing the goods or of acquiring
* equivalent goods;
* iv. the payment of the cost of having the goods repaired; or
* b. in the case of services:
* i. the supplying of the services again; or
* ii. the payment of the cost of having the services supplied again.
*
* The construction, validity and performance of this licence is governed
* by the laws in force in New South Wales, Australia.
*/
#ifdef __thumb__
#define bl blx
#endif
.section .text.head
.code 32
.global _start;
.align;
_start:
ldr sp, =__stack
bl platform_init
bl __container_init
1:
b 1b

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2007 Bahadir Balban
*/
#include <arch/mm.h>
#include <task.h>
#include <vm_area.h>
/* Extracts generic protection flags from architecture-specific pte */
unsigned int vm_prot_flags(pte_t pte)
{
unsigned int vm_prot_flags = 0;
unsigned int rw_flags = __MAP_USR_RW_FLAGS & PTE_PROT_MASK;
unsigned int ro_flags = __MAP_USR_RO_FLAGS & PTE_PROT_MASK;
/* Clear non-protection flags */
pte &= PTE_PROT_MASK;;
if (pte == ro_flags)
vm_prot_flags = VM_READ | VM_EXEC;
else if (pte == rw_flags)
vm_prot_flags = VM_READ | VM_WRITE | VM_EXEC;
else
vm_prot_flags = VM_NONE;
return vm_prot_flags;
}
#if defined(DEBUG_FAULT_HANDLING)
void arch_print_fault_params(struct fault_data *fault)
{
printf("%s: Handling %s fault (%s abort) from %d. fault @ 0x%x\n",
__TASKNAME__, (fault->reason & VM_READ) ? "read" : "write",
is_prefetch_abort(fault->kdata->fsr) ? "prefetch" : "data",
fault->task->tid, fault->address);
}
#else
void arch_print_fault_params(struct fault_data *fault) { }
#endif
/*
* PTE STATES:
* PTE type field: 00 (Translation fault)
* PTE type field correct, AP bits: None (Read or Write access fault)
* PTE type field correct, AP bits: RO (Write access fault)
*/
/* Extracts arch-specific fault parameters and puts them into generic format */
void set_generic_fault_params(struct fault_data *fault)
{
unsigned int prot_flags = vm_prot_flags(fault->kdata->pte);
fault->reason = 0;
fault->pte_flags = prot_flags;
if (is_prefetch_abort(fault->kdata->fsr)) {
fault->reason |= VM_READ;
fault->address = fault->kdata->faulty_pc;
} else {
fault->address = fault->kdata->far;
/* Always assume read fault first */
if (prot_flags & VM_NONE)
fault->reason |= VM_READ;
else if (prot_flags & VM_READ)
fault->reason |= VM_WRITE;
else
BUG();
}
arch_print_fault_params(fault);
}

131
conts/posix/mm0/src/boot.c Normal file
View File

@@ -0,0 +1,131 @@
/*
* Functions used for running initial tasks during boot.
*
* Copyright (C) 2008 Bahadir Balban
*/
#include <boot.h>
#include <mmap.h>
#include <shm.h>
#include <utcb.h>
#include <l4/api/thread.h>
#include <l4lib/arch/syslib.h>
int boottask_setup_regions(struct vm_file *file, struct tcb *task,
unsigned long task_start, unsigned long task_end)
{
/*
* Set task's main address space boundaries. Not all tasks
* run in the default user boundaries, e.g. mm0 pager.
*/
task->start = task_start;
task->end = task_end;
/* Task stack starts right after the environment. */
task->stack_end = task->end;
task->stack_start = task->stack_end - DEFAULT_STACK_SIZE;
/* Prepare environment boundaries. */
task->args_end = task->stack_end;
task->args_start = task->stack_start - DEFAULT_ENV_SIZE;
/* Currently RO text and RW data are one region. TODO: Fix this */
task->data_start = task->start;
task->data_end = task->start + page_align_up(file->length);
task->text_start = task->data_start;
task->text_end = task->data_end;
task->entry = task->text_start;
/* Task's region available for mmap */
task->map_start = task->data_end;
task->map_end = task->stack_start;
return 0;
}
/*
* Used for mmapping boot task regions. These are slightly different
* from a vfs executable file.
*/
int boottask_mmap_regions(struct tcb *task, struct vm_file *file)
{
void *mapped;
struct vm_file *shm;
/*
* mmap each task's physical image to task's address space.
* TODO: Map data and text separately when available from bootdesc.
*/
if (IS_ERR(mapped = do_mmap(file, 0, task, task->text_start,
VM_READ | VM_WRITE | VM_EXEC | VMA_PRIVATE,
__pfn(page_align_up(task->text_end) -
task->text_start)))) {
printf("do_mmap: failed with %d.\n", (int)mapped);
return (int)mapped;
}
/* mmap each task's stack as anonymous memory. */
if (IS_ERR(mapped = do_mmap(0, 0, task, task->stack_start,
VM_READ | VM_WRITE |
VMA_PRIVATE | VMA_ANONYMOUS,
__pfn(task->stack_end -
task->stack_start)))) {
printf("do_mmap: Mapping stack failed with %d.\n",
(int)mapped);
return (int)mapped;
}
/* Task's default shared page */
task->shared_page = shm_new_address(DEFAULT_SHPAGE_SIZE/PAGE_SIZE);
/* Create a shared memory segment available for shmat() */
if (IS_ERR(shm = shm_new((key_t)task->shared_page,
__pfn(DEFAULT_SHPAGE_SIZE))))
return (int)shm;
task_setup_utcb(task);
return 0;
}
/*
* Main entry point for the creation, initialisation and
* execution of a new task.
*/
struct tcb *boottask_exec(struct vm_file *f, unsigned long task_region_start,
unsigned long task_region_end, struct task_ids *ids)
{
struct tcb *task;
int err;
if (IS_ERR(task = task_create(0, ids, THREAD_NEW_SPACE,
TCB_NO_SHARING)))
return task;
if ((err = boottask_setup_regions(f, task, task_region_start,
task_region_end)) < 0)
return PTR_ERR(err);
if ((err = boottask_mmap_regions(task, f)) < 0)
return PTR_ERR(err);
if ((err = task_setup_registers(task, 0, 0, 0)) < 0)
return PTR_ERR(err);
/* Add the task to the global task list */
global_add_task(task);
/* Add the file to global vm lists */
global_add_vm_file(f);
/* Prefault all its regions */
if (ids->tid == VFS_TID)
task_prefault_regions(task, f);
/* Start the task */
if ((err = task_start(task)) < 0)
return PTR_ERR(err);
return task;
}

View File

@@ -0,0 +1,39 @@
/*
* Reading of bootdesc forged at build time.
*
* Copyright (C) 2007 - 2009 Bahadir Balban
*/
#include <bootdesc.h>
#include <bootm.h>
#include <init.h>
#include <l4lib/arch/syslib.h>
extern unsigned long _end[];
extern unsigned long pager_offset;
void read_bootdesc(struct initdata *initdata)
{
int npages;
struct bootdesc *bootdesc;
/*
* End of the executable image is where bootdesc resides
*/
bootdesc = (struct bootdesc *)_end;
/* Check if bootdesc is on an unmapped page */
if (is_page_aligned(bootdesc))
l4_map_helper(bootdesc - pager_offset, PAGE_SIZE);
/* Allocate bootdesc sized structure */
initdata->bootdesc = alloc_bootmem(bootdesc->desc_size, 0);
/* Copy bootdesc to initdata */
memcpy(initdata->bootdesc, bootdesc,
bootdesc->desc_size);
if (npages > 0)
l4_unmap_helper((void *)page_align_up(_end),
PAGE_SIZE * npages);
}

View File

@@ -0,0 +1,62 @@
/*
* Boot memory allocator
*
* Copyright (C) 2009 Bahadir Balban
*/
#include <l4/macros.h>
#include <l4/config.h>
#include <l4/types.h>
#include <l4/lib/list.h>
#include <l4/lib/math.h>
#include <l4/api/thread.h>
#include <l4/api/kip.h>
#include <l4/api/errno.h>
#include INC_GLUE(memory.h)
#include <stdio.h>
/* All memory allocated here is discarded after boot */
#define BOOTMEM_SIZE SZ_32K
SECTION(".init.bootmem") char bootmem[BOOTMEM_SIZE];
SECTION(".stack") char stack[4096];
// SECTION("init.data")
extern unsigned long __stack[]; /* Linker defined */
static unsigned long cursor = (unsigned long)&bootmem;
void *alloc_bootmem(int size, int alignment)
{
void *ptr;
/* If alignment is required */
if (alignment) {
/* And cursor is not aligned */
if (!is_aligned(cursor, alignment))
/* Align the cursor to alignment */
cursor = align_up(cursor, alignment);
/* Align to 4 byte by default */
} else if (size >= 4) {
/* And cursor is not aligned */
if (!is_aligned(cursor, 4))
/* Align the cursor to alignment */
cursor = align_up(cursor, 4);
}
/* Allocate from cursor */
ptr = (void *)cursor;
/* Update cursor */
cursor += size;
/* Check if cursor is passed bootmem area */
if (cursor >= (unsigned long)&bootmem[BOOTMEM_SIZE]) {
printk("Fatal: Insufficient boot memory.\n");
BUG();
}
return ptr;
}

View File

@@ -0,0 +1,85 @@
/*
* Pager's capabilities for kernel resources
*
* Copyright (C) 2009 Bahadir Balban
*/
#include <bootm.h>
#include <init.h>
#include <l4/lib/list.h>
#include <capability.h>
#include <l4/api/capability.h>
#include <l4lib/arch/syscalls.h>
#include <l4/generic/cap-types.h> /* TODO: Move this to API */
#include <lib/malloc.h>
struct cap_list capability_list;
__initdata static struct capability *caparray;
__initdata static int total_caps = 0;
/* Copy all init-memory allocated capabilities */
void copy_boot_capabilities(struct initdata *initdata)
{
struct capability *cap;
for (int i = 0; i < total_caps; i++) {
cap = kzalloc(sizeof(struct capability));
/* This copies kernel-allocated unique cap id as well */
memcpy(cap, &caparray[i], sizeof(struct capability));
/* Initialize capability list */
link_init(&cap->list);
/* Add capability to global cap list */
list_insert(&capability_list.caps, &cap->list);
}
}
int read_kernel_capabilities(struct initdata *initdata)
{
int ncaps;
int err;
/* Read number of capabilities */
if ((err = l4_capability_control(CAP_CONTROL_NCAPS, 0, &ncaps)) < 0) {
printf("l4_capability_control() reading # of capabilities failed.\n"
"Could not complete CAP_CONTROL_NCAPS request.\n");
goto error;
}
total_caps = ncaps;
/* Allocate array of caps from boot memory */
caparray = alloc_bootmem(sizeof(struct capability) * ncaps, 0);
/* Read all capabilities */
if ((err = l4_capability_control(CAP_CONTROL_READ_CAPS, 0, caparray)) < 0) {
printf("l4_capability_control() reading of capabilities failed.\n"
"Could not complete CAP_CONTROL_READ_CAPS request.\n");
goto error;
}
/* Set up initdata pointer to important capabilities */
initdata->bootcaps = caparray;
for (int i = 0; i < ncaps; i++) {
/*
* TODO: There may be multiple physmem caps!
* This really needs to be considered as multiple
* membanks!!!
*/
if ((caparray[i].type & CAP_RTYPE_MASK)
== CAP_RTYPE_PHYSMEM) {
initdata->physmem = &caparray[i];
return 0;
}
}
printf("%s: Error, pager has no physmem capability defined.\n",
__TASKNAME__);
goto error;
return 0;
error:
BUG();
}

188
conts/posix/mm0/src/clone.c Normal file
View File

@@ -0,0 +1,188 @@
/*
* Forking and cloning threads, processes
*
* Copyright (C) 2008 Bahadir Balban
*/
#include <l4lib/arch/syslib.h>
#include <l4lib/ipcdefs.h>
#include <l4lib/exregs.h>
#include <l4/api/errno.h>
#include <l4/api/thread.h>
#include <syscalls.h>
#include <vm_area.h>
#include <task.h>
#include <mmap.h>
#include <shm.h>
#include <test.h>
#include <clone.h>
/*
* Sends vfs task information about forked child, and its utcb
*/
int vfs_notify_fork(struct tcb *child, struct tcb *parent, unsigned int flags)
{
int err = 0;
// printf("%s/%s\n", __TASKNAME__, __FUNCTION__);
l4_save_ipcregs();
/* Write parent and child information */
write_mr(L4SYS_ARG0, parent->tid);
write_mr(L4SYS_ARG1, child->tid);
write_mr(L4SYS_ARG2, (unsigned int)child->shared_page);
write_mr(L4SYS_ARG3, flags);
if ((err = l4_sendrecv(VFS_TID, VFS_TID,
L4_IPC_TAG_NOTIFY_FORK)) < 0) {
printf("%s: L4 IPC Error: %d.\n", __FUNCTION__, err);
goto out;
}
/* Check if syscall was successful */
if ((err = l4_get_retval()) < 0) {
printf("%s: Pager from VFS read error: %d.\n",
__FUNCTION__, err);
goto out;
}
out:
l4_restore_ipcregs();
return err;
}
int sys_fork(struct tcb *parent)
{
int err;
struct tcb *child;
struct exregs_data exregs;
struct task_ids ids;
// = {
// .tid = TASK_ID_INVALID,
// .spid = parent->spid, /* spid to copy from */
// .tgid = TASK_ID_INVALID, /* FIXME: !!! FIX THIS */
// };
/* Make all shadows in this task read-only */
vm_freeze_shadows(parent);
/*
* Create a new L4 thread with parent's page tables
* kernel stack and kernel-side tcb copied
*/
if (IS_ERR(child = task_create(parent, &ids, THREAD_COPY_SPACE,
TCB_NO_SHARING)))
return (int)child;
/* Set child's fork return value to 0 */
memset(&exregs, 0, sizeof(exregs));
exregs_set_mr(&exregs, MR_RETURN, 0);
/* Set child's new utcb address set by task_create() */
BUG_ON(!child->utcb_address);
exregs_set_utcb(&exregs, child->utcb_address);
/* Do the actual exregs call to c0 */
if ((err = l4_exchange_registers(&exregs, child->tid)) < 0)
BUG();
/* Create and prefault a shared page for child and map it to vfs task */
shpage_map_to_task(child, find_task(VFS_TID),
SHPAGE_NEW_ADDRESS | SHPAGE_NEW_SHM |
SHPAGE_PREFAULT);
// printf("Mapped 0x%p to vfs as utcb of %d\n", child->utcb, child->tid);
/* We can now notify vfs about forked process */
vfs_notify_fork(child, parent, TCB_NO_SHARING);
/* Add child to global task list */
global_add_task(child);
/* Start forked child. */
l4_thread_control(THREAD_RUN, &ids);
/* Return child tid to parent */
return child->tid;
}
int do_clone(struct tcb *parent, unsigned long child_stack, unsigned int flags)
{
struct exregs_data exregs;
struct task_ids ids;
struct tcb *child;
int err;
//ids.tid = TASK_ID_INVALID;
//ids.spid = parent->spid;
/* Determine whether the cloned thread is in parent's thread group */
if (flags & TCB_SHARED_TGROUP)
ids.tgid = parent->tgid;
else
ids.tgid = TASK_ID_INVALID;
if (IS_ERR(child = task_create(parent, &ids, THREAD_SAME_SPACE, flags)))
return (int)child;
/* Set up child stack marks with given stack argument */
child->stack_end = child_stack;
child->stack_start = 0;
memset(&exregs, 0, sizeof(exregs));
/* Set child's stack pointer */
exregs_set_stack(&exregs, child_stack);
/* Set child's clone return value to 0 */
exregs_set_mr(&exregs, MR_RETURN, 0);
BUG_ON(!child->utcb_address);
exregs_set_utcb(&exregs, child->utcb_address);
/* Do the actual exregs call to c0 */
if ((err = l4_exchange_registers(&exregs, child->tid)) < 0)
BUG();
/* Create and prefault a shared page for child and map it to vfs task */
shpage_map_to_task(child, find_task(VFS_TID),
SHPAGE_NEW_ADDRESS | SHPAGE_NEW_SHM |
SHPAGE_PREFAULT);
/* We can now notify vfs about forked process */
vfs_notify_fork(child, parent, flags);
/* Add child to global task list */
global_add_task(child);
/* Start cloned child. */
// printf("%s/%s: Starting cloned child.\n", __TASKNAME__, __FUNCTION__);
l4_thread_control(THREAD_RUN, &ids);
/* Return child tid to parent */
return child->tid;
}
int sys_clone(struct tcb *parent, void *child_stack, unsigned int clone_flags)
{
unsigned int flags = 0;
if (!child_stack)
return -EINVAL;
if (clone_flags & CLONE_VM)
flags |= TCB_SHARED_VM;
if (clone_flags & CLONE_FS)
flags |= TCB_SHARED_FS;
if (clone_flags & CLONE_FILES)
flags |= TCB_SHARED_FILES;
if (clone_flags & CLONE_THREAD)
flags |= TCB_SHARED_TGROUP;
if (clone_flags & CLONE_PARENT)
flags |= TCB_SHARED_PARENT;
return do_clone(parent, (unsigned long)child_stack, flags);
}

52
conts/posix/mm0/src/dev.c Normal file
View File

@@ -0,0 +1,52 @@
/*
* Copyright (C) 2008 Bahadir Balban
*/
#include <l4/lib/list.h>
#include <vm_area.h>
#include <lib/malloc.h>
/*
* This is yet unused, it is more of an anticipation
* of how mmaped devices would be mapped with a pager.
*/
struct mmap_device {
struct link page_list; /* Dyn-allocated page list */
unsigned long pfn_start; /* Physical pfn start */
unsigned long pfn_end; /* Physical pfn end */
};
struct page *memdev_page_in(struct vm_object *vm_obj,
unsigned long pfn_offset)
{
struct vm_file *f = vm_object_to_file(vm_obj);
struct mmap_device *memdev = f->priv_data;
struct page *page;
/* Check if its within device boundary */
if (pfn_offset >= memdev->pfn_end - memdev->pfn_start)
return PTR_ERR(-1);
/* Simply return the page if found */
list_foreach_struct(page, &memdev->page_list, list)
if (page->offset == pfn_offset)
return page;
/* Otherwise allocate one of our own for that offset and return it */
page = kzalloc(sizeof(struct page));
link_init(&page->list);
spin_lock_init(&page->lock);
page->offset = pfn_offset;
page->owner = vm_obj;
list_insert(&page->list, &memdev->page_list);
return page;
}
/* All mmapable devices are handled by this */
struct vm_pager memdev_pager = {
.ops = {
.page_in = memdev_page_in,
},
};

View File

@@ -0,0 +1,366 @@
/*
* Program execution
*
* Copyright (C) 2008 Bahadir Balban
*/
#include <l4lib/arch/syslib.h>
#include <l4lib/arch/syscalls.h>
#include <l4lib/ipcdefs.h>
#include <l4lib/types.h>
#include <l4/macros.h>
#include <l4/api/errno.h>
#include <lib/malloc.h>
#include <vm_area.h>
#include <syscalls.h>
#include <string.h>
#include <exec.h>
#include <file.h>
#include <user.h>
#include <task.h>
#include <exit.h>
#include <lib/elf/elf.h>
/*
* Probes and parses the low-level executable file format and creates a
* generic execution description that can be used to run the task.
*/
int task_setup_from_executable(struct vm_file *vmfile, struct tcb *task,
struct exec_file_desc *efd)
{
memset(efd, 0, sizeof(*efd));
return elf_parse_executable(task, vmfile, efd);
}
int do_execve(struct tcb *sender, char *filename, struct args_struct *args,
struct args_struct *env)
{
unsigned long vnum, length;
struct vm_file *vmfile;
struct exec_file_desc efd;
struct tcb *new_task, *tgleader;
int err;
/* Get file info from vfs */
if ((err = vfs_open_bypath(filename, &vnum, &length)) < 0)
return err;
/* Create and get the file structure */
if (IS_ERR(vmfile = do_open2(0, 0, vnum, length)))
return (int)vmfile;
/* Create a new tcb */
if (IS_ERR(new_task = tcb_alloc_init(TCB_NO_SHARING))) {
vm_file_put(vmfile);
return (int)new_task;
}
/* Fill and validate tcb memory segment markers from executable file */
if ((err = task_setup_from_executable(vmfile, new_task, &efd)) < 0) {
vm_file_put(vmfile);
kfree(new_task);
return err;
}
/*
* If sender is a thread in a group, need to find the
* group leader and destroy all threaded children in
* the group.
*/
if (sender->clone_flags & TCB_SHARED_TGROUP) {
struct tcb *thread;
/* Find the thread group leader of sender */
BUG_ON(!(tgleader = find_task(sender->tgid)));
/* Destroy all children threads. */
list_foreach_struct(thread, &tgleader->children, child_ref)
do_exit(thread, 0);
} else {
/* Otherwise group leader is same as sender */
tgleader = sender;
}
/*
* Copy data to be retained from exec'ing task to new one.
* Release all task resources, do everything done in
* exit() except destroying the actual thread.
*/
if ((err = execve_recycle_task(new_task, tgleader)) < 0) {
vm_file_put(vmfile);
kfree(new_task);
return err;
}
/* Map task's new segment markers as virtual memory regions */
if ((err = task_mmap_segments(new_task, vmfile, &efd, args, env)) < 0) {
vm_file_put(vmfile);
kfree(new_task);
return err;
}
/* Set up task registers via exchange_registers() */
task_setup_registers(new_task, 0, new_task->args_start, new_task->pagerid);
/* Add new task to global list */
global_add_task(new_task);
/* Start the task */
task_start(new_task);
#if 0
TODO:
Dynamic Linking.
/* See if an interpreter (dynamic linker) is needed */
/* Find the interpreter executable file, if needed */
/*
* Map all dynamic linker file segments
* (should not clash with original executable
*/
/* Set up registers to run dynamic linker (exchange_registers()) */
/* Run the interpreter */
/*
* The interpreter will:
* - Need some initial info (dyn sym tables) at a certain location
* - Find necessary shared library files in userspace
* (will use open/read).
* - Map them into process address space via mmap()
* - Reinitialise references to symbols in the shared libraries
* - Jump to the entry point of main executable.
*/
#endif
return 0;
}
/*
* Copy from one buffer to another. Stop if maxlength or
* a page boundary is hit.
*/
int strncpy_page(void *to_ptr, void *from_ptr, int maxlength)
{
int count = 0;
char *to = to_ptr, *from = from_ptr;
do {
if ((to[count] = from[count]) == '\0') {
count++;
break;
} else
count++;
} while (count < maxlength && !page_boundary(&from[count]));
if (page_boundary(&from[count]))
return -EFAULT;
if (count == maxlength)
return -E2BIG;
return count;
}
/*
* Copy from one buffer to another. Stop if maxlength or
* a page boundary is hit. Breaks if unsigned long sized copy value is 0,
* as opposed to a 0 byte as in string copy. If byte size 0 was used
* a valid pointer with a 0 byte in it would give a false termination.
*/
int bufncpy_page(void *to_ptr, void *from_ptr, int maxlength)
{
int count = 0;
unsigned long *to = to_ptr, *from = from_ptr;
do {
if ((to[count] = from[count]) == 0) {
count++;
break;
} else
count++;
} while (count < maxlength && !page_boundary(&from[count]));
if (page_boundary(&from[count]))
return -EFAULT;
if (count == maxlength)
return -E2BIG;
return count;
}
/*
* Copies a variable sized userspace string or array of pointers
* (think &argv[0]), into buffer. If a page boundary is hit,
* unmaps the previous page, validates and maps the new page.
*/
int copy_user_buf(struct tcb *task, void *buf, char *user, int maxlength,
int elem_size)
{
int count = maxlength;
int copied = 0, ret = 0, total = 0;
void *mapped = 0;
int (*copy_func)(void *, void *, int count);
/* This bit determines what size copier function to use. */
if (elem_size == sizeof(char))
copy_func = strncpy_page;
else if (elem_size == sizeof(unsigned long))
copy_func = bufncpy_page;
else
return -EINVAL;
/* Map the first page the user buffer is in */
if (!(mapped = pager_validate_map_user_range(task, user,
TILL_PAGE_ENDS(user),
VM_READ)))
return -EINVAL;
while ((ret = copy_func(buf + copied, mapped, count)) < 0) {
if (ret == -E2BIG)
return ret;
else if (ret == -EFAULT) {
/*
* Copied is always in bytes no matter what elem_size is
* because we know we hit a page boundary and we increase
* by the page boundary bytes
*/
pager_unmap_user_range(mapped, TILL_PAGE_ENDS(mapped));
copied += TILL_PAGE_ENDS(mapped);
count -= TILL_PAGE_ENDS(mapped);
if (!(mapped =
pager_validate_map_user_range(task, user + copied,
TILL_PAGE_ENDS(user + copied),
VM_READ)))
return -EINVAL;
}
}
/* Note copied is always in bytes */
total = (copied / elem_size) + ret;
/* Unmap the final page */
pager_unmap_user_range(mapped, TILL_PAGE_ENDS(mapped));
return total;
}
/*
* Calls copy_user_buf with char-sized copying. This matters because
* buffer is variable and the terminator must be in char size
*/
static inline int
copy_user_string(struct tcb *task, void *buf, char *user,
int maxlength)
{
return copy_user_buf(task, buf, user, maxlength, sizeof(char));
}
/*
* Calls copy_user_buf with unsigned long sized copying. This matters
* because buffer is variable and the terminator must be in ulong size
*/
static inline int
copy_user_ptrs(struct tcb *task, void *buf, char *user,
int maxlength)
{
return copy_user_buf(task, buf, user, maxlength, sizeof(unsigned long));
}
int copy_user_args(struct tcb *task, struct args_struct *args,
void *argv_user, int args_max)
{
char **argv = 0;
void *argsbuf;
char *curbuf;
int argc = 0;
int used;
int count;
if (!(argsbuf = kzalloc(args_max)))
return -ENOMEM;
/*
* First, copy the null-terminated array of
* pointers to argument strings.
*/
if ((count = copy_user_ptrs(task, argsbuf, argv_user, args_max)) < 0)
goto out;
/* On success, we get the number of arg strings + the terminator */
argc = count - 1;
used = count * sizeof(char *);
argv = argsbuf;
curbuf = argsbuf + used;
/* Now we copy each argument string into buffer */
for (int i = 0; i < argc; i++) {
/* Copy string into empty space in buffer */
if ((count = copy_user_string(task, curbuf, argv[i],
args_max - used)) < 0)
goto out;
/* Replace pointer to string with copied location */
argv[i] = curbuf;
/* Update current empty buffer location */
curbuf += count;
/* Increase used buffer count */
used += count;
}
/* Set up the args struct */
args->argc = argc;
args->argv = argv;
args->size = used;
return 0;
out:
kfree(argsbuf);
return count;
}
int sys_execve(struct tcb *sender, char *pathname, char *argv[], char *envp[])
{
int ret;
char *path;
struct args_struct args;
struct args_struct env;
if (!(path = kzalloc(PATH_MAX)))
return -ENOMEM;
memset(&args, 0, sizeof(args));
memset(&env, 0, sizeof(env));
/* Copy the executable path string */
if ((ret = copy_user_string(sender, path, pathname, PATH_MAX)) < 0)
return ret;
// printf("%s: Copied pathname: %s\n", __FUNCTION__, path);
/* Copy the args */
if (argv && ((ret = copy_user_args(sender, &args, argv, ARGS_MAX)) < 0))
goto out1;
/* Copy the env */
if (envp && ((ret = copy_user_args(sender, &env, envp,
ARGS_MAX - args.size)) < 0))
goto out2;
ret = do_execve(sender, path, &args, &env);
if (env.argv)
kfree(env.argv);
out2:
if (args.argv)
kfree(args.argv);
out1:
kfree(path);
return ret;
}

166
conts/posix/mm0/src/exit.c Normal file
View File

@@ -0,0 +1,166 @@
/*
* exit()
*
* Copyright (C) 2008 Bahadir Balban
*/
#include <shm.h>
#include <task.h>
#include <file.h>
#include <exit.h>
#include <test.h>
#include <utcb.h>
#include <vm_area.h>
#include <syscalls.h>
#include <l4lib/arch/syslib.h>
#include <l4lib/arch/syscalls.h>
#include <l4lib/exregs.h>
#include <l4lib/ipcdefs.h>
#include <lib/malloc.h>
#include <l4/api/space.h>
/*
* Sends vfs task information about forked child, and its utcb
*/
int vfs_notify_exit(struct tcb *task, int status)
{
int err = 0;
// printf("%s/%s\n", __TASKNAME__, __FUNCTION__);
l4_save_ipcregs();
/* Write parent and child information */
write_mr(L4SYS_ARG0, task->tid);
write_mr(L4SYS_ARG1, status);
if ((err = l4_sendrecv(VFS_TID, VFS_TID,
L4_IPC_TAG_NOTIFY_EXIT)) < 0) {
printf("%s: L4 IPC Error: %d.\n", __FUNCTION__, err);
goto out;
}
/* Check if syscall was successful */
if ((err = l4_get_retval()) < 0) {
printf("%s: VFS returned ipc error: %d.\n",
__FUNCTION__, err);
goto out;
}
out:
l4_restore_ipcregs();
return err;
}
/* Closes all file descriptors of a task */
int task_close_files(struct tcb *task)
{
int err = 0;
/* Flush all file descriptors */
for (int fd = 0; fd < TASK_FILES_MAX; fd++)
if (task->files->fd[fd].vmfile)
if ((err = sys_close(task, fd)) < 0) {
printf("File close error. Tid: %d,"
" fd: %d, error: %d\n",
task->tid, fd, err);
break;
}
return err;
}
/* Prepare old task's environment for new task */
int execve_recycle_task(struct tcb *new, struct tcb *orig)
{
int err;
struct task_ids ids = {
.tid = orig->tid,
.spid = orig->spid,
.tgid = orig->tgid,
};
/*
* Copy data to new task that is
* to be retained from original
*/
/* Copy ids */
new->tid = orig->tid;
new->spid = orig->spid;
new->tgid = orig->tgid;
new->pagerid = orig->pagerid;
/* Copy shared page */
new->shared_page = orig->shared_page;
/* Copy parent relationship */
BUG_ON(new->parent);
new->parent = orig->parent;
list_insert(&new->child_ref, &orig->parent->children);
/* Flush all IO on task's files and close fds */
task_close_files(orig);
/* Destroy task's utcb slot */
task_destroy_utcb(orig);
/* Vfs still knows the thread */
/* Keep the shared page on vfs */
/* Ask the kernel to recycle the thread */
if ((err = l4_thread_control(THREAD_RECYCLE, &ids)) < 0) {
printf("%s: Suspending thread %d failed with %d.\n",
__FUNCTION__, orig->tid, err);
return err;
}
/* Destroy the locally known tcb */
tcb_destroy(orig);
return 0;
}
void do_exit(struct tcb *task, int status)
{
struct task_ids ids = {
.tid = task->tid,
.spid = task->spid,
.tgid = task->tgid,
};
/* Flush all IO on task's files and close fds */
task_close_files(task);
/* Destroy task's utcb slot */
task_destroy_utcb(task);
/* Tell vfs that task is exiting */
vfs_notify_exit(task, status);
/* Remove default shared page shm areas from vfs */
// printf("Unmapping 0x%p from vfs as shared-page of %d\n", task->shared_page, task->tid);
shpage_unmap_from_task(task, find_task(VFS_TID));
/* Free task's local tcb */
tcb_destroy(task);
/* Ask the kernel to delete the thread from its records */
l4_thread_control(THREAD_DESTROY, &ids);
/* TODO: Wake up any waiters about task's destruction */
#if 0
struct tcb *parent = find_task(task->parentid);
if (parent->waiting) {
exregs_set_mr_return(status);
l4_exchange_registers(parent->tid);
l4_thread_control(THREAD_RUN, parent->tid);
}
#endif
}
void sys_exit(struct tcb *task, int status)
{
do_exit(task, status);
}

1005
conts/posix/mm0/src/fault.c Normal file

File diff suppressed because it is too large Load Diff

993
conts/posix/mm0/src/file.c Normal file
View File

@@ -0,0 +1,993 @@
/*
* File read, write, open and close.
*
* Copyright (C) 2008 Bahadir Balban
*/
#include <init.h>
#include <vm_area.h>
#include <lib/malloc.h>
#include <mm/alloc_page.h>
#include <l4/macros.h>
#include <l4/api/errno.h>
#include <l4lib/types.h>
#include <l4lib/arch/syscalls.h>
#include <l4lib/arch/syslib.h>
#include <l4lib/ipcdefs.h>
#include <l4/api/kip.h>
#include <posix/sys/types.h>
#include <string.h>
#include <globals.h>
#include <file.h>
#include <user.h>
#include <test.h>
/* Copy from one page's buffer into another page */
int page_copy(struct page *dst, struct page *src,
unsigned long dst_offset, unsigned long src_offset,
unsigned long size)
{
void *dstvaddr, *srcvaddr;
BUG_ON(dst_offset + size > PAGE_SIZE);
BUG_ON(src_offset + size > PAGE_SIZE);
dstvaddr = l4_map_helper((void *)page_to_phys(dst), 1);
srcvaddr = l4_map_helper((void *)page_to_phys(src), 1);
#if 0
printf("%s: Copying from page with offset %d to page with offset %d\n"
"src copy offset: 0x%x, dst copy offset: 0x%x, copy size: %d\n",
__FUNCTION__, src->offset, dst->offset, src_offset, dst_offset,
size);
printf("%s: Copying string: %s\n", __FUNCTION__,
srcvaddr + src_offset);
#endif
memcpy(dstvaddr + dst_offset, srcvaddr + src_offset, size);
l4_unmap_helper(dstvaddr, 1);
l4_unmap_helper(srcvaddr, 1);
return 0;
}
int vfs_read(unsigned long vnum, unsigned long file_offset,
unsigned long npages, void *pagebuf)
{
int err = 0;
// printf("%s/%s\n", __TASKNAME__, __FUNCTION__);
l4_save_ipcregs();
write_mr(L4SYS_ARG0, vnum);
write_mr(L4SYS_ARG1, file_offset);
write_mr(L4SYS_ARG2, npages);
write_mr(L4SYS_ARG3, (u32)pagebuf);
if ((err = l4_sendrecv(VFS_TID, VFS_TID, L4_IPC_TAG_PAGER_READ)) < 0) {
printf("%s: L4 IPC Error: %d.\n", __FUNCTION__, err);
goto out;
}
/* Check if syscall was successful */
if ((err = l4_get_retval()) < 0) {
printf("%s: Error: %d.\n",
__FUNCTION__, err);
goto out;
}
out:
l4_restore_ipcregs();
return err;
}
/*
* Different from vfs_open(), which validates an already opened
* file descriptor, this call opens a new vfs file by the pager
* using the given path. The vnum handle and file length is returned
* since the pager uses this information to access file pages.
*/
int vfs_open_bypath(const char *pathname, unsigned long *vnum, unsigned long *length)
{
int err = 0;
struct tcb *vfs;
// printf("%s/%s\n", __TASKNAME__, __FUNCTION__);
if (!(vfs = find_task(VFS_TID)))
return -ESRCH;
/*
* Copy string to vfs shared page.
*
* FIXME: There's a chance we're overwriting other tasks'
* ipc information that is on the vfs shared page.
*/
strcpy(vfs->shared_page + 0x200, pathname);
l4_save_ipcregs();
write_mr(L4SYS_ARG0, (unsigned long)vfs->shared_page + 0x200);
if ((err = l4_sendrecv(VFS_TID, VFS_TID,
L4_IPC_TAG_PAGER_OPEN_BYPATH)) < 0) {
printf("%s: L4 IPC Error: %d.\n", __FUNCTION__, err);
goto out;
}
/* Check if syscall was successful */
if ((err = l4_get_retval()) < 0) {
printf("%s: VFS open error: %d.\n",
__FUNCTION__, err);
goto out;
}
/* Read file information */
*vnum = read_mr(L4SYS_ARG0);
*length = read_mr(L4SYS_ARG1);
out:
l4_restore_ipcregs();
return err;
}
/*
* When a task does a read/write/mmap request on a file, if
* the file descriptor is unknown to the pager, this call
* asks vfs if that file has been opened, and any other
* relevant information.
*/
int vfs_open(l4id_t opener, int fd, unsigned long *vnum, unsigned long *length)
{
int err = 0;
l4_save_ipcregs();
write_mr(L4SYS_ARG0, opener);
write_mr(L4SYS_ARG1, fd);
if ((err = l4_sendrecv(VFS_TID, VFS_TID, L4_IPC_TAG_PAGER_OPEN)) < 0) {
printf("%s: L4 IPC Error: %d.\n", __FUNCTION__, err);
goto out;
}
/* Check if syscall was successful */
if ((err = l4_get_retval()) < 0) {
printf("%s: VFS open error: %d.\n",
__FUNCTION__, err);
goto out;
}
/* Read file information */
*vnum = read_mr(L4SYS_ARG0);
*length = read_mr(L4SYS_ARG1);
out:
l4_restore_ipcregs();
return err;
}
/*
* Initialise a new file and the descriptor for it from given file data.
* Could be called by an actual task or a pager
*/
struct vm_file *do_open2(struct tcb *task, int fd, unsigned long vnum, unsigned long length)
{
struct vm_file *vmfile;
/* Is this an open by a task (as opposed to by the pager)? */
if (task) {
/* fd slot must be empty */
BUG_ON(task->files->fd[fd].vnum != 0);
BUG_ON(task->files->fd[fd].cursor != 0);
/* Assign vnum to given fd on the task */
task->files->fd[fd].vnum = vnum;
task->files->fd[fd].cursor = 0;
}
/* Check if that vm_file is already in the list */
list_foreach_struct(vmfile, &global_vm_files.list, list) {
/* Check whether it is a vfs file and if so vnums match. */
if ((vmfile->type & VM_FILE_VFS) &&
vm_file_to_vnum(vmfile) == vnum) {
/* Task opener? */
if (task)
/* Add a reference to it from the task */
task->files->fd[fd].vmfile = vmfile;
vmfile->openers++;
return vmfile;
}
}
/* Otherwise allocate a new one for this vnode */
if (IS_ERR(vmfile = vfs_file_create()))
return vmfile;
/* Initialise and add a reference to it from the task */
vm_file_to_vnum(vmfile) = vnum;
vmfile->length = length;
vmfile->vm_obj.pager = &file_pager;
/* Task opener? */
if (task)
task->files->fd[fd].vmfile = vmfile;
vmfile->openers++;
/* Add to file list */
global_add_vm_file(vmfile);
return vmfile;
}
/* Initialise a new file and the descriptor for it from given file data */
int do_open(struct tcb *task, int fd, unsigned long vnum, unsigned long length)
{
struct vm_file *vmfile;
/* fd slot must be empty */
BUG_ON(task->files->fd[fd].vnum != 0);
BUG_ON(task->files->fd[fd].cursor != 0);
/* Assign vnum to given fd on the task */
task->files->fd[fd].vnum = vnum;
task->files->fd[fd].cursor = 0;
/* Check if that vm_file is already in the list */
list_foreach_struct(vmfile, &global_vm_files.list, list) {
/* Check whether it is a vfs file and if so vnums match. */
if ((vmfile->type & VM_FILE_VFS) &&
vm_file_to_vnum(vmfile) == vnum) {
/* Add a reference to it from the task */
task->files->fd[fd].vmfile = vmfile;
vmfile->openers++;
return 0;
}
}
/* Otherwise allocate a new one for this vnode */
if (IS_ERR(vmfile = vfs_file_create()))
return (int)vmfile;
/* Initialise and add a reference to it from the task */
vm_file_to_vnum(vmfile) = vnum;
vmfile->length = length;
vmfile->vm_obj.pager = &file_pager;
task->files->fd[fd].vmfile = vmfile;
vmfile->openers++;
/* Add to file list */
global_add_vm_file(vmfile);
return 0;
}
int file_open(struct tcb *opener, int fd)
{
int err;
unsigned long vnum;
unsigned long length;
if (fd < 0 || fd > TASK_FILES_MAX)
return -EINVAL;
/* Ask vfs if such a file has been recently opened */
if ((err = vfs_open(opener->tid, fd, &vnum, &length)) < 0)
return err;
/* Initialise local structures with received file data */
if ((err = do_open(opener, fd, vnum, length)) < 0)
return err;
return 0;
}
/*
* Inserts the page to vmfile's list in order of page frame offset.
* We use an ordered list instead of a better data structure for now.
*/
int insert_page_olist(struct page *this, struct vm_object *vmo)
{
struct page *before, *after;
/* Add if list is empty */
if (list_empty(&vmo->page_cache)) {
list_insert_tail(&this->list, &vmo->page_cache);
return 0;
}
/* Else find the right interval */
list_foreach_struct(before, &vmo->page_cache, list) {
after = link_to_struct(before->list.next, struct page, list);
/* If there's only one in list */
if (before->list.next == &vmo->page_cache) {
/* Add as next if greater */
if (this->offset > before->offset)
list_insert(&this->list, &before->list);
/* Add as previous if smaller */
else if (this->offset < before->offset)
list_insert_tail(&this->list, &before->list);
else
BUG();
return 0;
}
/* If this page is in-between two other, insert it there */
if (before->offset < this->offset &&
after->offset > this->offset) {
list_insert(&this->list, &before->list);
return 0;
}
BUG_ON(this->offset == before->offset);
BUG_ON(this->offset == after->offset);
}
BUG();
}
/*
* This reads-in a range of pages from a file and populates the page cache
* just like a page fault, but its not in the page fault path.
*/
int read_file_pages(struct vm_file *vmfile, unsigned long pfn_start,
unsigned long pfn_end)
{
struct page *page;
for (int f_offset = pfn_start; f_offset < pfn_end; f_offset++) {
page = vmfile->vm_obj.pager->ops.page_in(&vmfile->vm_obj,
f_offset);
if (IS_ERR(page)) {
printf("%s: %s:Could not read page %d "
"from file with vnum: 0x%lu\n", __TASKNAME__,
__FUNCTION__, f_offset, vm_file_to_vnum(vmfile));
return (int)page;
}
}
return 0;
}
int vfs_write(unsigned long vnum, unsigned long file_offset,
unsigned long npages, void *pagebuf)
{
int err = 0;
// printf("%s/%s\n", __TASKNAME__, __FUNCTION__);
l4_save_ipcregs();
write_mr(L4SYS_ARG0, vnum);
write_mr(L4SYS_ARG1, file_offset);
write_mr(L4SYS_ARG2, npages);
write_mr(L4SYS_ARG3, (u32)pagebuf);
if ((err = l4_sendrecv(VFS_TID, VFS_TID, L4_IPC_TAG_PAGER_WRITE)) < 0) {
printf("%s: L4 IPC Error: %d.\n", __FUNCTION__, err);
goto out;
}
/* Check if syscall was successful */
if ((err = l4_get_retval()) < 0) {
printf("%s: Pager to VFS write error: %d.\n", __FUNCTION__, err);
goto out;
}
out:
l4_restore_ipcregs();
return err;
}
int vfs_close(l4id_t sender, int fd)
{
int err = 0;
// printf("%s/%s Sending to %d\n", __TASKNAME__, __FUNCTION__, VFS_TID);
l4_save_ipcregs();
write_mr(L4SYS_ARG0, sender);
write_mr(L4SYS_ARG1, fd);
if ((err = l4_sendrecv(VFS_TID, VFS_TID, L4_IPC_TAG_PAGER_CLOSE)) < 0) {
printf("%s: L4 IPC Error: %d.\n", __FUNCTION__, err);
goto out;
}
// printf("%s/%s Received from %d\n", __TASKNAME__, __FUNCTION__, VFS_TID);
/* Check if syscall was successful */
if ((err = l4_get_retval()) < 0) {
printf("%s: Pager to VFS write error: %d.\n", __FUNCTION__, err);
goto out;
}
out:
l4_restore_ipcregs();
return err;
}
/* Writes updated file stats back to vfs. (e.g. new file size) */
int vfs_update_file_stats(struct vm_file *f)
{
int err = 0;
// printf("%s/%s\n", __TASKNAME__, __FUNCTION__);
l4_save_ipcregs();
write_mr(L4SYS_ARG0, vm_file_to_vnum(f));
write_mr(L4SYS_ARG1, f->length);
if ((err = l4_sendrecv(VFS_TID, VFS_TID,
L4_IPC_TAG_PAGER_UPDATE_STATS)) < 0) {
printf("%s: L4 IPC Error: %d.\n", __FUNCTION__, err);
goto out;
}
/* Check if syscall was successful */
if ((err = l4_get_retval()) < 0) {
printf("%s: Pager to VFS write error: %d.\n",
__FUNCTION__, err);
goto out;
}
out:
l4_restore_ipcregs();
return err;
}
/* Writes pages in cache back to their file */
int write_file_pages(struct vm_file *f, unsigned long pfn_start,
unsigned long pfn_end)
{
int err;
/* We have only thought of vfs files for this */
BUG_ON(f->type != VM_FILE_VFS);
/* Need not flush files that haven't been written */
if (!(f->vm_obj.flags & VM_DIRTY))
return 0;
BUG_ON(pfn_end != __pfn(page_align_up(f->length)));
for (int f_offset = pfn_start; f_offset < pfn_end; f_offset++) {
err = f->vm_obj.pager->ops.page_out(&f->vm_obj, f_offset);
if (err < 0) {
printf("%s: %s:Could not write page %d "
"to file with vnum: 0x%lu\n", __TASKNAME__,
__FUNCTION__, f_offset, vm_file_to_vnum(f));
return err;
}
}
return 0;
}
/* Flush all dirty file pages and update file stats */
int flush_file_pages(struct vm_file *f)
{
int err;
if ((err = write_file_pages(f, 0, __pfn(page_align_up(f->length)))) < 0)
return err;
if ((err = vfs_update_file_stats(f)) < 0)
return err;
return 0;
}
/* Given a task and fd, syncs all IO on it */
int fsync_common(struct tcb *task, int fd)
{
int err;
/* Check fd validity */
if (fd < 0 || fd > TASK_FILES_MAX)
return -EINVAL;
/*
* If we don't know about the file, even if it was
* opened by the vfs, it is sure that there's no
* pending IO on it. We simply return.
*/
if (!task->files->fd[fd].vmfile)
return 0;
/* Finish I/O on file */
if ((err = flush_file_pages(task->files->fd[fd].vmfile)) < 0)
return err;
return 0;
}
void vm_file_put(struct vm_file *file)
{
/* Reduce file's opener count */
if (!(file->openers--))
/* No openers left, check any mappers */
if (!file->vm_obj.nlinks)
/* No links or openers, delete the file */
vm_file_delete(file);
}
/* Closes the file descriptor and notifies vfs */
int do_close(struct tcb *task, int fd)
{
int err;
// printf("%s: Closing fd: %d on task %d\n", __FUNCTION__,
// fd, task->tid);
if ((err = vfs_close(task->tid, fd)) < 0)
return err;
/*
* If there was no IO on it, we may not know the file,
* we simply return here. Since we notify VFS about the
* close, it can tell us if the fd was open or not.
*/
if (!task->files->fd[fd].vmfile)
return 0;
/* Reduce file refcount etc. */
vm_file_put(task->files->fd[fd].vmfile);
task->files->fd[fd].vnum = 0;
task->files->fd[fd].cursor = 0;
task->files->fd[fd].vmfile = 0;
return 0;
}
int sys_close(struct tcb *task, int fd)
{
int ret;
/* Sync the file and update stats */
if ((ret = fsync_common(task, fd)) < 0)
return ret;
/* Close the file descriptor. */
return do_close(task, fd);
}
int sys_fsync(struct tcb *task, int fd)
{
/* Sync the file and update stats */
return fsync_common(task, fd);
}
/* FIXME: Add error handling to this */
/* Extends a file's size by adding it new pages */
int new_file_pages(struct vm_file *f, unsigned long start, unsigned long end)
{
unsigned long npages = end - start;
struct page *page;
void *paddr;
/* Allocate the memory for new pages */
if (!(paddr = alloc_page(npages)))
return -ENOMEM;
/* Process each page */
for (unsigned long i = 0; i < npages; i++) {
page = phys_to_page(paddr + PAGE_SIZE * i);
page_init(page);
page->refcnt++;
page->owner = &f->vm_obj;
page->offset = start + i;
page->virtual = 0;
/* Add the page to file's vm object */
BUG_ON(!list_empty(&page->list));
insert_page_olist(page, &f->vm_obj);
}
/* Update vm object */
f->vm_obj.npages += npages;
return 0;
}
/* TODO:
* Re-evaluate. Possibly merge with read_cache_pages.
*/
/* Writes user data in buffer into pages in cache */
int write_cache_pages_orig(struct vm_file *vmfile, struct tcb *task, void *buf,
unsigned long pfn_start, unsigned long pfn_end,
unsigned long cursor_offset, int count)
{
struct page *head, *this;
unsigned long last_pgoff; /* Last copied page's offset */
unsigned long copy_offset; /* Current copy offset on the buffer */
int copysize, left;
/* Find the head of consecutive pages */
list_foreach_struct(head, &vmfile->vm_obj.page_cache, list)
if (head->offset == pfn_start)
goto copy;
/*
* Page not found, this is a bug. The writeable page range
* must have been readied by read_file_pages()/new_file_pages().
*/
BUG();
copy:
left = count;
/* Copy the first page and unmap. */
copysize = (left < PAGE_SIZE) ? left : PAGE_SIZE;
copy_offset = (unsigned long)buf;
page_copy(head, task_virt_to_page(task, copy_offset),
cursor_offset, copy_offset & PAGE_MASK, copysize);
head->flags |= VM_DIRTY;
head->owner->flags |= VM_DIRTY;
left -= copysize;
last_pgoff = head->offset;
/* Map the rest, copy and unmap. */
list_foreach_struct(this, &head->list, list) {
if (left == 0 || this->offset == pfn_end)
break;
/* Make sure we're advancing on pages consecutively */
BUG_ON(this->offset != last_pgoff + 1);
copysize = (left < PAGE_SIZE) ? left : PAGE_SIZE;
copy_offset = (unsigned long)buf + count - left;
/* Must be page aligned */
BUG_ON(!is_page_aligned(copy_offset));
page_copy(this, task_virt_to_page(task, copy_offset),
0, 0, copysize);
this->flags |= VM_DIRTY;
left -= copysize;
last_pgoff = this->offset;
}
BUG_ON(left != 0);
return count - left;
}
/*
* Writes user data in buffer into pages in cache. If a page is not
* found, it's a bug. The writeable page range must have been readied
* by read_file_pages()/new_file_pages().
*/
int write_cache_pages(struct vm_file *vmfile, struct tcb *task, void *buf,
unsigned long pfn_start, unsigned long pfn_end,
unsigned long cursor_offset, int count)
{
struct page *head;
unsigned long last_pgoff; /* Last copied page's offset */
unsigned long copy_offset; /* Current copy offset on the buffer */
int copysize, left;
/* Find the head of consecutive pages */
list_foreach_struct(head, &vmfile->vm_obj.page_cache, list) {
/* First page */
if (head->offset == pfn_start) {
left = count;
/* Copy the first page and unmap. */
copysize = (left < PAGE_SIZE) ? left : PAGE_SIZE;
copy_offset = (unsigned long)buf;
page_copy(head, task_virt_to_page(task, copy_offset),
cursor_offset, copy_offset & PAGE_MASK, copysize);
head->flags |= VM_DIRTY;
head->owner->flags |= VM_DIRTY;
left -= copysize;
last_pgoff = head->offset;
/* Rest of the consecutive pages */
} else if (head->offset > pfn_start && head->offset < pfn_end) {
/* Make sure we're advancing on pages consecutively */
BUG_ON(head->offset != last_pgoff + 1);
copysize = (left < PAGE_SIZE) ? left : PAGE_SIZE;
copy_offset = (unsigned long)buf + count - left;
/* Must be page aligned */
BUG_ON(!is_page_aligned(copy_offset));
page_copy(head, task_virt_to_page(task, copy_offset),
0, 0, copysize);
head->flags |= VM_DIRTY;
left -= copysize;
last_pgoff = head->offset;
} else if (head->offset == pfn_end || left == 0)
break;
}
BUG_ON(left != 0);
return count - left;
}
/*
* Reads a page range from an ordered list of pages into buffer.
*
* NOTE:
* This assumes the page range is consecutively available in the cache
* and count bytes are available. To ensure this, read_file_pages must
* be called first and count must be checked. Since it has read-checking
* assumptions, count must be satisfied.
*/
int read_cache_pages(struct vm_file *vmfile, struct tcb *task, void *buf,
unsigned long pfn_start, unsigned long pfn_end,
unsigned long cursor_offset, int count)
{
struct page *head, *this;
int copysize, left;
unsigned long last_pgoff; /* Last copied page's offset */
unsigned long copy_offset; /* Current copy offset on the buffer */
/* Find the head of consecutive pages */
list_foreach_struct(head, &vmfile->vm_obj.page_cache, list)
if (head->offset == pfn_start)
goto copy;
/* Page not found, nothing read */
return 0;
copy:
left = count;
/* Copy the first page and unmap. */
copysize = (left < PAGE_SIZE) ? left : PAGE_SIZE;
copy_offset = (unsigned long)buf;
page_copy(task_virt_to_page(task, copy_offset), head,
PAGE_MASK & copy_offset, cursor_offset, copysize);
left -= copysize;
last_pgoff = head->offset;
/* Map the rest, copy and unmap. */
list_foreach_struct(this, &head->list, list) {
if (left == 0 || this->offset == pfn_end)
break;
/* Make sure we're advancing on pages consecutively */
BUG_ON(this->offset != last_pgoff + 1);
/* Get copying size and start offset */
copysize = (left < PAGE_SIZE) ? left : PAGE_SIZE;
copy_offset = (unsigned long)buf + count - left;
/* MUST be page aligned */
BUG_ON(!is_page_aligned(copy_offset));
page_copy(task_virt_to_page(task, copy_offset),
this, 0, 0, copysize);
left -= copysize;
last_pgoff = this->offset;
}
BUG_ON(left != 0);
return count - left;
}
int sys_read(struct tcb *task, int fd, void *buf, int count)
{
unsigned long pfn_start, pfn_end;
unsigned long cursor, byte_offset;
struct vm_file *vmfile;
int ret = 0;
/* Check fd validity */
if (!task->files->fd[fd].vmfile)
if ((ret = file_open(task, fd)) < 0)
return ret;
/* Check count validity */
if (count < 0)
return -EINVAL;
else if (!count)
return 0;
/* Check user buffer validity. */
if ((ret = pager_validate_user_range(task, buf,
(unsigned long)count,
VM_READ)) < 0)
return -EFAULT;
vmfile = task->files->fd[fd].vmfile;
cursor = task->files->fd[fd].cursor;
/* If cursor is beyond file end, simply return 0 */
if (cursor >= vmfile->length)
return 0;
/* Start and end pages expected to be read by user */
pfn_start = __pfn(cursor);
pfn_end = __pfn(page_align_up(cursor + count));
/* But we can read up to maximum file size */
pfn_end = __pfn(page_align_up(vmfile->length)) < pfn_end ?
__pfn(page_align_up(vmfile->length)) : pfn_end;
/* If trying to read more than end of file, reduce it to max possible */
if (cursor + count > vmfile->length)
count = vmfile->length - cursor;
/* Read the page range into the cache from file */
if ((ret = read_file_pages(vmfile, pfn_start, pfn_end)) < 0)
return ret;
/* The offset of cursor on first page */
byte_offset = PAGE_MASK & cursor;
/* Read it into the user buffer from the cache */
if ((count = read_cache_pages(vmfile, task, buf, pfn_start, pfn_end,
byte_offset, count)) < 0)
return count;
/* Update cursor on success */
task->files->fd[fd].cursor += count;
return count;
}
/* FIXME:
*
* Error:
* We find the page buffer is in, and then copy from the *start* of the page
* rather than buffer's offset in that page. - I think this is fixed.
*/
int sys_write(struct tcb *task, int fd, void *buf, int count)
{
unsigned long pfn_wstart, pfn_wend; /* Write start/end */
unsigned long pfn_fstart, pfn_fend; /* File start/end */
unsigned long pfn_nstart, pfn_nend; /* New pages start/end */
unsigned long cursor, byte_offset;
struct vm_file *vmfile;
int ret = 0;
/* Check fd validity */
if (!task->files->fd[fd].vmfile)
if ((ret = file_open(task, fd)) < 0)
return ret;
/* Check count validity */
if (count < 0)
return -EINVAL;
else if (!count)
return 0;
/* Check user buffer validity. */
if ((ret = pager_validate_user_range(task, buf,
(unsigned long)count,
VM_WRITE | VM_READ)) < 0)
return -EINVAL;
vmfile = task->files->fd[fd].vmfile;
cursor = task->files->fd[fd].cursor;
/* See what pages user wants to write */
pfn_wstart = __pfn(cursor);
pfn_wend = __pfn(page_align_up(cursor + count));
/* Get file start and end pages */
pfn_fstart = 0;
pfn_fend = __pfn(page_align_up(vmfile->length));
/*
* Find the intersection to determine which pages are
* already part of the file, and which ones are new.
*/
if (pfn_wstart < pfn_fend) {
pfn_fstart = pfn_wstart;
/*
* Shorten the end if end page is
* less than file size
*/
if (pfn_wend < pfn_fend) {
pfn_fend = pfn_wend;
/* This also means no new pages in file */
pfn_nstart = 0;
pfn_nend = 0;
} else {
/* The new pages start from file end,
* and end by write end. */
pfn_nstart = pfn_fend;
pfn_nend = pfn_wend;
}
} else {
/* No intersection, its all new pages */
pfn_fstart = 0;
pfn_fend = 0;
pfn_nstart = pfn_wstart;
pfn_nend = pfn_wend;
}
/*
* Read in the portion that's already part of the file.
*/
if ((ret = read_file_pages(vmfile, pfn_fstart, pfn_fend)) < 0)
return ret;
/* Create new pages for the part that's new in the file */
if ((ret = new_file_pages(vmfile, pfn_nstart, pfn_nend)) < 0)
return ret;
/*
* At this point be it new or existing file pages, all pages
* to be written are expected to be in the page cache. Write.
*/
byte_offset = PAGE_MASK & cursor;
if ((ret = write_cache_pages(vmfile, task, buf, pfn_wstart,
pfn_wend, byte_offset, count)) < 0)
return ret;
/*
* Update the file size, and cursor. vfs will be notified
* of this change when the file is flushed (e.g. via fflush()
* or close())
*/
if (task->files->fd[fd].cursor + count > vmfile->length)
vmfile->length = task->files->fd[fd].cursor + count;
task->files->fd[fd].cursor += count;
return count;
}
/* FIXME: Check for invalid cursor values. Check for total, sometimes negative. */
int sys_lseek(struct tcb *task, int fd, off_t offset, int whence)
{
int retval = 0;
unsigned long long total, cursor;
/* Check fd validity */
if (!task->files->fd[fd].vmfile)
if ((retval = file_open(task, fd)) < 0)
return retval;
/* Offset validity */
if (offset < 0)
return -EINVAL;
switch (whence) {
case SEEK_SET:
retval = task->files->fd[fd].cursor = offset;
break;
case SEEK_CUR:
cursor = (unsigned long long)task->files->fd[fd].cursor;
if (cursor + offset > 0xFFFFFFFF)
retval = -EINVAL;
else
retval = task->files->fd[fd].cursor += offset;
break;
case SEEK_END:
cursor = (unsigned long long)task->files->fd[fd].cursor;
total = (unsigned long long)task->files->fd[fd].vmfile->length;
if (cursor + total > 0xFFFFFFFF)
retval = -EINVAL;
else {
retval = task->files->fd[fd].cursor =
task->files->fd[fd].vmfile->length + offset;
}
default:
retval = -EINVAL;
break;
}
return retval;
}

288
conts/posix/mm0/src/init.c Normal file
View File

@@ -0,0 +1,288 @@
/*
* Initialise the system.
*
* Copyright (C) 2007, 2008 Bahadir Balban
*/
#include <stdio.h>
#include <string.h>
#include <memory.h>
#include <mm/alloc_page.h>
#include <lib/malloc.h>
#include <lib/bit.h>
#include <l4lib/arch/syscalls.h>
#include <l4lib/arch/syslib.h>
#include <l4lib/utcb.h>
#include <task.h>
#include <shm.h>
#include <file.h>
#include <init.h>
#include <test.h>
#include <boot.h>
#include <utcb.h>
#include <bootm.h>
/* A separate list than the generic file list that keeps just the boot files */
LINK_DECLARE(boot_file_list);
/* Kernel data acquired during initialisation */
__initdata struct initdata initdata;
void print_bootdesc(struct bootdesc *bd)
{
for (int i = 0; i < bd->total_images; i++) {
printf("Task Image: %d\n", i);
printf("Name: %s\n", bd->images[i].name);
printf("Start: 0x%x\n", bd->images[i].phys_start);
printf("End: 0x%x\n", bd->images[i].phys_end);
}
}
void print_pfn_range(int pfn, int size)
{
unsigned int addr = pfn << PAGE_BITS;
unsigned int end = (pfn + size) << PAGE_BITS;
printf("Used: 0x%x - 0x%x\n", addr, end);
}
#if 0
void print_page_map(struct page_bitmap *map)
{
unsigned int start_pfn = 0;
unsigned int total_used = 0;
int numpages = 0;
// printf("Page map: 0x%x-0x%x\n", map->pfn_start << PAGE_BITS, map->pfn_end << PAGE_BITS);
for (int i = 0; i < (PHYSMEM_TOTAL_PAGES >> 5); i++) {
for (int x = 0; x < WORD_BITS; x++) {
if (map->map[i] & (1 << x)) { /* A used page found? */
if (!start_pfn) /* First such page found? */
start_pfn = (WORD_BITS * i) + x;
total_used++;
numpages++; /* Increase number of pages */
} else { /* Either used pages ended or were never found */
if (start_pfn) { /* We had a used page */
/* Finished end of used range.
* Print and reset. */
//print_pfn_range(start_pfn, numpages);
start_pfn = 0;
numpages = 0;
}
}
}
}
printf("%s: Pagemap: Total of %d used physical pages. %d Kbytes used.\n", __TASKNAME__, total_used, total_used << 2);
}
#endif
/*
* A specialised function for setting up the task environment of mm0.
* Essentially all the memory regions are set up but a new task isn't
* created, registers aren't assigned, and thread not started, since
* these are all already done by the kernel. But we do need a memory
* environment for mm0, hence this function.
*/
int mm0_task_init(struct vm_file *f, unsigned long task_start,
unsigned long task_end, struct task_ids *ids)
{
int err;
struct tcb *task;
/*
* The thread itself is already known by the kernel, so we just
* allocate a local task structure.
*/
BUG_ON(IS_ERR(task = tcb_alloc_init(TCB_NO_SHARING)));
task->tid = ids->tid;
task->spid = ids->spid;
task->tgid = ids->tgid;
if ((err = boottask_setup_regions(f, task,
task_start, task_end)) < 0)
return err;
if ((err = boottask_mmap_regions(task, f)) < 0)
return err;
/* Set pager as child and parent of itself */
list_insert(&task->child_ref, &task->children);
task->parent = task;
/*
* The first UTCB address is already assigned by the
* microkernel for this pager. Ensure that we also get
* the same from our internal utcb bookkeeping.
*/
BUG_ON(task->utcb_address != UTCB_AREA_START);
/* Pager must prefault its utcb */
prefault_page(task, task->utcb_address,
VM_READ | VM_WRITE);
/* Add the task to the global task list */
global_add_task(task);
/* Add the file to global vm lists */
global_add_vm_file(f);
return 0;
}
struct vm_file *initdata_next_bootfile(struct initdata *initdata)
{
struct vm_file *file, *n;
list_foreach_removable_struct(file, n,
&initdata->boot_file_list,
list) {
list_remove_init(&file->list);
return file;
}
return 0;
}
/*
* Reads boot files from init data, determines their task ids if they
* match with particular servers, and starts the tasks.
*/
int start_boot_tasks(struct initdata *initdata)
{
struct vm_file *file = 0, *fs0_file = 0, *mm0_file = 0, *n;
struct tcb *fs0_task;
struct svc_image *img;
struct task_ids ids;
struct link other_files;
int total = 0;
link_init(&other_files);
/* Separate out special server tasks and regular files */
do {
file = initdata_next_bootfile(initdata);
if (file) {
BUG_ON(file->type != VM_FILE_BOOTFILE);
img = file->priv_data;
if (!strcmp(img->name, __PAGERNAME__))
mm0_file = file;
else if (!strcmp(img->name, __VFSNAME__))
fs0_file = file;
else
list_insert(&file->list, &other_files);
} else
break;
} while (1);
/* MM0 needs partial initialisation since it's already running. */
// printf("%s: Initialising mm0 tcb.\n", __TASKNAME__);
ids.tid = PAGER_TID;
ids.spid = PAGER_TID;
ids.tgid = PAGER_TID;
if (mm0_task_init(mm0_file, INITTASK_AREA_START,
INITTASK_AREA_END, &ids) < 0)
BUG();
total++;
/* Initialise vfs with its predefined id */
ids.tid = VFS_TID;
ids.spid = VFS_TID;
ids.tgid = VFS_TID;
// printf("%s: Initialising fs0\n",__TASKNAME__);
BUG_ON((IS_ERR(fs0_task = boottask_exec(fs0_file, USER_AREA_START,
USER_AREA_END, &ids))));
total++;
/* Initialise other tasks */
list_foreach_removable_struct(file, n, &other_files, list) {
ids.tid = TASK_ID_INVALID;
ids.spid = TASK_ID_INVALID;
ids.tgid = TASK_ID_INVALID;
list_remove_init(&file->list);
BUG_ON(IS_ERR(boottask_exec(file, USER_AREA_START,
USER_AREA_END, &ids)));
total++;
}
if (!total) {
printf("%s: Could not start any tasks.\n",
__TASKNAME__);
BUG();
}
return 0;
}
extern unsigned long _start_init[];
extern unsigned long _end_init[];
/*
* Copy all necessary data from initmem to real memory,
* release initdata and any init memory used
*/
void copy_release_initdata(struct initdata *initdata)
{
/*
* Copy boot capabilities to a list of
* real capabilities
*/
copy_boot_capabilities(initdata);
/* Free and unmap init memory:
*
* FIXME: We can and do safely unmap the boot
* memory here, but because we don't utilize it yet,
* it remains as if it is a used block
*/
l4_unmap(_start_init,
__pfn(page_align_up(_end_init - _start_init)),
self_tid());
}
void init_mm(struct initdata *initdata)
{
/* Initialise the page and bank descriptors */
init_physmem_primary(initdata);
init_physmem_secondary(initdata, membank);
/* Initialise the page allocator on first bank. */
init_page_allocator(membank[0].free, membank[0].end);
/* Initialise the zero page */
init_devzero();
/* Initialise in-memory boot files */
init_boot_files(initdata);
if (shm_pool_init() < 0) {
printf("SHM initialisation failed.\n");
BUG();
}
if (utcb_pool_init() < 0) {
printf("SHM initialisation failed.\n");
BUG();
}
}
void init_pager(void)
{
pager_address_pool_init();
read_kernel_capabilities(&initdata);
read_bootdesc(&initdata);
init_mm(&initdata);
start_boot_tasks(&initdata);
copy_release_initdata(&initdata);
mm0_test_global_vm_integrity();
}

View File

@@ -0,0 +1,59 @@
/*
* This module allocates an unused address range from
* a given memory region defined as the pool range.
*
* Copyright (C) 2007 Bahadir Balban
*/
#include <lib/bit.h>
#include <l4/macros.h>
#include <l4/types.h>
#include INC_GLUE(memory.h)
#include <lib/addr.h>
#include <stdio.h>
/*
* Initializes an address pool, but uses an already
* allocated id pool for it.
*/
int address_pool_init_with_idpool(struct address_pool *pool,
struct id_pool *idpool,
unsigned long start, unsigned long end)
{
pool->idpool = idpool;
pool->start = start;
pool->end = end;
return 0;
}
int address_pool_init(struct address_pool *pool, unsigned long start, unsigned long end)
{
if ((pool->idpool = id_pool_new_init(__pfn(end - start))) < 0)
return (int)pool->idpool;
pool->start = start;
pool->end = end;
return 0;
}
void *address_new(struct address_pool *pool, int npages)
{
unsigned int pfn;
if ((int)(pfn = ids_new_contiguous(pool->idpool, npages)) < 0)
return 0;
return (void *)__pfn_to_addr(pfn) + pool->start;
}
int address_del(struct address_pool *pool, void *addr, int npages)
{
unsigned long pfn = __pfn(page_align(addr) - pool->start);
if (ids_del_contiguous(pool->idpool, pfn, npages) < 0) {
printf("%s: Invalid address range returned to "
"virtual address pool.\n", __FUNCTION__);
return -1;
}
return 0;
}

View File

@@ -0,0 +1,111 @@
/*
* Bit manipulation functions.
*
* Copyright (C) 2007 Bahadir Balban
*/
#include <lib/bit.h>
#include <l4/macros.h>
#include <l4/config.h>
#include <stdio.h>
#include INC_GLUE(memory.h)
/* Emulation of ARM's CLZ (count leading zeroes) instruction */
unsigned int __clz(unsigned int bitvector)
{
unsigned int x = 0;
while((!(bitvector & ((unsigned)1 << 31))) && (x < 32)) {
bitvector <<= 1;
x++;
}
return x;
}
int find_and_set_first_free_bit(u32 *word, unsigned int limit)
{
int success = 0;
int i;
for(i = 0; i < limit; i++) {
/* Find first unset bit */
if (!(word[BITWISE_GETWORD(i)] & BITWISE_GETBIT(i))) {
/* Set it */
word[BITWISE_GETWORD(i)] |= BITWISE_GETBIT(i);
success = 1;
break;
}
}
/* Return bit just set */
if (success)
return i;
else
return -1;
}
int find_and_set_first_free_contig_bits(u32 *word, unsigned int limit,
int nbits)
{
int i = 0, first = 0, last = 0, found = 0;
/* Can't allocate more than the limit */
if (nbits > limit)
return -1;
/* This is a state machine that checks n contiguous free bits. */
/* FIXME: It should be <= instead of <. Fix & test in a single patch */
while (i + nbits < limit) {
first = i;
last = i;
while (!(word[BITWISE_GETWORD(last)] & BITWISE_GETBIT(last))) {
last++;
i++;
if (last == first + nbits) {
found = 1;
break;
}
}
if (found)
break;
i++;
}
/* If found, set the bits */
if (found) {
for (int x = first; x < first + nbits; x++)
word[BITWISE_GETWORD(x)] |= BITWISE_GETBIT(x);
return first;
} else
return -1;
}
int check_and_clear_bit(u32 *word, int bit)
{
/* Check that bit was set */
if (word[BITWISE_GETWORD(bit)] & BITWISE_GETBIT(bit)) {
word[BITWISE_GETWORD(bit)] &= ~BITWISE_GETBIT(bit);
return 0;
} else {
printf("Trying to clear already clear bit\n");
return -1;
}
}
int check_and_set_bit(u32 *word, int bit)
{
/* Check that bit was clear */
if (!(word[BITWISE_GETWORD(bit)] & BITWISE_GETBIT(bit))) {
word[BITWISE_GETWORD(bit)] |= BITWISE_GETBIT(bit);
return 0;
} else {
//printf("Trying to set already set bit\n");
return -1;
}
}
int check_and_clear_contig_bits(u32 *word, int first, int nbits)
{
for (int i = first; i < first + nbits; i++)
if (check_and_clear_bit(word, i) < 0)
return -1;
return 0;
}

View File

@@ -0,0 +1,177 @@
/*
* ELF manipulation routines
*
* Copyright (C) 2008 Bahadir Balban
*/
#include <memory.h>
#include <vm_area.h>
#include <l4/api/errno.h>
#include <lib/elf/elf.h>
#include <lib/elf/elfprg.h>
#include <lib/elf/elfsym.h>
#include <lib/elf/elfsect.h>
int elf_probe(struct elf_header *header)
{
/* Test that it is a 32-bit little-endian ELF file */
if (header->e_ident[EI_MAG0] == ELFMAG0 &&
header->e_ident[EI_MAG1] == ELFMAG1 &&
header->e_ident[EI_MAG2] == ELFMAG2 &&
header->e_ident[EI_MAG3] == ELFMAG3 &&
header->e_ident[EI_CLASS] == ELFCLASS32 &&
header->e_ident[EI_DATA] == ELFDATA2LSB)
return 0;
else
return -1;
}
/*
* Sets or expands a segment region if it has the given type and flags
* For expansion we assume any new section must come consecutively
* after the existing segment, otherwise we ignore it for simplicity.
*/
int elf_test_expand_segment(struct elf_section_header *section,
unsigned int sec_type, unsigned int sec_flags,
unsigned int sec_flmask, unsigned long *start,
unsigned long *end, unsigned long *offset)
{
if (section->sh_type == sec_type &&
(section->sh_flags & sec_flmask) == sec_flags) {
/* Set new section */
if (!*start) {
BUG_ON(*offset || *end);
*offset = section->sh_offset;
*start = section->sh_addr;
*end = section->sh_addr + section->sh_size;
/* Expand existing section from the end */
} else if (*end == section->sh_addr)
*end = section->sh_addr + section->sh_size;
}
return 0;
}
/*
* Sift through sections and copy their marks to tcb and efd
* if they are recognised and loadable sections. Test the
* assigned segment marks and return an error if they're invalid.
*/
int elf_mark_segments(struct elf_section_header *sect_header, int nsections,
struct tcb *task, struct exec_file_desc *efd)
{
for (int i = 0; i < nsections; i++) {
struct elf_section_header *section = &sect_header[i];
/* Text + read-only data segments */
elf_test_expand_segment(section, SHT_PROGBITS,
SHF_ALLOC, SHF_ALLOC | SHF_WRITE,
&task->text_start, &task->text_end,
&efd->text_offset);
/* Data segment */
elf_test_expand_segment(section, SHT_PROGBITS, SHF_ALLOC |
SHF_WRITE, SHF_ALLOC | SHF_WRITE,
&task->data_start, &task->data_end,
&efd->data_offset);
/* Bss segment */
elf_test_expand_segment(section, SHT_NOBITS, SHF_ALLOC |
SHF_WRITE, SHF_ALLOC | SHF_WRITE,
&task->bss_start, &task->bss_end,
&efd->bss_offset);
}
/* Test anomalies with the mappings */
/* No text */
if (!task->text_start) {
printf("%s: Error: Could not find a text "
"segment in ELF file.\n", __FUNCTION__);
return -ENOEXEC;
}
/* Warn if no data or bss but it's not an error */
if (!task->data_start || !task->bss_start) {
printf("%s: NOTE: Could not find a data and/or "
"bss segment in ELF file.\n", __FUNCTION__);
}
/* Data and text are on the same page and not on a page boundary */
if (!((is_page_aligned(task->data_start) &&
task->data_start == task->text_end) ||
(page_align(task->data_start) > page_align(task->text_end))))
if ((task->data_start - task->text_end) < PAGE_SIZE &&
!is_page_aligned(task->text_end)) {
printf("%s: Error: Distance between data and text"
" sections are less than page size (%d bytes)\n",
__FUNCTION__, PAGE_SIZE);
return -ENOEXEC;
}
return 0;
}
/*
* Loading an ELF file:
*
* This first probes and detects that the given file is a valid elf file.
* Then it looks at the program header table to find the first (probably
* only) segment that has type LOAD. Then it looks at the section header
* table, to find out about every loadable section that is part of this
* aforementioned loadable program segment. Each section is marked in the
* efd and tcb structures for further memory mappings.
*/
int elf_parse_executable(struct tcb *task, struct vm_file *file,
struct exec_file_desc *efd)
{
struct elf_header elf_header, *elf_headerp = pager_map_page(file, 0);
struct elf_program_header *prg_header_start, *prg_header_load;
struct elf_section_header *sect_header;
unsigned long sect_offset, sect_size;
unsigned long prg_offset, prg_size;
int err = 0;
/* Test that it is a valid elf file */
if ((err = elf_probe(elf_headerp)) < 0)
return err;
/* Copy the elf header and unmap first page */
memcpy(&elf_header, elf_headerp, sizeof(elf_header));
pager_unmap_page(elf_headerp);
/* Find the markers for section and program header tables */
sect_offset = elf_header.e_shoff;
sect_size = elf_header.e_shentsize * elf_header.e_shnum;
prg_offset = elf_header.e_phoff;
prg_size = elf_header.e_phentsize * elf_header.e_phnum;
/* Get the program header table */
prg_header_start = (struct elf_program_header *)
pager_map_file_range(file, prg_offset, prg_size);
/* Get the first loadable segment. We currently just stare at it */
for (int i = 0; i < elf_header.e_phnum; i++) {
if (prg_header_start[i].p_type == PT_LOAD) {
prg_header_load = &prg_header_start[i];
break;
}
}
/* Get the section header table */
sect_header = (struct elf_section_header *)
pager_map_file_range(file, sect_offset, sect_size);
/* Copy segment marks from ELF file to task + efd. Return errors */
err = elf_mark_segments(sect_header, elf_header.e_shnum, task, efd);
/* Unmap program header table */
pager_unmap_pages(prg_header_start, __pfn(page_align_up(prg_size)));
/* Unmap section header table */
pager_unmap_pages(sect_header, __pfn(page_align_up(sect_size)));
return err;
}

View File

@@ -0,0 +1,88 @@
/*
* Used for thread and space ids.
*
* Copyright (C) 2007 Bahadir Balban
*/
#include <lib/idpool.h>
#include <lib/malloc.h>
#include <l4/macros.h>
#include INC_GLUE(memory.h)
#include <stdio.h>
#include <l4/api/errno.h>
struct id_pool *id_pool_new_init(int totalbits)
{
int nwords = BITWISE_GETWORD(totalbits) + 1;
struct id_pool *new = kzalloc((nwords * SZ_WORD)
+ sizeof(struct id_pool));
if (!new)
return PTR_ERR(-ENOMEM);
new->nwords = nwords;
new->bitlimit = totalbits;
return new;
}
/* Search for a free slot up to the limit given */
int id_new(struct id_pool *pool)
{
return find_and_set_first_free_bit(pool->bitmap, pool->bitlimit);
}
/* This finds n contiguous free ids, allocates and returns the first one */
int ids_new_contiguous(struct id_pool *pool, int numids)
{
int id = find_and_set_first_free_contig_bits(pool->bitmap,
pool->bitlimit,
numids);
if (id < 0)
printf("%s: Warning! New id alloc failed\n", __FUNCTION__);
return id;
}
/* This deletes a list of contiguous ids given the first one and number of ids */
int ids_del_contiguous(struct id_pool *pool, int first, int numids)
{
int ret;
if (pool->nwords * WORD_BITS < first + numids)
return -1;
if ((ret = check_and_clear_contig_bits(pool->bitmap, first, numids)))
printf("%s: Error: Invalid argument range.\n", __FUNCTION__);
return ret;
}
int id_del(struct id_pool *pool, int id)
{
int ret;
if (pool->nwords * WORD_BITS < id)
return -1;
if ((ret = check_and_clear_bit(pool->bitmap, id) < 0))
printf("%s: Error: Could not delete id.\n", __FUNCTION__);
return ret;
}
/* Return a specific id, if available */
int id_get(struct id_pool *pool, int id)
{
int ret;
ret = check_and_set_bit(pool->bitmap, id);
if (ret < 0)
return ret;
else
return id;
}
int id_is_empty(struct id_pool *pool)
{
for (int i = 0; i < pool->nwords; i++)
if (pool->bitmap[i])
return 0;
return 1;
}

View File

@@ -0,0 +1,413 @@
/*****************************************************************************
Simple malloc
Chris Giese <geezer@execpc.com> http://www.execpc.com/~geezer
Release date: Oct 30, 2002
This code is public domain (no copyright).
You can do whatever you want with it.
Features:
- First-fit
- free() coalesces adjacent free blocks
- Uses variable-sized heap, enlarged with kbrk()/sbrk() function
- Does not use mmap()
- Can be easily modified to use fixed-size heap
- Works with 16- or 32-bit compilers
Build this program with either of the two main() functions, then run it.
Messages that indicate a software error will contain three asterisks (***).
*****************************************************************************/
#include <string.h> /* memcpy(), memset() */
#include <stdio.h> /* printf() */
#include <l4/macros.h>
#define _32BIT 1
/* use small (32K) heap for 16-bit compilers,
large (500K) heap for 32-bit compilers */
#if defined(_32BIT)
#define HEAP_SIZE 500000uL
#else
#define HEAP_SIZE 32768u
#endif
#define MALLOC_MAGIC 0x6D92 /* must be < 0x8000 */
typedef struct _malloc /* Turbo C DJGPP */
{
size_t size; /* 2 bytes 4 bytes */
struct _malloc *next; /* 2 bytes 4 bytes */
unsigned magic : 15; /* 2 bytes total 4 bytes total */
unsigned used : 1;
} malloc_t; /* total 6 bytes 12 bytes */
static char *g_heap_bot, *g_kbrk, *g_heap_top;
/*****************************************************************************
*****************************************************************************/
void dump_heap(void)
{
unsigned blks_used = 0, blks_free = 0;
size_t bytes_used = 0, bytes_free = 0;
malloc_t *m;
int total;
printf("===============================================\n");
for(m = (malloc_t *)g_heap_bot; m != NULL; m = m->next)
{
printf("blk %5p: %6u bytes %s\n", m,
m->size, m->used ? "used" : "free");
if(m->used)
{
blks_used++;
bytes_used += m->size;
}
else
{
blks_free++;
bytes_free += m->size;
}
}
printf("blks: %6u used, %6u free, %6u total\n", blks_used,
blks_free, blks_used + blks_free);
printf("bytes: %6u used, %6u free, %6u total\n", bytes_used,
bytes_free, bytes_used + bytes_free);
printf("g_heap_bot=0x%p, g_kbrk=0x%p, g_heap_top=0x%p\n",
g_heap_bot, g_kbrk, g_heap_top);
total = (bytes_used + bytes_free) +
(blks_used + blks_free) * sizeof(malloc_t);
if(total != g_kbrk - g_heap_bot)
printf("*** some heap memory is not accounted for\n");
printf("===============================================\n");
}
/*****************************************************************************
POSIX sbrk() looks like this
void *sbrk(int incr);
Mine is a bit different so I can signal the calling function
if more memory than desired was allocated (e.g. in a system with paging)
If your kbrk()/sbrk() always allocates the amount of memory you ask for,
this code can be easily changed.
int brk( void *sbrk( void *kbrk(
function void *adr); int delta); int *delta);
---------------------- ------------ ------------ -------------
POSIX? yes yes NO
return value if error -1 -1 NULL
get break value . sbrk(0) int x=0; kbrk(&x);
set break value to X brk(X) sbrk(X - sbrk(0)) int x=X, y=0; kbrk(&x) - kbrk(&y);
enlarge heap by N bytes . sbrk(+N) int x=N; kbrk(&x);
shrink heap by N bytes . sbrk(-N) int x=-N; kbrk(&x);
can you tell if you're
given more memory
than you wanted? no no yes
*****************************************************************************/
static void *kbrk(int *delta)
{
static char heap[HEAP_SIZE];
/**/
char *new_brk, *old_brk;
/* heap doesn't exist yet */
if(g_heap_bot == NULL)
{
g_heap_bot = g_kbrk = heap;
g_heap_top = g_heap_bot + HEAP_SIZE;
}
new_brk = g_kbrk + (*delta);
/* too low: return NULL */
if(new_brk < g_heap_bot)
return NULL;
/* too high: return NULL */
if(new_brk >= g_heap_top)
return NULL;
/* success: adjust brk value... */
old_brk = g_kbrk;
g_kbrk = new_brk;
/* ...return actual delta... (for this sbrk(), they are the same)
(*delta) = (*delta); */
/* ...return old brk value */
return old_brk;
}
/*****************************************************************************
kmalloc() and kfree() use g_heap_bot, but not g_kbrk nor g_heap_top
*****************************************************************************/
void *kmalloc(size_t size)
{
unsigned total_size;
malloc_t *m, *n;
int delta;
if(size == 0)
return NULL;
total_size = size + sizeof(malloc_t);
/* search heap for free block (FIRST FIT) */
m = (malloc_t *)g_heap_bot;
/* g_heap_bot == 0 == NULL if heap does not yet exist */
if(m != NULL)
{
if(m->magic != MALLOC_MAGIC)
// panic("kernel heap is corrupt in kmalloc()");
{
printf("*** kernel heap is corrupt in kmalloc()\n");
return NULL;
}
for(; m->next != NULL; m = m->next)
{
if(m->used)
continue;
/* size == m->size is a perfect fit */
if(size == m->size)
m->used = 1;
else
{
/* otherwise, we need an extra sizeof(malloc_t) bytes for the header
of a second, free block */
if(total_size > m->size)
continue;
/* create a new, smaller free block after this one */
n = (malloc_t *)((char *)m + total_size);
n->size = m->size - total_size;
n->next = m->next;
n->magic = MALLOC_MAGIC;
n->used = 0;
/* reduce the size of this block and mark it used */
m->size = size;
m->next = n;
m->used = 1;
}
return (char *)m + sizeof(malloc_t);
}
}
/* use kbrk() to enlarge (or create!) heap */
delta = total_size;
n = kbrk(&delta);
/* uh-oh */
if(n == NULL)
return NULL;
if(m != NULL)
m->next = n;
n->size = size;
n->magic = MALLOC_MAGIC;
n->used = 1;
/* did kbrk() return the exact amount of memory we wanted?
cast to make "gcc -Wall -W ..." shut the hell up */
if((int)total_size == delta)
n->next = NULL;
else
{
/* it returned more than we wanted (it will never return less):
create a new, free block */
m = (malloc_t *)((char *)n + total_size);
m->size = delta - total_size - sizeof(malloc_t);
m->next = NULL;
m->magic = MALLOC_MAGIC;
m->used = 0;
n->next = m;
}
return (char *)n + sizeof(malloc_t);
}
/*****************************************************************************
*****************************************************************************/
void kfree(void *blk)
{
malloc_t *m, *n;
/* get address of header */
m = (malloc_t *)((char *)blk - sizeof(malloc_t));
if(m->magic != MALLOC_MAGIC)
// panic("attempt to kfree() block at 0x%p "
// "with bad magic value", blk);
{
printf("*** attempt to kfree() block at 0x%p "
"with bad magic value\n", blk);
BUG();
return;
}
/* find this block in the heap */
n = (malloc_t *)g_heap_bot;
if(n->magic != MALLOC_MAGIC)
// panic("kernel heap is corrupt in kfree()");
{
printf("*** kernel heap is corrupt in kfree()\n");
return;
}
for(; n != NULL; n = n->next)
{
if(n == m)
break;
}
/* not found? bad pointer or no heap or something else? */
if(n == NULL)
// panic("attempt to kfree() block at 0x%p "
// "that is not in the heap", blk);
{
printf("*** attempt to kfree() block at 0x%p "
"that is not in the heap\n", blk);
return;
}
/* free the block */
m->used = 0;
/* BB: Addition: put 0xFF to block memory so we know if we use freed memory */
memset(blk, 0xFF, m->size);
/* coalesce adjacent free blocks
Hard to spell, hard to do */
for(m = (malloc_t *)g_heap_bot; m != NULL; m = m->next)
{
while(!m->used && m->next != NULL && !m->next->used)
{
/* resize this block */
m->size += sizeof(malloc_t) + m->next->size;
/* merge with next block */
m->next = m->next->next;
}
}
}
/*****************************************************************************
*****************************************************************************/
void *krealloc(void *blk, size_t size)
{
void *new_blk;
malloc_t *m;
/* size == 0: free block */
if(size == 0)
{
if(blk != NULL)
kfree(blk);
new_blk = NULL;
}
else
{
/* allocate new block */
new_blk = kmalloc(size);
/* if allocation OK, and if old block exists, copy old block to new */
if(new_blk != NULL && blk != NULL)
{
m = (malloc_t *)((char *)blk - sizeof(malloc_t));
if(m->magic != MALLOC_MAGIC)
// panic("attempt to krealloc() block at "
// "0x%p with bad magic value", blk);
{
printf("*** attempt to krealloc() block at "
"0x%p with bad magic value\n", blk);
return NULL;
}
/* copy minimum of old and new block sizes */
if(size > m->size)
size = m->size;
memcpy(new_blk, blk, size);
/* free the old block */
kfree(blk);
}
}
return new_blk;
}
/*****************************************************************************
*****************************************************************************/
#if 0
#include <stdlib.h> /* rand() */
#define SLOTS 17
int main(void)
{
unsigned lifetime[SLOTS];
void *blk[SLOTS];
int i, j, k;
dump_heap();
memset(lifetime, 0, sizeof(lifetime));
memset(blk, 0, sizeof(blk));
for(i = 0; i < 1000; i++)
{
printf("Pass %6u\n", i);
for(j = 0; j < SLOTS; j++)
{
/* age the block */
if(lifetime[j] != 0)
{
(lifetime[j])--;
continue;
}
/* too old; free it */
if(blk[j] != NULL)
{
kfree(blk[j]);
blk[j] = NULL;
}
/* alloc new block of random size
Note that size_t==unsigned, but kmalloc() uses integer math,
so block size must be positive integer */
#if defined(_32BIT)
k = rand() % 40960 + 1;
#else
k = rand() % 4096 + 1;
#endif
blk[j] = kmalloc(k);
if(blk[j] == NULL)
printf("failed to alloc %u bytes\n", k);
else
/* give it a random lifetime 0-20 */
lifetime[j] = rand() % 21;
}
}
/* let's see what we've wrought */
printf("\n\n");
dump_heap();
/* free everything */
for(j = 0; j < SLOTS; j++)
{
if(blk[j] != NULL)
{
kfree(blk[j]);
blk[j] = NULL;
}
(lifetime[j]) = 0;
}
/* after all that, we should have a single, unused block */
dump_heap();
return 0;
}
/*****************************************************************************
*****************************************************************************/
int main(void)
{
void *b1, *b2, *b3;
dump_heap();
b1 = kmalloc(42);
dump_heap();
b2 = kmalloc(23);
dump_heap();
b3 = kmalloc(7);
dump_heap();
b2 = krealloc(b2, 24);
dump_heap();
kfree(b1);
dump_heap();
b1 = kmalloc(5);
dump_heap();
kfree(b2);
dump_heap();
kfree(b3);
dump_heap();
kfree(b1);
dump_heap();
return 0;
}
#endif

View File

@@ -0,0 +1,203 @@
/*
* Initialise the memory structures.
*
* Copyright (C) 2007, 2008 Bahadir Balban
*/
#include <init.h>
#include <memory.h>
#include <l4/macros.h>
#include <l4/config.h>
#include <l4/types.h>
#include <l4/api/errno.h>
#include <l4/generic/space.h>
#include <l4lib/arch/syslib.h>
#include INC_GLUE(memory.h)
#include INC_SUBARCH(mm.h)
#include <memory.h>
#include <file.h>
#include <user.h>
struct address_pool pager_vaddr_pool;
/* FIXME:
* ID pool id allocation size (i.e. bitlimit/nwords parameters)
* must be in sync with address pool allocation range. Here, since
* the id pool needs to be determined at compile time, the two
* parameters don't match yet.
*/
/* Bitmap size to represent an address pool of 256 MB. */
#define ADDRESS_POOL_256MB 2048
/* Same as a regular id pool except that its bitmap size is fixed */
static struct pager_virtual_address_id_pool {
int nwords;
int bitlimit;
u32 bitmap[ADDRESS_POOL_256MB];
} pager_virtual_address_id_pool = {
.nwords = ADDRESS_POOL_256MB,
.bitlimit = ADDRESS_POOL_256MB * 32,
};
/* End of pager image */
extern unsigned char _end[];
/* For supplying contiguous virtual addresses to pager */
int pager_address_pool_init(void)
{
/*
* Initialise id pool for pager virtual address
* allocation. This spans from end of pager image
* until the end of the virtual address range
* allocated for the pager
*/
address_pool_init_with_idpool(&pager_vaddr_pool,
(struct id_pool *)
&pager_virtual_address_id_pool,
page_align_up((unsigned long)_end + PAGE_SIZE),
(unsigned long)0xF0000000);
return 0;
}
void *l4_new_virtual(int npages)
{
return pager_new_address(npages);
}
void *l4_del_virtual(void *virt, int npages)
{
pager_delete_address(virt, npages);
return 0;
}
/* Maps a page from a vm_file to the pager's address space */
void *pager_map_page(struct vm_file *f, unsigned long page_offset)
{
int err;
struct page *p;
if ((err = read_file_pages(f, page_offset, page_offset + 1)) < 0)
return PTR_ERR(err);
if ((p = find_page(&f->vm_obj, page_offset)))
return (void *)l4_map_helper((void *)page_to_phys(p), 1);
else
return 0;
}
/* Unmaps a page's virtual address from the pager's address space */
void pager_unmap_page(void *addr)
{
l4_unmap_helper(addr, 1);
}
void *pager_new_address(int npages)
{
return address_new(&pager_vaddr_pool, npages);
}
int pager_delete_address(void *virt_addr, int npages)
{
return address_del(&pager_vaddr_pool, virt_addr, npages);
}
/* Maps a page from a vm_file to the pager's address space */
void *pager_map_pages(struct vm_file *f, unsigned long page_offset, unsigned long npages)
{
int err;
struct page *p;
void *addr_start, *addr;
/* Get the pages */
if ((err = read_file_pages(f, page_offset, page_offset + npages)) < 0)
return PTR_ERR(err);
/* Get the address range */
if (!(addr_start = pager_new_address(npages)))
return PTR_ERR(-ENOMEM);
addr = addr_start;
/* Map pages contiguously one by one */
for (unsigned long pfn = page_offset; pfn < page_offset + npages; pfn++) {
BUG_ON(!(p = find_page(&f->vm_obj, pfn)))
l4_map((void *)page_to_phys(p), addr, 1, MAP_USR_RW_FLAGS, self_tid());
addr += PAGE_SIZE;
}
return addr_start;
}
/* Unmaps a page's virtual address from the pager's address space */
void pager_unmap_pages(void *addr, unsigned long npages)
{
/* Align to page if unaligned */
if (!is_page_aligned(addr))
addr = (void *)page_align(addr);
/* Unmap so many pages */
l4_unmap_helper(addr, npages);
}
/*
* Maps multiple pages on a contiguous virtual address range,
* returns pointer to byte offset in the file.
*/
void *pager_map_file_range(struct vm_file *f, unsigned long byte_offset,
unsigned long size)
{
unsigned long mapsize = (byte_offset & PAGE_MASK) + size;
void *page = pager_map_pages(f, __pfn(byte_offset), __pfn(page_align_up(mapsize)));
return (void *)((unsigned long)page | (PAGE_MASK & byte_offset));
}
void *pager_validate_map_user_range2(struct tcb *user, void *userptr,
unsigned long size, unsigned int vm_flags)
{
unsigned long start = page_align(userptr);
unsigned long end = page_align_up(userptr + size);
unsigned long npages = __pfn(end - start);
void *virt, *virt_start;
void *mapped = 0;
/* Validate that user task owns this address range */
if (pager_validate_user_range(user, userptr, size, vm_flags) < 0)
return 0;
/* Get the address range */
if (!(virt_start = pager_new_address(npages)))
return PTR_ERR(-ENOMEM);
virt = virt_start;
/* Map every page contiguously in the allocated virtual address range */
for (unsigned long addr = start; addr < end; addr += PAGE_SIZE) {
struct page *p = task_virt_to_page(user, addr);
if (IS_ERR(p)) {
/* Unmap pages mapped so far */
l4_unmap_helper(virt_start, __pfn(addr - start));
/* Delete virtual address range */
pager_delete_address(virt_start, npages);
return p;
}
l4_map((void *)page_to_phys(task_virt_to_page(user, addr)),
virt, 1, MAP_USR_RW_FLAGS, self_tid());
virt += PAGE_SIZE;
}
/* Set the mapped pointer to offset of user pointer given */
mapped = virt_start;
mapped = (void *)(((unsigned long)mapped) |
((unsigned long)(PAGE_MASK &
(unsigned long)userptr)));
/* Return the mapped pointer */
return mapped;
}

424
conts/posix/mm0/src/mmap.c Normal file
View File

@@ -0,0 +1,424 @@
/*
* mmap/munmap and friends.
*
* Copyright (C) 2007, 2008 Bahadir Balban
*/
#include <l4/lib/math.h>
#include <vm_area.h>
#include <lib/malloc.h>
#include INC_API(errno.h)
#include <posix/sys/types.h>
#include <l4lib/arch/syscalls.h>
#include <l4lib/arch/syslib.h>
#include <memory.h>
#include <task.h>
#include <mmap.h>
#include <file.h>
#include <shm.h>
#include <syscalls.h>
#include <user.h>
#include <shm.h>
struct vm_area *vma_new(unsigned long pfn_start, unsigned long npages,
unsigned int flags, unsigned long file_offset)
{
struct vm_area *vma;
/* Allocate new area */
if (!(vma = kzalloc(sizeof(struct vm_area))))
return 0;
link_init(&vma->list);
link_init(&vma->vm_obj_list);
vma->pfn_start = pfn_start;
vma->pfn_end = pfn_start + npages;
vma->flags = flags;
vma->file_offset = file_offset;
return vma;
}
/*
* Inserts a new vma to the ordered vm area list.
*
* The new vma is assumed to have been correctly set up not to intersect
* with any other existing vma.
*/
int task_insert_vma(struct vm_area *this, struct link *vma_list)
{
struct vm_area *before, *after;
/* Add if list is empty */
if (list_empty(vma_list)) {
list_insert_tail(&this->list, vma_list);
return 0;
}
/* Else find the right interval */
list_foreach_struct(before, vma_list, list) {
after = link_to_struct(before->list.next, struct vm_area, list);
/* If there's only one in list */
if (before->list.next == vma_list) {
/* Eliminate the possibility of intersection */
BUG_ON(set_intersection(this->pfn_start, this->pfn_end,
before->pfn_start,
before->pfn_end));
/* Add as next if greater */
if (this->pfn_start > before->pfn_start)
list_insert(&this->list, &before->list);
/* Add as previous if smaller */
else if (this->pfn_start < before->pfn_start)
list_insert_tail(&this->list, &before->list);
else
BUG();
return 0;
}
/* If this page is in-between two other, insert it there */
if (before->pfn_start < this->pfn_start &&
after->pfn_start > this->pfn_start) {
/* Eliminate possibility of intersection */
BUG_ON(set_intersection(this->pfn_start, this->pfn_end,
before->pfn_start,
before->pfn_end));
BUG_ON(set_intersection(this->pfn_start, this->pfn_end,
after->pfn_start,
after->pfn_end));
list_insert(&this->list, &before->list);
return 0;
}
}
BUG();
}
/*
* Search an empty space in the task's mmapable address region.
*
* This does a less than O(n) algorithm by starting the estimated region
* and vma comparison from the beginning, once a vma is not intersected
* that means it is an available slot. However if vma's and estimated
* region does not go head-to-head for comparison, individual intersection
* checks would be meaningless since any other vma could be intersecting.
* Therefore head-to-head comparison is essential here.
*/
unsigned long find_unmapped_area(unsigned long npages, struct tcb *task)
{
unsigned long pfn_start = __pfn(task->start);
unsigned long pfn_end = pfn_start + npages;
struct vm_area *vma;
if (npages > __pfn(task->end - task->start))
return 0;
/* If no vmas, first map slot is available. */
if (list_empty(&task->vm_area_head->list))
return task->start;
/* First vma to check our range against */
vma = link_to_struct(task->vm_area_head->list.next, struct vm_area, list);
/* Start searching from task's end of data to start of stack */
while (pfn_end <= __pfn(task->end)) {
/* If intersection, skip the vma and fast-forward to next */
if (set_intersection(pfn_start, pfn_end,
vma->pfn_start, vma->pfn_end)) {
/* Update interval to next available space */
pfn_start = vma->pfn_end;
pfn_end = pfn_start + npages;
/*
* Decision point, no more vmas left to check.
* Are we out of task map area?
*/
if (vma->list.next == &task->vm_area_head->list) {
if (pfn_end > __pfn(task->end))
break; /* Yes, fail */
else /* No, success */
return __pfn_to_addr(pfn_start);
}
/* Otherwise get next vma entry */
vma = link_to_struct(vma->list.next,
struct vm_area, list);
continue;
}
BUG_ON(pfn_start + npages > __pfn(task->end));
return __pfn_to_addr(pfn_start);
}
return 0;
}
/* Validate an address that is a possible candidate for an mmap() region */
int mmap_address_validate(struct tcb *task, unsigned long map_address,
unsigned int vm_flags)
{
if (map_address == 0)
return 0;
/* Private mappings can only go in task address space */
if (vm_flags & VMA_PRIVATE) {
if ((map_address >= task->start &&
map_address < task->end) ||
(map_address >= UTCB_AREA_START &&
map_address < UTCB_AREA_END)) {
return 1;
} else
return 0;
/*
* Shared mappings can go in task, utcb, and shared
* memory address space,
*/
} else if (vm_flags & VMA_SHARED) {
if ((map_address >= task->start &&
map_address < task->end) ||
(map_address >= SHM_AREA_START &&
map_address < SHM_AREA_END))
return 1;
else
return 0;
} else
BUG();
}
/*
* Returns a suitably mmap'able address. It allocates
* differently for shared and private areas.
*/
unsigned long mmap_new_address(struct tcb *task, unsigned int flags,
unsigned int npages)
{
if (flags & VMA_SHARED)
return (unsigned long)shm_new_address(npages);
else
return find_unmapped_area(npages, task);
}
/*
* Side note:
* Why in do_mmap() shm files have devzero mapped behind separately but
* anonymous files map devzero directly? Because private anonymous files get
* shadow objects in front when written to. Shm files are not private, so they
* stay where they are and just grow. Other processes can reach and map them.
*/
/*
* Maps the given file with given flags at the given page offset to the given
* task's address space at the specified virtual memory address and length.
*
* The actual paging in/out of the file from/into memory pages is handled by
* the file's pager upon page faults.
*/
void *do_mmap(struct vm_file *mapfile, unsigned long file_offset,
struct tcb *task, unsigned long map_address,
unsigned int flags, unsigned int npages)
{
struct vm_obj_link *vmo_link, *vmo_link2;
unsigned long file_npages;
struct vm_area *new;
int err;
/* Set up devzero if none given */
if (!mapfile) {
if (flags & VMA_ANONYMOUS) {
BUG_ON(!(mapfile = get_devzero()));
file_offset = 0;
} else
return PTR_ERR(-EINVAL);
}
/* Get total file pages, check if mapping is within file size */
file_npages = __pfn(page_align_up(mapfile->length));
if (npages > file_npages - file_offset) {
printf("%s: Trying to map %d pages from page %lu, "
"but file length is %lu\n", __FUNCTION__,
npages, file_offset, file_npages);
return PTR_ERR(-EINVAL);
}
/* Check invalid page size */
if (npages == 0) {
printf("Trying to map %d pages.\n", npages);
return PTR_ERR(-EINVAL);
}
if (npages > __pfn(TASK_SIZE)) {
printf("Trying to map too many pages: %d\n", npages);
return PTR_ERR(-ENOMEM);
}
/* Check invalid map address */
if (!mmap_address_validate(task, map_address, flags)) {
if (flags & VMA_FIXED)
return PTR_ERR(-EINVAL);
else if (!(map_address = mmap_new_address(task, flags, npages)))
return PTR_ERR(-ENOMEM);
}
/* Unmap any existing vmas that overlap with the new mapping */
if ((err = do_munmap(task, map_address, npages)) < 0)
return PTR_ERR(err);
/* For valid regions that aren't allocated by us, create the vma. */
if (!(new = vma_new(__pfn(map_address), npages, flags,
__pfn(file_offset))))
return PTR_ERR(-ENOMEM);
/* Attach the file as the first vm object of this vma */
if (!(vmo_link = vm_objlink_create())) {
kfree(new);
return PTR_ERR(-ENOMEM);
}
/* Attach link to object */
vm_link_object(vmo_link, &mapfile->vm_obj);
/* Add link to vma list */
list_insert_tail(&vmo_link->list, &new->vm_obj_list);
/*
* If the file is a shm file, also map devzero behind it. i.e.
* vma -> vm_link -> vm_link
* | |
* v v
* shm_file devzero
*
* So that faults go through shm file and then devzero, as in
* the shadow object copy_on_write setup in fault.c
*/
if (mapfile->type == VM_FILE_SHM) {
struct vm_file *dzero = get_devzero();
/* Attach the file as the first vm object of this vma */
if (!(vmo_link2 = vm_objlink_create())) {
kfree(new);
kfree(vmo_link);
return PTR_ERR(-ENOMEM);
}
vm_link_object(vmo_link2, &dzero->vm_obj);
list_insert_tail(&vmo_link2->list, &new->vm_obj_list);
}
/* Finished initialising the vma, add it to task */
dprintf("%s: Mapping 0x%x - 0x%x\n", __FUNCTION__,
map_address, map_address + __pfn_to_addr(npages));
task_insert_vma(new, &task->vm_area_head->list);
/*
* If area is going to be used going downwards, (i.e. as a stack)
* we return the *end* of the area as the start address.
*/
if (flags & VMA_GROWSDOWN)
map_address += __pfn_to_addr(npages);
return (void *)map_address;
}
/* mmap system call implementation */
void *__sys_mmap(struct tcb *task, void *start, size_t length, int prot,
int flags, int fd, unsigned long file_offset)
{
unsigned int vmflags = 0;
struct vm_file *file = 0;
int err;
/* Check file validity */
if (!(flags & MAP_ANONYMOUS))
if (!task->files->fd[fd].vmfile)
if ((err = file_open(task, fd)) < 0)
return PTR_ERR(err);
/* Check file offset is page aligned */
if (!is_page_aligned(file_offset))
return PTR_ERR(-EINVAL);
/* TODO:
* Check that @start does not already have a mapping.
* Check that pfn + npages range is within the file range.
* Check that posix flags passed match those defined in vm_area.h
*/
if (flags & MAP_ANONYMOUS) {
file = 0;
vmflags |= VMA_ANONYMOUS;
} else {
file = task->files->fd[fd].vmfile;
}
if (flags & MAP_FIXED)
vmflags |= VMA_FIXED;
if (flags & MAP_PRIVATE)
/* This means COW, if writeable. */
vmflags |= VMA_PRIVATE;
else /* This also means COW, if writeable and anonymous */
vmflags |= VMA_SHARED;
if (flags & MAP_GROWSDOWN)
vmflags |= VMA_GROWSDOWN;
if (prot & PROT_READ)
vmflags |= VM_READ;
if (prot & PROT_WRITE)
vmflags |= VM_WRITE;
if (prot & PROT_EXEC)
vmflags |= VM_EXEC;
/*
* Currently MAP_SHARED && MAP_ANONYMOUS mappings use the
* shm interface to create virtual shared memory files and
* do_mmap is internally called through this interface.
*
* FIXME: A common method of creating virtual shm files
* should be used by both sys_mmap and sys_shmget. With the
* current method, a task that guesses the shmid of an
* anonymous shared mmap can attach to it via shmat.
*/
if ((flags & MAP_ANONYMOUS) &&
(flags & MAP_SHARED)) {
/* Create a new shared memory virtual file */
l4id_t shmid = sys_shmget(IPC_PRIVATE,
page_align_up(length),
0);
/* Find and mmap the file via do_shmat() */
return sys_shmat(task, shmid, 0, 0);
}
return do_mmap(file, file_offset, task, (unsigned long)start,
vmflags, __pfn(page_align_up(length)));
}
void *sys_mmap(struct tcb *task, struct sys_mmap_args *args)
{
struct sys_mmap_args *mapped_args;
void *ret;
if (!(mapped_args = pager_validate_map_user_range(task, args,
sizeof(*args),
VM_READ | VM_WRITE)))
return PTR_ERR(-EINVAL);
ret = __sys_mmap(task, mapped_args->start, mapped_args->length,
mapped_args->prot, mapped_args->flags, mapped_args->fd,
mapped_args->offset);
pager_unmap_user_range(mapped_args, sizeof(*args));
return ret;
}
/* Sets the end of data segment for sender */
int sys_brk(struct tcb *sender, void *ds_end)
{
return 0;
}

View File

@@ -0,0 +1,261 @@
/*
* munmap() for unmapping a portion of an address space.
*
* Copyright (C) 2008 Bahadir Balban
*/
#include <mmap.h>
#include <file.h>
#include <l4/api/errno.h>
#include <l4/lib/math.h>
#include <l4lib/arch/syslib.h>
#include <vm_area.h>
#include <lib/malloc.h>
/* This splits a vma, splitter region must be in the *middle* of original vma */
int vma_split(struct vm_area *vma, struct tcb *task,
const unsigned long pfn_start, const unsigned long pfn_end)
{
struct vm_area *new;
unsigned long unmap_start = pfn_start, unmap_end = pfn_end;
/* Allocate an uninitialised vma first */
if (!(new = vma_new(0, 0, 0, 0)))
return -ENOMEM;
/*
* Some sanity checks to show that splitter range does end up
* producing two smaller vmas.
*/
BUG_ON(vma->pfn_start >= pfn_start || vma->pfn_end <= pfn_end);
/* Update new and original vmas */
new->pfn_end = vma->pfn_end;
new->pfn_start = pfn_end;
new->file_offset = vma->file_offset + new->pfn_start - vma->pfn_start;
vma->pfn_end = pfn_start;
new->flags = vma->flags;
/*
* Copy the object links of original vma to new vma. A split like this
* increases the map count of mapped object(s) since now 2 vmas on the
* same task maps the same object(s).
*/
vma_copy_links(new, vma);
/* Add new one next to original vma */
list_insert_tail(&new->list, &vma->list);
/* Unmap the removed portion */
BUG_ON(l4_unmap((void *)__pfn_to_addr(unmap_start),
unmap_end - unmap_start, task->tid) < 0);
return 0;
}
/* This shrinks the vma from *one* end only, either start or end */
int vma_shrink(struct vm_area *vma, struct tcb *task,
const unsigned long pfn_start, const unsigned long pfn_end)
{
unsigned long diff, unmap_start, unmap_end;
int err;
/* Shrink from the end */
if (vma->pfn_start < pfn_start) {
BUG_ON(pfn_start >= vma->pfn_end);
unmap_start = pfn_start;
unmap_end = vma->pfn_end;
vma->pfn_end = pfn_start;
/* Shrink from the beginning */
} else if (vma->pfn_end > pfn_end) {
BUG_ON(pfn_end <= vma->pfn_start);
unmap_start = vma->pfn_start;
unmap_end = pfn_end;
diff = pfn_end - vma->pfn_start;
vma->file_offset += diff;
vma->pfn_start = pfn_end;
} else
BUG();
/* Unmap the shrinked portion */
BUG_ON((err = l4_unmap((void *)__pfn_to_addr(unmap_start),
unmap_end - unmap_start, task->tid)) < 0);
return 0;
}
/* Destroys a single vma from a task and unmaps its range from task space */
int vma_destroy_single(struct tcb *task, struct vm_area *vma)
{
int ret;
/* Release all object links */
if ((ret = vma_drop_merge_delete_all(vma)) < 0)
return ret;
/*
* Unmap the whole vma address range. Note that this
* may return -1 if the area was already faulted, which
* means the area was unmapped before being touched.
*/
l4_unmap((void *)__pfn_to_addr(vma->pfn_start),
vma->pfn_end - vma->pfn_start, task->tid);
/* Unlink and delete vma */
list_remove(&vma->list);
kfree(vma);
return 0;
}
/*
* Unmaps the given region from a vma. Depending on the region and vma range,
* this may result in either shrinking, splitting or destruction of the vma.
*/
int vma_unmap(struct vm_area *vma, struct tcb *task,
const unsigned long pfn_start, const unsigned long pfn_end)
{
// printf("Unmapping vma. Tid: %d, 0x%x-0x%x\n",task->tid, __pfn_to_addr(pfn_start), __pfn_to_addr(pfn_end));
/* Split needed? */
if (vma->pfn_start < pfn_start && vma->pfn_end > pfn_end)
return vma_split(vma, task, pfn_start, pfn_end);
/* Shrink needed? */
else if (((vma->pfn_start >= pfn_start) && (vma->pfn_end > pfn_end))
|| ((vma->pfn_start < pfn_start) && (vma->pfn_end <= pfn_end)))
return vma_shrink(vma, task, pfn_start, pfn_end);
/* Destroy needed? */
else if ((vma->pfn_start >= pfn_start) && (vma->pfn_end <= pfn_end))
return vma_destroy_single(task, vma);
else
BUG();
return 0;
}
/* Checks vma and vm_object type and flushes its pages accordingly */
int vma_flush_pages(struct vm_area *vma)
{
struct vm_object *vmo;
struct vm_obj_link *vmo_link;
int err;
/* Read-only vmas need not flush objects */
if (!(vma->flags & VM_WRITE))
return 0;
/*
* We just check the first object under the vma, since there
* could only be a single VM_SHARED file-backed object in the chain.
*/
BUG_ON(list_empty(&vma->list));
vmo_link = link_to_struct(vma->vm_obj_list.next, struct vm_obj_link, list);
vmo = vmo_link->obj;
/* Only dirty objects would need flushing */
if (!(vmo->flags & VM_DIRTY))
return 0;
/* Only vfs file objects are flushed */
if (vmo->flags & VM_OBJ_FILE &&
vmo->flags & VMA_SHARED &&
!(vmo->flags & VMA_ANONYMOUS)) {
/* Only vfs files ought to match above criteria */
BUG_ON(vm_object_to_file(vmo)->type != VM_FILE_VFS);
/* Flush the pages */
if ((err = flush_file_pages(vm_object_to_file(vmo))) < 0)
return err;
}
return 0;
}
/*
* Unmaps the given virtual address range from the task, the region
* may span into zero or more vmas, and may involve shrinking, splitting
* and destruction of multiple vmas.
*
* NOTE: Shared object addresses are returned back to their pools when
* such objects are deleted, and not via this function.
*/
int do_munmap(struct tcb *task, unsigned long vaddr, unsigned long npages)
{
const unsigned long munmap_start = __pfn(vaddr);
const unsigned long munmap_end = munmap_start + npages;
struct vm_area *vma, *n;
int err;
list_foreach_removable_struct(vma, n, &task->vm_area_head->list, list) {
/* Check for intersection */
if (set_intersection(munmap_start, munmap_end,
vma->pfn_start, vma->pfn_end)) {
/*
* Flush pages if vma is writable,
* dirty and file-backed.
*/
if ((err = vma_flush_pages(vma)) < 0)
return err;
/* Unmap the vma accordingly. This may delete the vma */
if ((err = vma_unmap(vma, task, munmap_start,
munmap_end)) < 0)
return err;
}
}
return 0;
}
int sys_munmap(struct tcb *task, void *start, unsigned long length)
{
/* Must be aligned on a page boundary */
if (!is_page_aligned(start))
return -EINVAL;
return do_munmap(task, (unsigned long)start,
__pfn(page_align_up(length)));
}
/* Syncs mapped area. Currently just synchronously */
int do_msync(struct tcb *task, void *vaddr, unsigned long npages, int flags)
{
const unsigned long msync_start = __pfn(vaddr);
const unsigned long msync_end = msync_start + npages;
struct vm_area *vma;
unsigned long addr = (unsigned long)vaddr;
int err;
/* Find a vma that overlaps with this address range */
while ((vma = find_vma(addr, &task->vm_area_head->list))) {
/* Flush pages if vma is writable, dirty and file-backed. */
if ((err = vma_flush_pages(vma)) < 0)
return err;
/* Update address to next vma */
addr = __pfn_to_addr(vma->pfn_end);
/* Are we still good to go? */
if (addr >= msync_end)
break;
}
return 0;
}
int sys_msync(struct tcb *task, void *start, unsigned long length, int flags)
{
/* Must be aligned on a page boundary */
if (!is_page_aligned(start))
return -EINVAL;
/*
* TODO: We need to pass sync'ed and non-sync'ed file flushes to vfs
* and support synced and non-synced io.
*/
return do_msync(task, start, __pfn(page_align_up(length)), flags);
}

View File

@@ -0,0 +1,391 @@
/*
* Copyright (C) 2008 Bahadir Balban
*/
#include <l4/macros.h>
#include <l4/lib/list.h>
#include <l4lib/arch/syscalls.h>
#include <l4lib/arch/syslib.h>
#include <lib/malloc.h>
#include <mm/alloc_page.h>
#include <vm_area.h>
#include <string.h>
#include <globals.h>
#include <file.h>
#include <init.h>
#include <l4/api/errno.h>
struct page *page_init(struct page *page)
{
/* Reset page */
memset(page, 0, sizeof(*page));
page->refcnt = -1;
spin_lock_init(&page->lock);
link_init(&page->list);
return page;
}
struct page *find_page(struct vm_object *obj, unsigned long pfn)
{
struct page *p;
list_foreach_struct(p, &obj->page_cache, list)
if (p->offset == pfn)
return p;
return 0;
}
/*
* Deletes all pages in a page cache, assumes pages are from the
* page allocator, and page structs are from the page_array, which
* is the default situation.
*/
int default_release_pages(struct vm_object *vm_obj)
{
struct page *p, *n;
list_foreach_removable_struct(p, n, &vm_obj->page_cache, list) {
list_remove_init(&p->list);
BUG_ON(p->refcnt);
/* Reinitialise the page */
page_init(p);
/* Return page back to allocator */
free_page((void *)page_to_phys(p));
/* Reduce object page count */
BUG_ON(--vm_obj->npages < 0);
}
return 0;
}
int file_page_out(struct vm_object *vm_obj, unsigned long page_offset)
{
struct vm_file *f = vm_object_to_file(vm_obj);
struct page *page;
void *vaddr, *paddr;
int err;
/* Check first if the file has such a page at all */
if (__pfn(page_align_up(f->length) <= page_offset)) {
printf("%s: %s: Trying to look up page %lu, but file length "
"is %lu bytes.\n", __TASKNAME__, __FUNCTION__,
page_offset, f->length);
BUG();
}
/* If the page is not in the page cache, simply return. */
if (!(page = find_page(vm_obj, page_offset)))
return 0;
/* If the page is not dirty, simply return */
if (!(page->flags & VM_DIRTY))
return 0;
paddr = (void *)page_to_phys(page);
vaddr = l4_new_virtual(1);
/* Map the page to vfs task */
l4_map(paddr, vaddr, 1, MAP_USR_RW_FLAGS, VFS_TID);
// printf("%s/%s: Writing to vnode %d, at pgoff 0x%x, %d pages, buf at 0x%x\n",
// __TASKNAME__, __FUNCTION__, vm_file_to_vnum(f), page_offset, 1, vaddr);
/* Syscall to vfs to write page back to file. */
if ((err = vfs_write(vm_file_to_vnum(f), page_offset, 1, vaddr)) < 0)
goto out_err;
/* Unmap it from vfs */
l4_unmap(vaddr, 1, VFS_TID);
l4_del_virtual(vaddr, 1);
/* Clear dirty flag */
page->flags &= ~VM_DIRTY;
return 0;
out_err:
l4_unmap(vaddr, 1, VFS_TID);
l4_del_virtual(vaddr, 1);
return err;
}
struct page *file_page_in(struct vm_object *vm_obj, unsigned long page_offset)
{
struct vm_file *f = vm_object_to_file(vm_obj);
struct page *page;
void *vaddr, *paddr;
int err;
/* Check first if the file has such a page at all */
if (__pfn(page_align_up(f->length) <= page_offset)) {
printf("%s: %s: Trying to look up page %lu, but file length "
"is %lu bytes.\n", __TASKNAME__, __FUNCTION__,
page_offset, f->length);
BUG();
}
/* Call vfs only if the page is not resident in page cache. */
if (!(page = find_page(vm_obj, page_offset))) {
/* Allocate a new page */
paddr = alloc_page(1);
vaddr = l4_new_virtual(1);
page = phys_to_page(paddr);
/* Map the page to vfs task */
l4_map(paddr, vaddr, 1, MAP_USR_RW_FLAGS, VFS_TID);
/* Syscall to vfs to read into the page. */
if ((err = vfs_read(vm_file_to_vnum(f), page_offset,
1, vaddr)) < 0)
goto out_err;
/* Unmap it from vfs */
l4_unmap(vaddr, 1, VFS_TID);
l4_del_virtual(vaddr, 1);
/* Update vm object details */
vm_obj->npages++;
/* Update page details */
page_init(page);
page->refcnt++;
page->owner = vm_obj;
page->offset = page_offset;
page->virtual = 0;
/* Add the page to owner's list of in-memory pages */
BUG_ON(!list_empty(&page->list));
insert_page_olist(page, vm_obj);
}
return page;
out_err:
l4_unmap(vaddr, 1, VFS_TID);
l4_del_virtual(vaddr, 1);
free_page(paddr);
return PTR_ERR(err);
}
/*
* All non-mmapable char devices are handled by this.
* VFS calls those devices to read their pages
*/
struct vm_pager file_pager = {
.ops = {
.page_in = file_page_in,
.page_out = file_page_out,
.release_pages = default_release_pages,
},
};
/* A proposal for shadow vma container, could be part of vm_file->priv_data */
struct vm_swap_node {
struct vm_file *swap_file;
struct task_ids task_ids;
struct address_pool *pool;
};
/*
* This should save swap_node/page information either in the pte or in a global
* list of swap descriptors, and then write the page into the possibly one and
* only swap file.
*/
struct page *swap_page_in(struct vm_object *vm_obj, unsigned long file_offset)
{
struct page *p;
/* No swapping yet, so the page is either here or not here. */
if (!(p = find_page(vm_obj, file_offset)))
return PTR_ERR(-EINVAL);
else
return p;
}
struct vm_pager swap_pager = {
.ops = {
.page_in = swap_page_in,
.release_pages = default_release_pages,
},
};
/*
* Just releases the page structures since the actual pages are
* already in memory as read-only.
*/
int bootfile_release_pages(struct vm_object *vm_obj)
{
struct page *p, *n;
list_foreach_removable_struct(p, n, &vm_obj->page_cache, list) {
list_remove(&p->list);
BUG_ON(p->refcnt);
/* Reinitialise the page */
page_init(p);
/*
* We don't free the page because it doesn't
* come from the page allocator
*/
// free_page((void *)page_to_phys(p));
/* Reduce object page count */
BUG_ON(--vm_obj->npages < 0);
}
return 0;
}
/* Returns the page with given offset in this vm_object */
struct page *bootfile_page_in(struct vm_object *vm_obj,
unsigned long offset)
{
struct vm_file *boot_file = vm_object_to_file(vm_obj);
struct svc_image *img = boot_file->priv_data;
struct page *page;
/* Check first if the file has such a page at all */
if (__pfn(page_align_up(boot_file->length) <= offset)) {
printf("%s: %s: Trying to look up page %lu, but file length "
"is %lu bytes.\n", __TASKNAME__, __FUNCTION__,
offset, boot_file->length);
BUG();
}
/* The page is not resident in page cache. */
if (!(page = find_page(vm_obj, offset))) {
page = phys_to_page(img->phys_start + __pfn_to_addr(offset));
/* Update page */
page_init(page);
page->refcnt++;
page->owner = vm_obj;
page->offset = offset;
/* Update object */
vm_obj->npages++;
/* Add the page to owner's list of in-memory pages */
BUG_ON(!list_empty(&page->list));
insert_page_olist(page, vm_obj);
}
return page;
}
struct vm_pager bootfile_pager = {
.ops = {
.page_in = bootfile_page_in,
.release_pages = bootfile_release_pages,
},
};
void bootfile_destroy_priv_data(struct vm_file *bootfile)
{
}
/* From bare boot images, create mappable device files */
int init_boot_files(struct initdata *initdata)
{
struct bootdesc *bd = initdata->bootdesc;
struct vm_file *boot_file;
struct svc_image *img;
link_init(&initdata->boot_file_list);
for (int i = 0; i < bd->total_images; i++) {
img = &bd->images[i];
boot_file = vm_file_create();
/* Allocate private data */
boot_file->priv_data = kzalloc(sizeof(*img));
memcpy(boot_file->priv_data, img, sizeof(*img));
boot_file->length = img->phys_end - img->phys_start;
boot_file->type = VM_FILE_BOOTFILE;
boot_file->destroy_priv_data =
bootfile_destroy_priv_data;
/* Initialise the vm object */
boot_file->vm_obj.flags = VM_OBJ_FILE;
boot_file->vm_obj.pager = &bootfile_pager;
/* Add the file to initdata's bootfile list */
list_insert_tail(&boot_file->list, &initdata->boot_file_list);
}
return 0;
}
/* Returns the page with given offset in this vm_object */
struct page *devzero_page_in(struct vm_object *vm_obj,
unsigned long page_offset)
{
struct vm_file *devzero = vm_object_to_file(vm_obj);
struct page *zpage = devzero->priv_data;
BUG_ON(!(devzero->type & VM_FILE_DEVZERO));
/* Update zero page struct. */
spin_lock(&zpage->lock);
BUG_ON(zpage->refcnt < 0);
zpage->refcnt++;
spin_unlock(&zpage->lock);
return zpage;
}
struct vm_pager devzero_pager = {
.ops = {
.page_in = devzero_page_in,
},
};
struct vm_file *get_devzero(void)
{
struct vm_file *f;
list_foreach_struct(f, &global_vm_files.list, list)
if (f->type == VM_FILE_DEVZERO)
return f;
return 0;
}
int init_devzero(void)
{
void *zphys, *zvirt;
struct page *zpage;
struct vm_file *devzero;
/* Allocate and initialise the zero page */
zphys = alloc_page(1);
zpage = phys_to_page(zphys);
zvirt = l4_map_helper(zphys, 1);
memset(zvirt, 0, PAGE_SIZE);
l4_unmap_helper(zvirt, 1);
/* Allocate and initialise devzero file */
devzero = vm_file_create();
devzero->type = VM_FILE_DEVZERO;
devzero->priv_data = zpage;
devzero->length = page_align(~0UL); /* So we dont wraparound to 0! */
devzero->vm_obj.npages = __pfn(devzero->length);
devzero->vm_obj.pager = &devzero_pager;
devzero->vm_obj.flags = VM_OBJ_FILE;
/* Initialise zpage */
zpage->refcnt++;
zpage->owner = &devzero->vm_obj;
global_add_vm_file(devzero);
return 0;
}

View File

@@ -0,0 +1,175 @@
/*
* Global physical memory descriptions.
*
* Copyright (C) 2007 - 2009 Bahadir Balban
*/
#include <l4/macros.h>
#include <l4/config.h>
#include <l4/types.h>
#include <l4/lib/list.h>
#include <l4/lib/math.h>
#include <l4/api/thread.h>
#include <l4/api/kip.h>
#include <l4/api/errno.h>
#include INC_GLUE(memory.h)
#include <l4lib/arch/syslib.h>
#include <stdio.h>
#include <init.h>
#include <physmem.h>
#include <bootm.h>
struct memdesc physmem; /* Initial, primitive memory descriptor */
struct membank membank[1]; /* The memory bank */
struct page *page_array; /* The physical page array based on mem bank */
static void init_page_map(struct page_bitmap *pmap, unsigned long pfn_start, unsigned long pfn_end)
{
pmap->pfn_start = pfn_start;
pmap->pfn_end = pfn_end;
set_page_map(pmap, pfn_start, pfn_end - pfn_start, 0);
}
/*
* Marks pages in the global page_map as used or unused.
*
* @start = start page address to set, inclusive.
* @numpages = number of pages to set.
*/
int set_page_map(struct page_bitmap *page_map, unsigned long pfn_start,
int numpages, int val)
{
unsigned long pfn_end = pfn_start + numpages;
unsigned long pfn_err = 0;
if (page_map->pfn_start > pfn_start || page_map->pfn_end < pfn_start) {
pfn_err = pfn_start;
goto error;
}
if (page_map->pfn_end < pfn_end || page_map->pfn_start > pfn_end) {
pfn_err = pfn_end;
goto error;
}
if (val)
for (int i = pfn_start; i < pfn_end; i++)
page_map->map[BITWISE_GETWORD(i)] |= BITWISE_GETBIT(i);
else
for (int i = pfn_start; i < pfn_end; i++)
page_map->map[BITWISE_GETWORD(i)] &= ~BITWISE_GETBIT(i);
return 0;
error:
BUG_MSG("Given page area is out of system page_map range: 0x%lx\n",
pfn_err << PAGE_BITS);
return -1;
}
/* Allocates page descriptors and initialises them using page_map information */
void init_physmem_secondary(struct initdata *initdata, struct membank *membank)
{
struct page_bitmap *pmap = initdata->page_map;
int npages = pmap->pfn_end - pmap->pfn_start;
/* Allocation marks for the struct page array; npages, start, end */
int pg_npages, pg_spfn, pg_epfn;
unsigned long ffree_addr;
/*
* Means the page array won't map one to one to pfns. That's ok,
* but we dont allow it for now.
*/
// BUG_ON(pmap->pfn_start);
membank[0].start = __pfn_to_addr(pmap->pfn_start);
membank[0].end = __pfn_to_addr(pmap->pfn_end);
/* First find the first free page after last used page */
for (int i = 0; i < npages; i++)
if ((pmap->map[BITWISE_GETWORD(i)] & BITWISE_GETBIT(i)))
membank[0].free = (i + 1) * PAGE_SIZE;
BUG_ON(membank[0].free >= membank[0].end);
/*
* One struct page for every physical page. Calculate how many pages
* needed for page structs, start and end pfn marks.
*/
pg_npages = __pfn(page_align_up((sizeof(struct page) * npages)));
/* These are relative pfn offsets to the start of the memory bank */
/* FIXME:
* 1.) These values were only right when membank started from pfn 0.
* 2.) Use set_page_map to set page map below instead of manually.
*/
pg_spfn = __pfn(membank[0].free);
pg_epfn = pg_spfn + pg_npages;
/* Use free pages from the bank as the space for struct page array */
membank[0].page_array = l4_map_helper((void *)membank[0].free,
pg_npages);
/* Update free memory left */
membank[0].free += pg_npages * PAGE_SIZE;
/* Update page bitmap for the pages used for the page array */
for (int i = pg_spfn; i < pg_epfn; i++)
pmap->map[BITWISE_GETWORD(i)] |= BITWISE_GETBIT(i);
/* Initialise the page array */
for (int i = 0; i < npages; i++) {
link_init(&membank[0].page_array[i].list);
/* Set use counts for pages the kernel has already used up */
if (!(pmap->map[BITWISE_GETWORD(i)] & BITWISE_GETBIT(i)))
membank[0].page_array[i].refcnt = -1;
else /* Last page used +1 is free */
ffree_addr = (i + 1) * PAGE_SIZE;
}
/* First free address must come up the same for both */
BUG_ON(ffree_addr != membank[0].free);
/* Set global page array to this bank's array */
page_array = membank[0].page_array;
/* Test that page/phys macros work */
BUG_ON(phys_to_page(page_to_phys(&page_array[5])) != &page_array[5])
}
/* Fills in the physmem structure with free physical memory information */
void init_physmem_primary(struct initdata *initdata)
{
unsigned long pfn_start, pfn_end, pfn_images_end = 0;
struct bootdesc *bootdesc = initdata->bootdesc;
/* Allocate page map structure */
initdata->page_map = alloc_bootmem(sizeof(struct page_bitmap) +
((initdata->physmem->end -
initdata->physmem->start)
>> 5) + 1, 0);
/* Initialise page map from physmem capability */
init_page_map(initdata->page_map,
initdata->physmem->start,
initdata->physmem->end);
/* Mark pager and other boot task areas as used */
for (int i = 0; i < bootdesc->total_images; i++) {
pfn_start =
__pfn(page_align_up(bootdesc->images[i].phys_start));
pfn_end = __pfn(page_align_up(bootdesc->images[i].phys_end));
if (pfn_end > pfn_images_end)
pfn_images_end = pfn_end;
set_page_map(initdata->page_map, pfn_start,
pfn_end - pfn_start, 1);
}
physmem.start = initdata->physmem->start;
physmem.end = initdata->physmem->end;
physmem.free_cur = pfn_images_end;
physmem.free_end = physmem.end;
physmem.numpages = __pfn(physmem.end - physmem.start);
}

381
conts/posix/mm0/src/shm.c Normal file
View File

@@ -0,0 +1,381 @@
/*
* Copyright (C) 2007, 2008 Bahadir Balban
*
* Posix shared memory implementation
*/
#include <shm.h>
#include <stdio.h>
#include <task.h>
#include <mmap.h>
#include <vm_area.h>
#include <globals.h>
#include <lib/malloc.h>
#include <l4lib/arch/syscalls.h>
#include <l4lib/arch/syslib.h>
#include <lib/idpool.h>
#include <lib/addr.h>
#include <lib/spinlock.h>
#include <l4/api/errno.h>
#include <l4/lib/list.h>
#include <l4/macros.h>
#include <l4/config.h>
#include <l4/types.h>
#include INC_GLUE(memlayout.h)
#include <posix/sys/ipc.h>
#include <posix/sys/shm.h>
#include <posix/sys/types.h>
#define shm_file_to_desc(shm_file) \
((struct shm_descriptor *)shm_file->priv_data)
/* Unique shared memory ids */
static struct id_pool *shm_ids;
/* Globally disjoint shm virtual address pool */
static struct address_pool shm_vaddr_pool;
void *shm_new_address(int npages)
{
return address_new(&shm_vaddr_pool, npages);
}
int shm_delete_address(void *shm_addr, int npages)
{
return address_del(&shm_vaddr_pool, shm_addr, npages);
}
int shm_pool_init()
{
int err;
/* Initialise shm id pool */
if(IS_ERR(shm_ids = id_pool_new_init(SHM_AREA_MAX))) {
printf("SHM id pool initialisation failed.\n");
return (int)shm_ids;
}
/* Initialise the global shm virtual address pool */
if ((err = address_pool_init(&shm_vaddr_pool,
SHM_AREA_START, SHM_AREA_END)) < 0) {
printf("SHM Address pool initialisation failed.\n");
return err;
}
return 0;
}
/*
* Attaches to given shm segment mapped at shm_addr if the shm descriptor
* does not already have a base address assigned. If neither shm_addr nor
* the descriptor has an address, allocates one from the shm address pool.
*/
static void *do_shmat(struct vm_file *shm_file, void *shm_addr, int shmflg,
struct tcb *task)
{
struct shm_descriptor *shm = shm_file_to_desc(shm_file);
unsigned int vmflags;
void *mapped;
if (!task) {
printf("%s:%s: Cannot find caller task with tid %d\n",
__TASKNAME__, __FUNCTION__, task->tid);
BUG();
}
if ((unsigned long)shm_addr & PAGE_MASK) {
if (shmflg & SHM_RND)
shm_addr = (void *)page_align(shm_addr);
else
return PTR_ERR(-EINVAL);
}
/* Set mmap flags for segment */
vmflags = VM_READ | VMA_SHARED | VMA_ANONYMOUS;
vmflags |= (shmflg & SHM_RDONLY) ? 0 : VM_WRITE;
/*
* Currently all tasks use the same address for each unique segment.
* If address is already assigned, the supplied address must match
* the original address. We don't look for object map count because
* utcb addresses are assigned before being mapped. NOTE: We may do
* all this in a specific shm_mmap() call in do_mmap() in the future.
*/
if (shm_file_to_desc(shm_file)->shm_addr) {
if (shm_addr && (shm->shm_addr != shm_addr))
return PTR_ERR(-EINVAL);
}
/*
* mmap the area to the process as shared. Page fault
* handler would handle allocating and paging-in the
* shared pages.
*/
if (IS_ERR(mapped = do_mmap(shm_file, 0, task,
(unsigned long)shm_addr,
vmflags, shm->npages))) {
printf("do_mmap: Mapping shm area failed with %d.\n",
(int)mapped);
return PTR_ERR(mapped);
}
/* Assign new shm address if not assigned */
if (!shm->shm_addr)
shm->shm_addr = mapped;
else
BUG_ON(shm->shm_addr != mapped);
return shm->shm_addr;
}
void *sys_shmat(struct tcb *task, l4id_t shmid, void *shmaddr, int shmflg)
{
struct vm_file *shm_file, *n;
list_foreach_removable_struct(shm_file, n, &global_vm_files.list, list) {
if (shm_file->type == VM_FILE_SHM &&
shm_file_to_desc(shm_file)->shmid == shmid)
return do_shmat(shm_file, shmaddr,
shmflg, task);
}
return PTR_ERR(-EINVAL);
}
int do_shmdt(struct tcb *task, struct vm_file *shm)
{
int err;
if ((err = do_munmap(task,
(unsigned long)shm_file_to_desc(shm)->shm_addr,
shm_file_to_desc(shm)->npages)) < 0)
return err;
return 0;
}
int sys_shmdt(struct tcb *task, const void *shmaddr)
{
struct vm_file *shm_file, *n;
list_foreach_removable_struct(shm_file, n, &global_vm_files.list, list)
if (shm_file->type == VM_FILE_SHM &&
shm_file_to_desc(shm_file)->shm_addr == shmaddr)
return do_shmdt(task, shm_file);
return -EINVAL;
}
/*
* This finds out what address pool the shm area came from and
* returns the address back to that pool. There are 2 pools,
* one for utcbs and one for regular shm segments.
*/
void shm_destroy_priv_data(struct vm_file *shm_file)
{
struct shm_descriptor *shm_desc = shm_file_to_desc(shm_file);
/* Release the shared memory address */
BUG_ON(shm_delete_address(shm_desc->shm_addr,
shm_file->vm_obj.npages) < 0);
/* Release the shared memory id */
BUG_ON(id_del(shm_ids, shm_desc->shmid) < 0);
/* Now delete the private data itself */
kfree(shm_file->priv_data);
}
/* Creates an shm area and glues its details with shm pager and devzero */
struct vm_file *shm_new(key_t key, unsigned long npages)
{
struct shm_descriptor *shm_desc;
struct vm_file *shm_file;
BUG_ON(!npages);
/* Allocate file and shm structures */
if (IS_ERR(shm_file = vm_file_create()))
return PTR_ERR(shm_file);
if (!(shm_desc = kzalloc(sizeof(struct shm_descriptor)))) {
kfree(shm_file);
return PTR_ERR(-ENOMEM);
}
/* Initialise the shm descriptor */
if (IS_ERR(shm_desc->shmid = id_new(shm_ids))) {
kfree(shm_file);
kfree(shm_desc);
return PTR_ERR(shm_desc->shmid);
}
shm_desc->key = (int)key;
shm_desc->npages = npages;
/* Initialise the file */
shm_file->length = __pfn_to_addr(npages);
shm_file->type = VM_FILE_SHM;
shm_file->priv_data = shm_desc;
shm_file->destroy_priv_data = shm_destroy_priv_data;
/* Initialise the vm object */
shm_file->vm_obj.pager = &swap_pager;
shm_file->vm_obj.flags = VM_OBJ_FILE | VM_WRITE;
/* Add to shm file and global object list */
global_add_vm_file(shm_file);
return shm_file;
}
/*
* Fast internal path to do shmget/shmat() together for mm0's
* convenience. Works for existing areas.
*/
void *shmat_shmget_internal(struct tcb *task, key_t key, void *shmaddr)
{
struct vm_file *shm_file;
struct shm_descriptor *shm_desc;
list_foreach_struct(shm_file, &global_vm_files.list, list) {
if(shm_file->type == VM_FILE_SHM) {
shm_desc = shm_file_to_desc(shm_file);
/* Found the key, shmat that area */
if (shm_desc->key == key)
return do_shmat(shm_file, shmaddr,
0, task);
}
}
return PTR_ERR(-EEXIST);
}
/*
* FIXME: Make sure hostile tasks don't subvert other tasks' shared pages
* by early-registring their shared page address here.
*/
int sys_shmget(key_t key, int size, int shmflg)
{
unsigned long npages = __pfn(page_align_up(size));
struct shm_descriptor *shm_desc;
struct vm_file *shm;
/* First check argument validity */
if (npages > SHM_SHMMAX || npages < SHM_SHMMIN)
return -EINVAL;
/*
* IPC_PRIVATE means create a no-key shm area, i.e. private to this
* process so that it would only share it with its forked children.
*/
if (key == IPC_PRIVATE) {
key = -1; /* Our meaning of no key */
if (!(shm = shm_new(key, npages)))
return -ENOSPC;
else
return shm_file_to_desc(shm)->shmid;
}
list_foreach_struct(shm, &global_vm_files.list, list) {
if (shm->type != VM_FILE_SHM)
continue;
shm_desc = shm_file_to_desc(shm);
if (shm_desc->key == key) {
/*
* Exclusive means a create request
* on an existing key should fail.
*/
if ((shmflg & IPC_CREAT) && (shmflg & IPC_EXCL))
return -EEXIST;
else
/* Found it but do we have a size problem? */
if (shm_desc->npages < npages)
return -EINVAL;
else /* Return shmid of the existing key */
return shm_desc->shmid;
}
}
/* Key doesn't exist and create is set, so we create */
if (shmflg & IPC_CREAT)
if (!(shm = shm_new(key, npages)))
return -ENOSPC;
else
return shm_file_to_desc(shm)->shmid;
else /* Key doesn't exist, yet create isn't set, its an -ENOENT */
return -ENOENT;
}
/*
* Currently, a default shm page is allocated to every thread in the system
* for efficient ipc communication. This part below provides the allocation
* and mapping of this page using shmat/get/dt call semantics.
*/
/*
* Sends shpage address information to requester. The requester then uses
* this address as a shm key and maps it via shmget/shmat.
*/
void *task_send_shpage_address(struct tcb *sender, l4id_t taskid)
{
struct tcb *task = find_task(taskid);
/* Is the task asking for its own utcb address */
if (sender->tid == taskid) {
/* It hasn't got one allocated. */
BUG_ON(!task->shared_page);
/* Return it to requester */
return task->shared_page;
/* A task is asking for someone else's utcb */
} else {
/* Only vfs is allowed to do so yet, because its a server */
if (sender->tid == VFS_TID) {
/*
* Return shpage address to requester. Note if there's
* none allocated so far, requester gets 0. We don't
* allocate one here.
*/
return task->shared_page;
}
}
return 0;
}
int shpage_map_to_task(struct tcb *owner, struct tcb *mapper, unsigned int flags)
{
struct vm_file *default_shm;
/* Allocate a new shared page address */
if (flags & SHPAGE_NEW_ADDRESS)
owner->shared_page =
shm_new_address(DEFAULT_SHPAGE_SIZE/PAGE_SIZE);
else if (!owner->shared_page)
BUG();
/* Create a new shared memory segment */
if (flags & SHPAGE_NEW_SHM)
if (IS_ERR(default_shm = shm_new((key_t)owner->shared_page,
__pfn(DEFAULT_SHPAGE_SIZE))))
return (int)default_shm;
/* Map the shared page to mapper */
if (IS_ERR(shmat_shmget_internal(mapper, (key_t)owner->shared_page,
owner->shared_page)))
BUG();
/* Prefault the owner's shared page to mapper's address space */
if (flags & SHPAGE_PREFAULT)
for (int i = 0; i < __pfn(DEFAULT_SHPAGE_SIZE); i++)
prefault_page(mapper, (unsigned long)owner->shared_page +
__pfn_to_addr(i), VM_READ | VM_WRITE);
return 0;
}
int shpage_unmap_from_task(struct tcb *owner, struct tcb *mapper)
{
return sys_shmdt(mapper, owner->shared_page);
}

707
conts/posix/mm0/src/task.c Normal file
View File

@@ -0,0 +1,707 @@
/*
* Task management.
*
* Copyright (C) 2007 Bahadir Balban
*/
#include <l4/macros.h>
#include <l4/config.h>
#include <l4/types.h>
#include <l4/lib/list.h>
#include <l4/lib/math.h>
#include <l4/api/thread.h>
#include <l4/api/kip.h>
#include <l4/api/errno.h>
#include INC_GLUE(memory.h)
#include <l4lib/arch/syscalls.h>
#include <l4lib/arch/syslib.h>
#include <l4lib/arch/utcb.h>
#include <l4lib/ipcdefs.h>
#include <l4lib/exregs.h>
#include <lib/addr.h>
#include <lib/malloc.h>
#include <init.h>
#include <string.h>
#include <vm_area.h>
#include <memory.h>
#include <globals.h>
#include <file.h>
#include <task.h>
#include <exec.h>
#include <shm.h>
#include <mmap.h>
#include <boot.h>
#include <test.h>
#include <utcb.h>
struct global_list global_tasks = {
.list = { &global_tasks.list, &global_tasks.list },
.total = 0,
};
void print_tasks(void)
{
struct tcb *task;
printf("Tasks:\n========\n");
list_foreach_struct(task, &global_tasks.list, list) {
printf("Task tid: %d, spid: %d\n", task->tid, task->spid);
}
}
void global_add_task(struct tcb *task)
{
BUG_ON(!list_empty(&task->list));
list_insert_tail(&task->list, &global_tasks.list);
global_tasks.total++;
}
void global_remove_task(struct tcb *task)
{
BUG_ON(list_empty(&task->list));
list_remove_init(&task->list);
BUG_ON(--global_tasks.total < 0);
}
struct tcb *find_task(int tid)
{
struct tcb *t;
list_foreach_struct(t, &global_tasks.list, list)
if (t->tid == tid)
return t;
return 0;
}
struct tcb *tcb_alloc_init(unsigned int flags)
{
struct tcb *task;
if (!(task = kzalloc(sizeof(struct tcb))))
return PTR_ERR(-ENOMEM);
/* Allocate new vma head if its not shared */
if (!(flags & TCB_SHARED_VM)) {
if (!(task->vm_area_head =
kzalloc(sizeof(*task->vm_area_head)))) {
kfree(task);
return PTR_ERR(-ENOMEM);
}
task->vm_area_head->tcb_refs = 1;
link_init(&task->vm_area_head->list);
/* Also allocate a utcb head for new address space */
if (!(task->utcb_head =
kzalloc(sizeof(*task->utcb_head)))) {
kfree(task->vm_area_head);
kfree(task);
return PTR_ERR(-ENOMEM);
}
task->utcb_head->tcb_refs = 1;
link_init(&task->utcb_head->list);
}
/* Allocate file structures if not shared */
if (!(flags & TCB_SHARED_FILES)) {
if (!(task->files =
kzalloc(sizeof(*task->files)))) {
kfree(task->vm_area_head);
kfree(task->utcb_head);
kfree(task);
return PTR_ERR(-ENOMEM);
}
task->files->tcb_refs = 1;
}
/* Ids will be acquired from the kernel */
task->tid = TASK_ID_INVALID;
task->spid = TASK_ID_INVALID;
task->tgid = TASK_ID_INVALID;
/* Initialise list structure */
link_init(&task->list);
link_init(&task->child_ref);
link_init(&task->children);
return task;
}
/*
* Free vmas, fd structure and utcb address.
* Make sure to sync all IO beforehand
*/
int task_free_resources(struct tcb *task)
{
/*
* Threads may share file descriptor structure
* if no users left, free it.
*/
if (!(--task->files->tcb_refs))
kfree(task->files);
/*
* Threads may share the virtual space.
* if no users of the vma struct left,
* free it along with all its vma links.
*/
if (!(--task->vm_area_head->tcb_refs)) {
/* Free all vmas */
task_release_vmas(task->vm_area_head);
/* Free the head */
kfree(task->vm_area_head);
}
/*
* Threads may share utcb chain
*/
if (!(--task->utcb_head->tcb_refs)) {
/* UTCBs must have been deleted explicitly */
BUG_ON(!list_empty(&task->utcb_head->list));
/* Free the head */
kfree(task->utcb_head);
}
return 0;
}
int tcb_destroy(struct tcb *task)
{
struct tcb *child, *n;
global_remove_task(task);
/* Free all resources of the task */
task_free_resources(task);
/*
* All children of the current task becomes children
* of the parent of this task.
*/
list_foreach_removable_struct(child, n, &task->children,
child_ref) {
list_remove_init(&child->child_ref);
list_insert_tail(&child->child_ref,
&task->parent->children);
child->parent = task->parent;
}
/* The task is not a child of its parent */
list_remove_init(&task->child_ref);
/* Now task deletion make sure task is in no list */
BUG_ON(!list_empty(&task->list));
BUG_ON(!list_empty(&task->child_ref));
BUG_ON(!list_empty(&task->children));
kfree(task);
return 0;
}
/*
* Copy all vmas from the given task and populate each with
* links to every object that the original vma is linked to.
* Note, that we don't copy vm objects but just the links to
* them, because vm objects are not per-process data.
*/
int task_copy_vmas(struct tcb *to, struct tcb *from)
{
struct vm_area *vma, *new_vma;
list_foreach_struct(vma, &from->vm_area_head->list, list) {
/* Create a new vma */
new_vma = vma_new(vma->pfn_start, vma->pfn_end - vma->pfn_start,
vma->flags, vma->file_offset);
/* Copy all object links */
vma_copy_links(new_vma, vma);
/* All link copying is finished, now add the new vma to task */
task_insert_vma(new_vma, &to->vm_area_head->list);
}
return 0;
}
/*
* Traverse all vmas, release all links to vm_objects.
* Used when a task or thread group with a shared vm is exiting.
*/
int task_release_vmas(struct task_vma_head *vma_head)
{
struct vm_area *vma, *n;
list_foreach_removable_struct(vma, n, &vma_head->list, list) {
/* Release all links */
vma_drop_merge_delete_all(vma);
/* Delete the vma from task's vma list */
list_remove(&vma->list);
/* Free the vma */
kfree(vma);
}
return 0;
}
int copy_tcb(struct tcb *to, struct tcb *from, unsigned int flags)
{
/* Copy program segment boundary information */
to->start = from->start;
to->end = from->end;
to->text_start = from->text_start;
to->text_end = from->text_end;
to->data_start = from->data_start;
to->data_end = from->data_end;
to->bss_start = from->bss_start;
to->bss_end = from->bss_end;
to->stack_start = from->stack_start;
to->stack_end = from->stack_end;
to->heap_start = from->heap_start;
to->heap_end = from->heap_end;
to->args_start = from->args_start;
to->args_end = from->args_end;
to->map_start = from->map_start;
to->map_end = from->map_end;
/* Sharing the list of vmas and utcbs */
if (flags & TCB_SHARED_VM) {
to->vm_area_head = from->vm_area_head;
to->vm_area_head->tcb_refs++;
to->utcb_head = from->utcb_head;
to->utcb_head->tcb_refs++;
} else {
/* Copy all vm areas */
task_copy_vmas(to, from);
/*
* NOTE:
* No copy for utcb descriptor list,
* forker shall start its own unique.
*/
}
/* Copy all file descriptors */
if (flags & TCB_SHARED_FILES) {
to->files = from->files;
to->files->tcb_refs++;
} else {
/* Bulk copy all file descriptors */
memcpy(to->files, from->files, sizeof(*to->files));
/* Increase refcount for all open files */
for (int i = 0; i < TASK_FILES_MAX; i++)
if (to->files->fd[i].vmfile)
to->files->fd[i].vmfile->openers++;
}
return 0;
}
struct tcb *task_create(struct tcb *parent, struct task_ids *ids,
unsigned int ctrl_flags, unsigned int share_flags)
{
struct tcb *task;
int err;
/* Set task ids if a parent is supplied */
if (parent) {
ids->tid = parent->tid;
ids->spid = parent->spid;
/*
* Determine whether the cloned thread
* is in parent's thread group
*/
if (share_flags & TCB_SHARED_TGROUP)
ids->tgid = parent->tgid;
else
ids->tgid = TASK_ID_INVALID;
}
/* Create the thread structures and address space */
if ((err = l4_thread_control(THREAD_CREATE | ctrl_flags, ids)) < 0) {
printf("l4_thread_control failed with %d.\n", err);
return PTR_ERR(err);
}
/* Create a task and use given space and thread ids. */
if (IS_ERR(task = tcb_alloc_init(share_flags)))
return PTR_ERR(task);
/* Set task's ids */
task->tid = ids->tid;
task->spid = ids->spid;
task->tgid = ids->tgid;
/* Set task's creation flags */
task->clone_flags = share_flags;
/*
* If a parent task has been specified, that means either
* we are forking, or we are cloning the original tcb fully
* or partially. Therefore we copy tcbs depending on share flags.
*/
if (parent) {
copy_tcb(task, parent, share_flags);
/* Set up a new utcb for new thread */
task_setup_utcb(task);
/* Set up parent-child relationship */
if ((share_flags & TCB_SHARED_PARENT) ||
(share_flags & TCB_SHARED_TGROUP)) {
/*
* On these conditions child shares
* the parent of the caller
*/
list_insert_tail(&task->child_ref,
&parent->parent->children);
task->parent = parent->parent;
} else {
list_insert_tail(&task->child_ref,
&parent->children);
task->parent = parent;
}
} else {
struct tcb *pager = find_task(PAGER_TID);
/* All parentless tasks are children of the pager */
list_insert_tail(&task->child_ref, &pager->children);
task->parent = pager;
}
return task;
}
/*
* Copy argument and environment strings into task's stack in a
* format that is expected by the C runtime.
*
* e.g. uclibc expects stack state:
*
* (low) |->argc|argv[0]|argv[1]|...|argv[argc] = 0|envp[0]|...|NULL| (high)
*
*/
int task_args_to_user(char *user_stack, struct args_struct *args,
struct args_struct *env)
{
BUG_ON((unsigned long)user_stack & 7);
/* Copy argc */
*((int *)user_stack) = args->argc;
user_stack += sizeof(int);
/* Copy argument strings one by one */
for (int i = 0; i < args->argc; i++) {
strcpy(user_stack, args->argv[i]);
user_stack += strlen(args->argv[i]) + 1;
}
/* Put the null terminator integer */
*((int *)user_stack) = 0;
user_stack = user_stack + sizeof(int);
/* Copy environment strings one by one */
for (int i = 0; i < env->argc; i++) {
strcpy(user_stack, env->argv[i]);
user_stack += strlen(env->argv[i]) + 1;
}
return 0;
}
int task_map_stack(struct vm_file *f, struct exec_file_desc *efd, struct tcb *task,
struct args_struct *args, struct args_struct *env)
{
/* First set up task's stack markers */
unsigned long stack_used = align_up(args->size + env->size, 8);
unsigned long arg_pages = __pfn(page_align_up(stack_used));
char *args_on_stack;
void *mapped;
task->stack_end = USER_AREA_END;
task->stack_start = USER_AREA_END - DEFAULT_STACK_SIZE;
task->args_end = task->stack_end;
task->args_start = task->stack_end - stack_used;
BUG_ON(stack_used > DEFAULT_STACK_SIZE);
/*
* mmap task's stack as anonymous memory.
* TODO: Add VMA_GROWSDOWN here so the stack can expand.
*/
if (IS_ERR(mapped = do_mmap(0, 0, task, task->stack_start,
VM_READ | VM_WRITE |
VMA_PRIVATE | VMA_ANONYMOUS,
__pfn(task->stack_end -
task->stack_start)))) {
printf("do_mmap: Mapping stack failed with %d.\n",
(int)mapped);
return (int)mapped;
}
/* Map the stack's part that will contain args and environment */
if (IS_ERR(args_on_stack =
pager_validate_map_user_range2(task,
(void *)task->args_start,
stack_used,
VM_READ | VM_WRITE))) {
return (int)args_on_stack;
}
/* Copy arguments and env */
task_args_to_user(args_on_stack, args, env);
/* Unmap task's those stack pages from pager */
pager_unmap_pages(args_on_stack, arg_pages);
return 0;
}
/*
* If bss comes consecutively after the data section, prefault the
* last page of the data section and zero out the bit that contains
* the beginning of bss. If bss spans into more pages, then map those
* pages as anonymous pages which are mapped by the devzero file.
*/
int task_map_bss(struct vm_file *f, struct exec_file_desc *efd, struct tcb *task)
{
unsigned long bss_mmap_start;
void *mapped;
/*
* Test if bss starts right from the end of data,
* and not on a new page boundary.
*/
if ((task->data_end == task->bss_start) &&
!is_page_aligned(task->bss_start)) {
unsigned long bss_size = task->bss_end - task->bss_start;
struct page *last_data_page;
void *pagebuf, *bss;
/* Prefault the last data page */
BUG_ON(prefault_page(task, task->data_end,
VM_READ | VM_WRITE) < 0);
/* Get the page */
last_data_page = task_virt_to_page(task, task->data_end);
/* Map the page */
pagebuf = l4_map_helper((void *)page_to_phys(last_data_page), 1);
/* Find the bss offset */
bss = (void *)((unsigned long)pagebuf |
(PAGE_MASK & task->bss_start));
/*
* Zero out the part that is bss. This is minimum of either
* end of bss or until the end of page, whichever is met first.
*/
memset((void *)bss, 0, min(TILL_PAGE_ENDS(task->data_end),
(int)bss_size));
/* Unmap the page */
l4_unmap_helper(pagebuf, 1);
/* Push bss mmap start to next page */
bss_mmap_start = page_align_up(task->bss_start);
} else /* Otherwise bss mmap start is same as bss_start */
bss_mmap_start = task->bss_start;
/*
* Now if there are more pages covering bss,
* map those as anonymous zero pages
*/
if (task->bss_end > bss_mmap_start) {
if (IS_ERR(mapped = do_mmap(0, 0, task, task->bss_start,
VM_READ | VM_WRITE |
VMA_PRIVATE | VMA_ANONYMOUS,
__pfn(page_align_up(task->bss_end) -
page_align(task->bss_start))))) {
printf("do_mmap: Mapping environment failed with %d.\n",
(int)mapped);
return (int)mapped;
}
}
return 0;
}
int task_mmap_segments(struct tcb *task, struct vm_file *file, struct exec_file_desc *efd,
struct args_struct *args, struct args_struct *env)
{
void *mapped;
//struct vm_file *shm;
int err;
int text_size, data_size;
/* Set up task's user boundary regions */
task->start = USER_AREA_START;
task->end = USER_AREA_END;
task->map_start = task->start;
task->map_end = task->end;
text_size = __pfn(page_align_up(task->text_end) -
page_align(task->text_start));
data_size = __pfn(page_align_up(task->data_end) -
page_align(task->text_start));
/* mmap task's text to task's address space. */
if (IS_ERR(mapped = do_mmap(file, efd->text_offset, task,
task->text_start, VM_READ | VM_WRITE |
VM_EXEC | VMA_PRIVATE, text_size))) {
printf("do_mmap: failed with %d.\n", (int)mapped);
err = (int)mapped;
goto out_err;
}
/* mmap task's data to task's address space. */
if (IS_ERR(mapped = do_mmap(file, efd->data_offset, task,
task->data_start, VM_READ | VM_WRITE |
VMA_PRIVATE, data_size))) {
printf("do_mmap: failed with %d.\n", (int)mapped);
err = (int)mapped;
goto out_err;
}
/* mmap task's bss as anonymous memory. */
if ((err = task_map_bss(file, efd, task)) < 0) {
printf("%s: Mapping bss has failed.\n",
__FUNCTION__);
goto out_err;
}
/* mmap task's stack, writing in the arguments and environment */
if ((err = task_map_stack(file, efd, task, args, env)) < 0) {
printf("%s: Mapping task's stack has failed.\n",
__FUNCTION__);
goto out_err;
}
/* Get a new utcb slot for new task */
if ((err = task_setup_utcb(task)) < 0) {
printf("%s: Mapping task's utcb has failed.\n",
__FUNCTION__);
goto out_err;
}
return 0;
out_err:
task_free_resources(task);
return err;
}
int task_setup_registers(struct tcb *task, unsigned int pc,
unsigned int sp, l4id_t pager)
{
int err;
struct exregs_data exregs;
/* Set up task's registers to default. */
if (!sp)
sp = align(task->stack_end - 1, 8);
if (!pc)
if (!(pc = task->entry))
pc = task->text_start;
if (!pager)
pager = self_tid();
/* Set up the task's thread details, (pc, sp, pager etc.) */
exregs_set_stack(&exregs, sp);
exregs_set_pc(&exregs, pc);
exregs_set_pager(&exregs, pager);
exregs_set_utcb(&exregs, task->utcb_address);
if ((err = l4_exchange_registers(&exregs, task->tid)) < 0) {
printf("l4_exchange_registers failed with %d.\n", err);
return err;
}
return 0;
}
int task_start(struct tcb *task)
{
int err;
struct task_ids ids = {
.tid = task->tid,
.spid = task->spid,
.tgid = task->tgid,
};
/* Start the thread */
// printf("%s: Starting task with thread id: %d, space id: %d\n",
// __TASKNAME__, task->tid, task->spid);
if ((err = l4_thread_control(THREAD_RUN, &ids)) < 0) {
printf("l4_thread_control failed with %d\n", err);
return err;
}
return 0;
}
/*
* During its initialisation FS0 wants to learn how many boot tasks
* are running, and their tids, which includes itself. This function
* provides that information.
*/
int vfs_send_task_data(struct tcb *vfs)
{
int li = 0;
struct tcb *t, *self;
struct task_data_head *tdata_head;
if (vfs->tid != VFS_TID) {
printf("%s: Task data requested by %d, which is not "
"FS0 id %d, ignoring.\n", __TASKNAME__, vfs->tid,
VFS_TID);
return 0;
}
BUG_ON(!(self = find_task(self_tid())));
BUG_ON(!vfs->shared_page);
/* Attach mm0 to vfs's utcb segment just like a normal task */
shpage_map_to_task(vfs, self, SHPAGE_PREFAULT);
/* Write all requested task information to shared pages's user buffer area */
tdata_head = (struct task_data_head *)vfs->shared_page;
/* First word is total number of tcbs */
tdata_head->total = global_tasks.total;
/* Write per-task data for all tasks */
list_foreach_struct(t, &global_tasks.list, list) {
tdata_head->tdata[li].tid = t->tid;
tdata_head->tdata[li].shpage_address = (unsigned long)t->shared_page;
li++;
}
return 0;
}
/*
* Prefaults all mapped regions of a task. The reason we have this is
* some servers are in the page fault handling path (e.g. fs0), and we
* don't want them to fault and cause deadlocks and circular deps.
*
* Normally fs0 faults dont cause dependencies because its faults
* are handled by the boot pager, which is part of mm0. BUT: It may
* cause deadlocks because fs0 may fault while serving a request
* from mm0.(Which is expected to also handle the fault).
*/
int task_prefault_regions(struct tcb *task, struct vm_file *f)
{
struct vm_area *vma;
list_foreach_struct(vma, &task->vm_area_head->list, list) {
for (int pfn = vma->pfn_start; pfn < vma->pfn_end; pfn++)
BUG_ON(prefault_page(task, __pfn_to_addr(pfn),
VM_READ | VM_WRITE) < 0);
}
return 0;
}

130
conts/posix/mm0/src/test.c Normal file
View File

@@ -0,0 +1,130 @@
/*
* These functions here do run-time checks on all fields
* of tasks, vmas, and vm objects to see that they
* have expected values.
*
* Copyright (C) 2008 Bahadir Balban
*/
#include <vm_area.h>
#include <mmap.h>
#include <shm.h>
#include <globals.h>
struct vm_statistics {
int tasks; /* All tasks counted on the system */
int vm_objects; /* All objects counted on the system */
int shadow_objects; /* Shadows counted by hand (well almost!) */
int shadows_referred; /* Shadows that objects say they have */
int file_objects; /* Objects that are found to be files */
int vm_files; /* All files counted on the system */
int shm_files; /* SHM files counted */
int boot_files; /* Boot files counted */
int vfs_files; /* VFS files counted */
int devzero; /* Devzero count, must be 1 */
};
/* Count links in objects link list, and compare with nlinks */
int vm_object_test_link_count(struct vm_object *vmo)
{
int links = 0;
struct vm_obj_link *l;
list_foreach_struct(l, &vmo->link_list, linkref)
links++;
BUG_ON(links != vmo->nlinks);
return 0;
}
int vm_object_test_shadow_count(struct vm_object *vmo)
{
struct vm_object *sh;
int shadows = 0;
list_foreach_struct(sh, &vmo->shdw_list, shref)
shadows++;
BUG_ON(shadows != vmo->shadows);
return 0;
}
/* TODO:
* Add checking that total open file descriptors are
* equal to total opener count of all files
*/
int mm0_test_global_vm_integrity(void)
{
struct tcb *task;
struct vm_object *vmo;
struct vm_statistics vmstat;
struct vm_file *f;
memset(&vmstat, 0, sizeof(vmstat));
/* Count all shadow and file objects */
list_foreach_struct(vmo, &global_vm_objects.list, list) {
vmstat.shadows_referred += vmo->shadows;
if (vmo->flags & VM_OBJ_SHADOW)
vmstat.shadow_objects++;
if (vmo->flags & VM_OBJ_FILE)
vmstat.file_objects++;
vmstat.vm_objects++;
vm_object_test_shadow_count(vmo);
vm_object_test_link_count(vmo);
}
/* Count all registered vmfiles */
list_foreach_struct(f, &global_vm_files.list, list) {
vmstat.vm_files++;
if (f->type == VM_FILE_SHM)
vmstat.shm_files++;
else if (f->type == VM_FILE_BOOTFILE)
vmstat.boot_files++;
else if (f->type == VM_FILE_VFS)
vmstat.vfs_files++;
else if (f->type == VM_FILE_DEVZERO)
vmstat.devzero++;
else BUG();
}
if (vmstat.vm_files != global_vm_files.total) {
printf("Total counted files don't match "
"global_vm_files total\n");
BUG();
}
if (vmstat.vm_objects != global_vm_objects.total) {
printf("Total counted vm_objects don't "
"match global_vm_objects total\n");
BUG();
}
/* Total file objects must be equal to total vm files */
if (vmstat.vm_files != vmstat.file_objects) {
printf("\nTotal files don't match total file objects.\n");
printf("vm files:\n");
vm_print_files(&global_vm_files.list);
printf("\nvm objects:\n");
vm_print_objects(&global_vm_objects.list);
printf("\n");
BUG();
}
/* Counted and referred shadows must match */
BUG_ON(vmstat.shadow_objects != vmstat.shadows_referred);
/* Count all tasks */
list_foreach_struct(task, &global_tasks.list, list)
vmstat.tasks++;
if (vmstat.tasks != global_tasks.total) {
printf("Total counted tasks don't match global_tasks total\n");
BUG();
}
return 0;
}

View File

@@ -0,0 +1,76 @@
/*
* Functions to validate, map and unmap user buffers.
*
* Copyright (C) 2008 Bahadir Balban
*/
#include <l4lib/arch/syslib.h>
#include <vm_area.h>
#include <task.h>
#include <user.h>
/*
* Checks if the given user virtual address range is
* validly owned by that user with given flags.
*
* FIXME: This scans the vmas page by page, we can do it faster
* by leaping from one vma to next.
*/
int pager_validate_user_range(struct tcb *user, void *userptr, unsigned long size,
unsigned int vmflags)
{
struct vm_area *vma;
unsigned long start = page_align(userptr);
unsigned long end = page_align_up(userptr + size);
/* Find the vma that maps that virtual address */
for (unsigned long vaddr = start; vaddr < end; vaddr += PAGE_SIZE) {
if (!(vma = find_vma(vaddr, &user->vm_area_head->list))) {
//printf("%s: No VMA found for 0x%x on task: %d\n",
// __FUNCTION__, vaddr, user->tid);
return -1;
}
if ((vma->flags & vmflags) != vmflags)
return -1;
}
return 0;
}
/*
* Validates and maps the user virtual address range to the pager.
* Every virtual page needs to be mapped individually because it's
* not guaranteed pages are physically contiguous.
*
* FIXME: There's no logic here to make non-contiguous physical pages
* to get mapped virtually contiguous.
*/
void *pager_validate_map_user_range(struct tcb *user, void *userptr,
unsigned long size, unsigned int vm_flags)
{
unsigned long start = page_align(userptr);
unsigned long end = page_align_up(userptr + size);
void *mapped = 0;
/* Validate that user task owns this address range */
if (pager_validate_user_range(user, userptr, size, vm_flags) < 0)
return 0;
/* Map first page and calculate the mapped address of pointer */
mapped = l4_map_helper((void *)page_to_phys(task_virt_to_page(user, start)), 1);
mapped = (void *)(((unsigned long)mapped) |
((unsigned long)(PAGE_MASK & (unsigned long)userptr)));
/* Map the rest of the pages, if any */
for (unsigned long i = start + PAGE_SIZE; i < end; i += PAGE_SIZE)
l4_map_helper((void *)page_to_phys(task_virt_to_page(user,
start + i)), 1);
return mapped;
}
void pager_unmap_user_range(void *mapped_ptr, unsigned long size)
{
l4_unmap_helper((void *)page_align(mapped_ptr),
__pfn(page_align_up(size)));
}

184
conts/posix/mm0/src/utcb.c Normal file
View File

@@ -0,0 +1,184 @@
/*
* Management of task utcb regions and own utcb.
*
* Copyright (C) 2007-2009 Bahadir Bilgehan Balban
*/
#include <l4lib/arch/utcb.h>
#include <l4/macros.h>
#include INC_GLUE(memlayout.h)
#include <mmap.h>
#include <utcb.h>
#include <lib/malloc.h>
#include <vm_area.h>
/*
* UTCB management in Codezero
*/
/* Globally disjoint utcb virtual region pool */
static struct address_pool utcb_region_pool;
int utcb_pool_init()
{
int err;
/* Initialise the global shm virtual address pool */
if ((err = address_pool_init(&utcb_region_pool,
UTCB_AREA_START, UTCB_AREA_END)) < 0) {
printf("UTCB address pool initialisation failed.\n");
return err;
}
return 0;
}
void *utcb_new_address(int npages)
{
return address_new(&utcb_region_pool, npages);
}
int utcb_delete_address(void *utcb_address, int npages)
{
return address_del(&utcb_region_pool, utcb_address, npages);
}
/* Return an empty utcb slot in this descriptor */
unsigned long utcb_new_slot(struct utcb_desc *desc)
{
int slot;
if ((slot = id_new(desc->slots)) < 0)
return 0;
else
return desc->utcb_base + (unsigned long)slot * UTCB_SIZE;
}
int utcb_delete_slot(struct utcb_desc *desc, unsigned long address)
{
BUG_ON(id_del(desc->slots, (address - desc->utcb_base)
/ UTCB_SIZE) < 0);
return 0;
}
unsigned long task_new_utcb_desc(struct tcb *task)
{
struct utcb_desc *d;
/* Allocate a new descriptor */
if (!(d = kzalloc(sizeof(*d))))
return 0;
link_init(&d->list);
/* We currently assume UTCB is smaller than PAGE_SIZE */
BUG_ON(UTCB_SIZE > PAGE_SIZE);
/* Initialise utcb slots */
d->slots = id_pool_new_init(PAGE_SIZE / UTCB_SIZE);
/* Obtain a new and unique utcb base */
/* FIXME: Use variable size than a page */
d->utcb_base = (unsigned long)utcb_new_address(1);
/* Add descriptor to tcb's chain */
list_insert(&d->list, &task->utcb_head->list);
/* Obtain and return first slot */
return utcb_new_slot(d);
}
int task_delete_utcb_desc(struct tcb *task, struct utcb_desc *d)
{
/* Unlink desc from its list */
list_remove_init(&d->list);
/* Unmap the descriptor region */
do_munmap(task, d->utcb_base, 1);
/* Return descriptor address */
utcb_delete_address((void *)d->utcb_base, 1);
/* Free the descriptor */
kfree(d);
return 0;
}
/*
* Upon fork, the utcb descriptor list is origaced by a new one, since it is a new
* address space. A new utcb is allocated and mmap'ed for the child task
* running in the newly created address space.
*
* The original privately mmap'ed regions for thread-local utcbs remain
* as copy-on-write on the new task, just like mmap'ed the stacks for cloned
* threads in the parent address space.
*
* Upon clone, naturally the utcb descriptor chain and vm_areas remain to be
* shared. A new utcb slot is allocated either by using an empty one in one of
* the existing mmap'ed utcb regions, or by mmaping a new utcb region.
*/
int task_setup_utcb(struct tcb *task)
{
struct utcb_desc *udesc;
unsigned long slot;
void *err;
/* Setting this up twice is a bug */
BUG_ON(task->utcb_address);
/* Search for an empty utcb slot already allocated to this space */
list_foreach_struct(udesc, &task->utcb_head->list, list)
if ((slot = utcb_new_slot(udesc)))
goto out;
/* Allocate a new utcb memory region and return its base */
slot = task_new_utcb_desc(task);
out:
/* Check if utcb is already mapped (in case of multiple threads) */
if (!find_vma(slot, &task->vm_area_head->list)) {
/* Map this region as private to current task */
if (IS_ERR(err = do_mmap(0, 0, task, slot,
VMA_ANONYMOUS | VMA_PRIVATE |
VMA_FIXED | VM_READ | VM_WRITE, 1))) {
printf("UTCB: mmapping failed with %d\n", (int)err);
return (int)err;
}
}
/* Assign task's utcb address */
task->utcb_address = slot;
// printf("UTCB created at 0x%x.\n", slot);
return 0;
}
/*
* Deletes a utcb slot by first deleting the slot entry, the descriptor
* address if emptied, the mapping of the descriptor, and the descriptor itself
*/
int task_destroy_utcb(struct tcb *task)
{
struct utcb_desc *udesc;
// printf("UTCB: Destroying 0x%x\n", task->utcb_address);
/* Find the utcb descriptor slot first */
list_foreach_struct(udesc, &task->utcb_head->list, list) {
/* FIXME: Use variable alignment than a page */
/* Detect matching slot */
if (page_align(task->utcb_address) == udesc->utcb_base) {
/* Delete slot from the descriptor */
utcb_delete_slot(udesc, task->utcb_address);
/* Is the desc completely empty now? */
if (id_is_empty(udesc->slots))
/* Delete the descriptor */
task_delete_utcb_desc(task, udesc);
return 0; /* Finished */
}
}
BUG();
}

View File

@@ -0,0 +1,217 @@
/*
* vm object utility functions.
*
* Copyright (C) 2008 Bahadir Balban
*/
#include <file.h>
#include <vm_area.h>
#include <l4/macros.h>
#include <l4/api/errno.h>
#include <lib/malloc.h>
#include <globals.h>
/* Global list of all in-memory files on the system */
struct global_list global_vm_files = {
.list = { &global_vm_files.list, &global_vm_files.list },
.total = 0,
};
/* Global list of in-memory vm objects in the system */
struct global_list global_vm_objects = {
.list = { &global_vm_objects.list, &global_vm_objects.list },
.total = 0,
};
void global_add_vm_object(struct vm_object *obj)
{
BUG_ON(!list_empty(&obj->list));
list_insert(&obj->list, &global_vm_objects.list);
global_vm_objects.total++;
}
void global_remove_vm_object(struct vm_object *obj)
{
BUG_ON(list_empty(&obj->list));
list_remove_init(&obj->list);
BUG_ON(--global_vm_objects.total < 0);
}
void global_add_vm_file(struct vm_file *f)
{
BUG_ON(!list_empty(&f->list));
list_insert(&f->list, &global_vm_files.list);
global_vm_files.total++;
global_add_vm_object(&f->vm_obj);
}
void global_remove_vm_file(struct vm_file *f)
{
BUG_ON(list_empty(&f->list));
list_remove_init(&f->list);
BUG_ON(--global_vm_files.total < 0);
global_remove_vm_object(&f->vm_obj);
}
void print_cache_pages(struct vm_object *vmo)
{
struct page *p;
if (!list_empty(&vmo->page_cache))
printf("Pages:\n======\n");
list_foreach_struct(p, &vmo->page_cache, list) {
dprintf("Page offset: 0x%x, virtual: 0x%x, refcnt: %d\n", p->offset,
p->virtual, p->refcnt);
}
}
void vm_object_print(struct vm_object *vmo)
{
struct vm_file *f;
printf("Object type: %s %s. links: %d, shadows: %d, Pages in cache: %d.\n",
vmo->flags & VM_WRITE ? "writeable" : "read-only",
vmo->flags & VM_OBJ_FILE ? "file" : "shadow", vmo->nlinks, vmo->shadows,
vmo->npages);
if (vmo->flags & VM_OBJ_FILE) {
f = vm_object_to_file(vmo);
char *ftype;
if (f->type == VM_FILE_DEVZERO)
ftype = "devzero";
else if (f->type == VM_FILE_BOOTFILE)
ftype = "bootfile";
else if (f->type == VM_FILE_SHM)
ftype = "shm file";
else if (f->type == VM_FILE_VFS)
ftype = "regular";
else
BUG();
printf("File type: %s\n", ftype);
}
// print_cache_pages(vmo);
// printf("\n");
}
void vm_print_files(struct link *files)
{
struct vm_file *f;
list_foreach_struct(f, files, list)
vm_object_print(&f->vm_obj);
}
void vm_print_objects(struct link *objects)
{
struct vm_object *vmo;
list_foreach_struct(vmo, objects, list)
vm_object_print(vmo);
}
struct vm_object *vm_object_init(struct vm_object *obj)
{
link_init(&obj->list);
link_init(&obj->shref);
link_init(&obj->shdw_list);
link_init(&obj->page_cache);
link_init(&obj->link_list);
return obj;
}
/* Allocate and initialise a vmfile, and return it */
struct vm_object *vm_object_create(void)
{
struct vm_object *obj;
if (!(obj = kzalloc(sizeof(*obj))))
return 0;
return vm_object_init(obj);
}
struct vm_file *vm_file_create(void)
{
struct vm_file *f;
if (!(f = kzalloc(sizeof(*f))))
return PTR_ERR(-ENOMEM);
link_init(&f->list);
vm_object_init(&f->vm_obj);
f->vm_obj.flags = VM_OBJ_FILE;
return f;
}
/*
* Populates the priv_data with vfs-file-specific
* information.
*/
struct vm_file *vfs_file_create(void)
{
struct vm_file *f = vm_file_create();
if (IS_ERR(f))
return f;
f->priv_data = kzalloc(sizeof(struct vfs_file_data));
f->type = VM_FILE_VFS;
return f;
}
/* Deletes the object via its base, along with all its pages */
int vm_object_delete(struct vm_object *vmo)
{
struct vm_file *f;
// vm_object_print(vmo);
/* Release all pages */
vmo->pager->ops.release_pages(vmo);
/* Remove from global list */
if (vmo->flags & VM_OBJ_FILE)
global_remove_vm_file(vm_object_to_file(vmo));
else if (vmo->flags & VM_OBJ_SHADOW)
global_remove_vm_object(vmo);
else BUG();
/* Check any references */
BUG_ON(vmo->nlinks);
BUG_ON(vmo->shadows);
BUG_ON(!list_empty(&vmo->shdw_list));
BUG_ON(!list_empty(&vmo->link_list));
BUG_ON(!list_empty(&vmo->page_cache));
BUG_ON(!list_empty(&vmo->shref));
/* Obtain and free via the base object */
if (vmo->flags & VM_OBJ_FILE) {
f = vm_object_to_file(vmo);
BUG_ON(!list_empty(&f->list));
if (f->priv_data) {
if (f->destroy_priv_data)
f->destroy_priv_data(f);
else
kfree(f->priv_data);
}
kfree(f);
} else if (vmo->flags & VM_OBJ_SHADOW)
kfree(vmo);
else BUG();
return 0;
}
int vm_file_delete(struct vm_file *f)
{
/* Delete file via base object */
return vm_object_delete(&f->vm_obj);
}