mirror of
https://github.com/drasko/codezero.git
synced 2026-04-02 01:59:05 +02:00
Added posix code
This commit is contained in:
BIN
conts/posix/mm0/src/.scons14756
Normal file
BIN
conts/posix/mm0/src/.scons14756
Normal file
Binary file not shown.
1
conts/posix/mm0/src/arch
Symbolic link
1
conts/posix/mm0/src/arch
Symbolic link
@@ -0,0 +1 @@
|
||||
arch-arm
|
||||
94
conts/posix/mm0/src/arch-arm/crt0.S
Normal file
94
conts/posix/mm0/src/arch-arm/crt0.S
Normal 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
|
||||
|
||||
72
conts/posix/mm0/src/arch-arm/mm.c
Normal file
72
conts/posix/mm0/src/arch-arm/mm.c
Normal 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
131
conts/posix/mm0/src/boot.c
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
39
conts/posix/mm0/src/bootdesc.c
Normal file
39
conts/posix/mm0/src/bootdesc.c
Normal 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);
|
||||
}
|
||||
62
conts/posix/mm0/src/bootm.c
Normal file
62
conts/posix/mm0/src/bootm.c
Normal 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;
|
||||
}
|
||||
|
||||
85
conts/posix/mm0/src/capability.c
Normal file
85
conts/posix/mm0/src/capability.c
Normal 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
188
conts/posix/mm0/src/clone.c
Normal 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
52
conts/posix/mm0/src/dev.c
Normal 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,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
366
conts/posix/mm0/src/execve.c
Normal file
366
conts/posix/mm0/src/execve.c
Normal 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
166
conts/posix/mm0/src/exit.c
Normal 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
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
993
conts/posix/mm0/src/file.c
Normal 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
288
conts/posix/mm0/src/init.c
Normal 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();
|
||||
}
|
||||
|
||||
59
conts/posix/mm0/src/lib/addr.c
Normal file
59
conts/posix/mm0/src/lib/addr.c
Normal 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;
|
||||
}
|
||||
|
||||
111
conts/posix/mm0/src/lib/bit.c
Normal file
111
conts/posix/mm0/src/lib/bit.c
Normal 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;
|
||||
}
|
||||
|
||||
177
conts/posix/mm0/src/lib/elf/elf.c
Normal file
177
conts/posix/mm0/src/lib/elf/elf.c
Normal 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 = §_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;
|
||||
}
|
||||
|
||||
88
conts/posix/mm0/src/lib/idpool.c
Normal file
88
conts/posix/mm0/src/lib/idpool.c
Normal 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;
|
||||
}
|
||||
|
||||
413
conts/posix/mm0/src/lib/malloc.c
Normal file
413
conts/posix/mm0/src/lib/malloc.c
Normal 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
|
||||
|
||||
203
conts/posix/mm0/src/memory.c
Normal file
203
conts/posix/mm0/src/memory.c
Normal 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
424
conts/posix/mm0/src/mmap.c
Normal 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;
|
||||
}
|
||||
|
||||
261
conts/posix/mm0/src/munmap.c
Normal file
261
conts/posix/mm0/src/munmap.c
Normal 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);
|
||||
}
|
||||
|
||||
391
conts/posix/mm0/src/pagers.c
Normal file
391
conts/posix/mm0/src/pagers.c
Normal 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;
|
||||
}
|
||||
|
||||
175
conts/posix/mm0/src/physmem.c
Normal file
175
conts/posix/mm0/src/physmem.c
Normal 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
381
conts/posix/mm0/src/shm.c
Normal 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
707
conts/posix/mm0/src/task.c
Normal 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
130
conts/posix/mm0/src/test.c
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
76
conts/posix/mm0/src/user.c
Normal file
76
conts/posix/mm0/src/user.c
Normal 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
184
conts/posix/mm0/src/utcb.c
Normal 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();
|
||||
}
|
||||
|
||||
|
||||
217
conts/posix/mm0/src/vm_object.c
Normal file
217
conts/posix/mm0/src/vm_object.c
Normal 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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user