Kernel updates since December 2009

This commit is contained in:
Bahadir Balban
2010-03-25 01:12:40 +02:00
parent 16818191b3
commit 74b5963fcb
487 changed files with 22477 additions and 3857 deletions

View File

@@ -0,0 +1,71 @@
# -*- mode: python; coding: utf-8; -*-
#
# Codezero -- Virtualization microkernel for embedded systems.
#
# Copyright © 2009 B Labs Ltd
#
import os, shelve, sys
from os.path import *
PROJRELROOT = '../..'
sys.path.append(PROJRELROOT)
from config.projpaths import *
from config.configuration import *
from config.lib import *
config = configuration_retrieve()
arch = config.arch
subarch = config.subarch
platform = config.platform
gcc_arch_flag = config.gcc_arch_flag
LIBL4_RELDIR = 'conts/libl4'
KERNEL_INCLUDE = join(PROJROOT, 'include')
LIBL4_DIR = join(PROJROOT, LIBL4_RELDIR)
LIBL4_INCLUDE = join(LIBL4_DIR, 'include')
LIBL4_LIBPATH = join(BUILDDIR, LIBL4_RELDIR)
# Locally important paths are here
LIBC_RELDIR = 'conts/libc'
LIBC_DIR = join(PROJROOT, LIBC_RELDIR)
LIBC_LIBPATH = join(BUILDDIR, LIBC_RELDIR)
LIBC_INCLUDE = [join(LIBC_DIR, 'include'), \
join(LIBC_DIR, 'include/arch' + '/' + arch)]
LIBDEV_RELDIR = 'conts/libdev'
LIBDEV_DIR = join(PROJROOT, LIBDEV_RELDIR)
LIBDEV_LIBPATH = join(join(BUILDDIR, LIBDEV_RELDIR), 'sys-userspace')
LIBDEV_INCLUDE = [join(LIBDEV_DIR, 'uart/include'), join(LIBDEV_DIR, 'include')]
LIBMEM_RELDIR = 'conts/libmem'
LIBMEM_DIR = join(PROJROOT, LIBMEM_RELDIR)
LIBMEM_LIBPATH = join(BUILDDIR, LIBMEM_RELDIR)
LIBMEM_INCLUDE = LIBMEM_DIR
env = Environment(CC = config.toolchain + 'gcc',
# We don't use -nostdinc because sometimes we need standard headers,
# such as stdarg.h e.g. for variable args, as in printk().
CCFLAGS = ['-g', '-nostdlib', '-ffreestanding', '-std=gnu99', '-Wall', '-march=' + gcc_arch_flag],
LINKFLAGS = ['-nostdlib', '-T' + "include/project_linker.lds", "-u_start"],\
ASFLAGS = ['-D__ASSEMBLY__'], \
PROGSUFFIX = '.elf', # The suffix to use for final executable\
ENV = {'PATH' : os.environ['PATH']}, # Inherit shell path\
LIBS = ['gcc', 'libl4', 'libmc', 'c-userspace', 'libdev-userspace', 'gcc', 'libmalloc',
'c-userspace'], # libgcc.a - This is required for division routines.
CPPPATH = ["#include", KERNEL_INCLUDE, LIBL4_INCLUDE, LIBDEV_INCLUDE, LIBC_INCLUDE, LIBMEM_INCLUDE],
LIBPATH = [LIBL4_LIBPATH, LIBDEV_LIBPATH, LIBC_LIBPATH, LIBMEM_LIBPATH],
CPPFLAGS = '-include l4/config.h -include l4/macros.h -include l4/types.h')
src = Glob('*.[cS]')
src += Glob('src/*.[cS]')
src += Glob('src/arch/' + arch + '/*.[cS]')
src += Glob('src/api/*.c');
src += Glob('src/perf/*.c');
src += Glob('src/cli_serv/*.c');
src += Glob('src/mthread/*.c');
src += Glob('src/arch/' + arch + '/' + subarch + '/*.[cS]')
objs = env.Object(src)
prog = env.Program('main.elf', objs)
Depends(prog, 'include/linker.lds')

34
conts/test_suite0/TODO Normal file
View File

@@ -0,0 +1,34 @@
Missing tests:
1) Library tests:
~~~~~~~~~~~~~~~~~
- L4Lib Thread library needs to be tested
- Stack allocation
- UTCB allocation
- L4Lib Capability library needs to be tested
- Capability read, print, manipulation
- Address allocation, id allocation functions need to be tested.
- Page allocator needs to be tested.
- Memcache needs to be tested.
- All above libraries must be tested with many many different combinations.
2) API Tests:
~~~~~~~~~~~~~
- Null pointer tests
- Extended IPC via page faulting.
- Mutex tests on different virtual but same physical page.
- Cache flushing tests
- Capability limit tests (e.g. pushing limits of given capabilities)
- Capability manipulation tests (manipulating existing capabilities)
3) Performance Tests:
~~~~~~~~~~~~~~~~~~~~~
Performance tests should fall back to using timer if perfmon does not exist.
Performance tests need to run on multiple architectures.
(e.g. beagleboard perfmon, timer ...)
- Map/unmap
- Ipc results are missing

View File

@@ -0,0 +1,112 @@
Codezero Buildsystem For This Container
Autogenerated by the Build system
This is an autogenerated file that is meant to walk you through the build
process. It is meant to be the most simple to get on with, therefore if
you feel any complications, please reach us on our l4dev.org mailing list
on http://lists.l4dev.org/mailman/listinfo/codezero-devel
You have created a new container called `test_suite0'.
The parameters you have supplied are described in the ".container" file
placed at the top-level directory. Note, that this is only an informative
file for your reference, and it can be optionally removed.
1. Directory Structure:
1.1) Directory tree:
.
|-- SConstruct
|-- build.readme
|-- .container
|-- include
| `-- linker.lds.example
|-- main.c
`-- src
|-- test.c
In the above directory tree:
1.2) |-- SConstruct
This is the top-level build file, that will build your project in its current
state. You may freely reorganize directories, but must reflect changes in this
file. For more, please see the SCons build tool at http://www.scons.org/
The build system will search for this file, and execute it by the:
`scons'
command at the root of the directory. You may issue the same command manually
for building and testing your build. If you choose to use another build tool
such as make, you may freely replace scons, and the build system will search
and call your custom build command.
1.3) |--include
`--src
These are the directories that include your header files and sources. You may
freely change and reorganize these, but make sure to have a valid build file
that reflects those changes at the top-level directory.
1.4) |-- include
| `-- linker.lds.example
This is an example linker script for your project. Using this as your default
linker script is often useful, since it has been autogenerated to contain all
the parameters you need for the memory regions of your application defined at
configuration time. You may freely replace it, but make sure to edit the
top-level build script accordingly.
2. Build Process
2.1) Build overview
The complete Codezero system will be built from a top-level `build.py' script by
that resides in the top-level directory of Codezero sources.
The Codezero system build script will build this container at a certain stage
during the build, by referring to build script file named such as `SConstruct'
or `Makefile' that resides in this container's top-level directory.
Once the executables are built, it will search for all files with a .elf
extension in any of the subdirectories, and recognize those as loadable
executables. There may be more than one of these files present after the build.
In the future this behaviour may change such that the loadable executable files
are also specified in the configuration.
Finally, executables of all containers will be picked up and built into the
final.elf file, which is a self-loading elf executable.
3. Reconfiguring this container
If you want to reconfigure the container with new parameters, you may do so by
executing the `configure.py' script at the top-level Codezero directory by:
'./configure.py'
This will populate only brand new container directories with new files. It will
update it's existing internal configuration for existing containers (such as
container memory regions) but it won't touch any files that exist under an
already-defined container.
If you want to start from scratch, specify a new directory name, if you want
to reconfigure existing container parameters, run this on an existing directory,
and it will only update its internal records for this container, but not touch
the directory.
4. Example source files
Example source files populated by the configuration contain valid examples
of how the generic libl4 userspace library can be used. Each test contains a
valid example from the available API, and may be modified, changed and removed
freely.

View File

@@ -0,0 +1,28 @@
/*
* Container entry point for pager
*
* Copyright (C) 2007-2009 B Labs Ltd.
*/
#include <l4lib/init.h>
#include <l4lib/utcb.h>
#include <l4lib/lib/thread.h>
#include <l4lib/lib/cap.h>
extern void main(void);
void __container_init(void)
{
/* Generic L4 initialisation */
__l4_init();
/* Thread library initialisation */
__l4_threadlib_init();
/* Capability library initialization */
__l4_capability_init();
/* Entry to main */
main();
}

83
conts/test_suite0/exit.ct Normal file
View File

@@ -0,0 +1,83 @@
/*
* Main function for all tests
*
* Copyright (C) 2009 B Labs Ltd.
*/
#include <l4/api/errno.h>
#include <container.h>
#include <thread.h>
#include <tests.h>
#include <l4lib/macros.h>
#include L4LIB_INC_ARCH(syslib.h)
#include L4LIB_INC_ARCH(syscalls.h)
#include <l4/api/space.h>
int exit_test_thread(void *arg)
{
while (1)
;
//l4_thread_switch(0);
//l4_exit(5);
return 0;
}
int exit_test(void)
{
int ret;
struct task_ids ids;
/* Create and run a new thread */
if ((ret = thread_create(exit_test_thread, 0,
TC_SHARE_SPACE | TC_AS_PAGER,
&ids)) < 0) {
printf("Top-level simple_pager creation failed.\n");
goto out_err;
} else
printf("Thread (%d) created successfully.\n", ids.tid);
// l4_thread_switch(0);
/* Kill it */
printf("Killing Thread (%d).\n", ids.tid);
if ((ret = l4_thread_control(THREAD_DESTROY, &ids)) < 0)
printf("Error: Killing Thread (%d), err = %d\n", ids.tid, ret);
else
printf("Success: Killed Thread (%d)\n", ids.tid);
#if 0
/* Wait on it */
printf("Waiting on Thread (%d) to exit.\n", ids.tid);
if ((ret = l4_thread_control(THREAD_WAIT, &ids)) >= 0)
printf("Success. Paged child returned %d\n", ret);
else
printf("Error. Wait on (%d) failed. err = %d\n",
ids.tid, ret);
#endif
return 0;
out_err:
BUG();
}
int main(void)
{
printf("%s: Container %s started\n",
__CONTAINER__, __CONTAINER_NAME__);
/* Performance tests */
/* API Tests */
capability_test();
//exit_test();
/* Now quit to demo self-paging quit */
//l4_exit(0);
/* Now quit by null pointer */
// *((int *)0) = 5;
return 0;
}

View File

@@ -0,0 +1,16 @@
#ifndef __TEST_SUITE_API_H__
#define __TEST_SUITE_API_H__
int test_api_cctrl(void);
int test_api_tctrl(void);
int test_api_capctrl(void);
int test_api_getid(void);
int test_api_mutexctrl(void);
int test_api_tswitch(void);
int test_api_exregs(void);
int test_api_ipc(void);
int test_api_irqctrl(void);
int test_api_map_unmap(void);
#endif /* __TEST_SUITE_API_H__ */

View File

@@ -0,0 +1,6 @@
#ifndef __CAPABILITY_H__
#define __CAPABILITY_H__
int caps_read_all();
#endif /* __CAPABILITY_H__ */

View File

@@ -0,0 +1,13 @@
/*
* Autogenerated definitions for this container.
*/
#ifndef __CONTAINER_H__
#define __CONTAINER_H__
#define __CONTAINER_NAME__ "test_suite0"
#define __CONTAINER_ID__ 0
#define __CONTAINER__ "cont0"
#endif /* __CONTAINER_H__ */

View File

@@ -0,0 +1,44 @@
/*
* Debug/performance measurements for mm0
*
* Copyright (C) 2010 B Labs Ltd.
*/
#ifndef __ARCH_DEBUG_H__
#define __ARCH_DEBUG_H__
#if !defined(CONFIG_DEBUG_PERFMON_USER)
#include <l4lib/types.h>
/* Common empty definitions for all arches */
static inline u32 perfmon_read_cyccnt() { return 0; }
static inline void perfmon_reset_start_cyccnt() { }
static inline u32 perfmon_read_reset_start_cyccnt() { return 0; }
#define debug_record_cycles(str)
#else /* End of CONFIG_DEBUG_PERFMON_USER */
/* Architecture specific perfmon cycle counting */
#include L4LIB_INC_SUBARCH(perfmon.h)
extern u64 perfmon_total_cycles;
extern u64 current_cycles;
/*
* This is for Cortex-A9 running at 400Mhz. 25 / 100000 is
* a rewriting of 2.5 nanosec / 1,000,000
*/
#define debug_record_cycles(str) \
{ \
current_cycles = perfmon_read_cyccnt(); \
perfmon_total_cycles += current_cycles; \
printf("%s: took %llu milliseconds\n", str, \
current_cycles * 64 * 25 / 100000); \
perfmon_reset_start_cyccnt(); \
}
#endif /* End of !CONFIG_DEBUG_PERFMON_USER */
#endif /* __ARCH_DEBUG_H__ */

View File

@@ -0,0 +1,44 @@
#ifndef __FAULT_H__
#define __FAULT_H__
#include <l4/macros.h>
#include <l4/types.h>
#include INC_GLUE(memory.h)
#include INC_ARCH(exception.h)
/* Protection flags */
#define VM_NONE (1 << 0)
#define VM_READ (1 << 1)
#define VM_EXEC (1 << 2)
#define VM_WRITE (1 << 3)
#define VM_PROT_MASK (VM_READ | VM_WRITE | VM_EXEC)
/* Shared copy of a file */
#define VMA_SHARED (1 << 4)
/* VMA that's not file-backed, always maps devzero as VMA_COW */
#define VMA_ANONYMOUS (1 << 5)
/* Private copy of a file */
#define VMA_PRIVATE (1 << 6)
/* For wired pages */
#define VMA_FIXED (1 << 7)
/* For stack, where mmap returns end address */
#define VMA_GROWSDOWN (1 << 8)
/* Set when the page is dirty in cache but not written to disk */
#define VM_DIRTY (1 << 9)
/* Fault data specific to this task + ptr to kernel's data */
struct fault_data {
fault_kdata_t *kdata; /* Generic data forged by the kernel */
unsigned int reason; /* Generic fault reason flags */
unsigned int address; /* Aborted address */
unsigned int pte_flags; /* Generic protection flags on pte */
l4id_t sender; /* Inittask-related fault data */
};
void set_generic_fault_params(struct fault_data *fault);
void arch_print_fault_params(struct fault_data *fault);
void fault_handle_error(struct fault_data *fault);
#endif /* __FAULT_H__ */

View File

@@ -0,0 +1,16 @@
#ifndef __TEST_SUITE_LINKER_H__
#define __TEST_SUITE_LINKER_H__
extern unsigned char __stack[];
extern unsigned char __end[];
extern unsigned char vma_start[];
extern unsigned char lma_start[];
extern unsigned char offset[];
extern unsigned char __data_start[];
extern unsigned char __data_end[];
extern unsigned char __bss_start[];
extern unsigned char __bss_end[];
extern unsigned char __stack_start[];
extern unsigned char __stack_end[];
#endif /* __LINKER_H__ */

View File

@@ -0,0 +1,30 @@
/*
* Example working linker script for this container.
*
* Copyright (C) 2009 B Labs Ltd.
*/
vma_start = 0xa0000000;
lma_start = 0x100000;
offset = vma_start - lma_start;
ENTRY(_start)
SECTIONS
{
. = vma_start;
.text : AT (ADDR(.text) - offset) { *(.text.head) *(.text) }
.rodata : AT (ADDR(.rodata) - offset) { *(.rodata) }
.rodata1 : AT (ADDR(.rodata1) - offset) { *(.rodata1) }
. = ALIGN(4K);
.data : AT (ADDR(.data) - offset) { *(.data) }
.bss : AT (ADDR(.bss) - offset)
{
*(.bss)
. += 0x1000;
. = ALIGN(8);
__stack = .;
}
__end = .;
}

View File

@@ -0,0 +1,9 @@
#ifndef __TEST_MACROS_H__
#define __TEST_MACROS_H__
#define __INC_ARCH(x) <arch/__ARCH__/x>
#define __INC_SUBARCH(x) <arch/__ARCH__/__SUBARCH__/x>
#define __INC_PLAT(x) <platform/__PLATFORM__/x>
#define __INC_GLUE(x) <glue/__ARCH__/x>
#endif /* __TEST_MACROS_H__ */

View File

@@ -0,0 +1,12 @@
#ifndef __TESTSUITE_MEMORY_H__
#define __TESTSUITE_MEMORY_H__
void *virtual_page_new(int npages);
void *physical_page_new(int npages);
void virtual_page_free(void *address, int npages);
void physical_page_free(void *address, int npages);
void page_pool_init(void);
#endif /* __TESTSUITE_MEMORY_H__ */

View File

@@ -0,0 +1,69 @@
#ifndef __PERF_TESTS_H__
#define __PERF_TESTS_H__
/* Architecture specific perfmon cycle counting */
#include <l4lib/types.h>
#include <l4lib/macros.h>
#include L4LIB_INC_SUBARCH(perfmon.h)
struct perfmon_cycles {
u64 last; /* Last op cycles */
u64 min; /* Minimum cycles */
u64 max; /* Max cycles */
u64 avg; /* Average cycles */
u64 total; /* Total cycles */
u64 ops; /* Total ops */
};
/*
* This is for converting cycle count to timings on
* Cortex-A9 running at 400Mhz. 25 / 100000 is
* a rewriting of 2.5 nanosec / 1,000,000 in millisec
*
* 25 / 100 = 2.5nanosec * 10 / 1000 = microseconds
*/
#define CORTEXA9_400MHZ_USEC 25 / 10000
#define CORTEXA9_400MHZ_MSEC 25 / 10000000
#define USEC_MULTIPLIER CORTEXA9_400MHZ_USEC
#define MSEC_MULTIPLIER CORTEXA9_400MHZ_MSEC
#if !defined(CONFIG_DEBUG_PERFMON_USER)
#define perfmon_record_cycles(ptr, str)
#else /* End of CONFIG_DEBUG_PERFMON_USER */
#define perfmon_record_cycles(pcyc, str) \
{ \
(pcyc)->ops++; \
(pcyc)->last = perfmon_read_cyccnt() * 64; \
(pcyc)->total += (pcyc)->last; \
if ((pcyc)->min > (pcyc)->last) \
(pcyc)->min = (pcyc)->last; \
if ((pcyc)->max < (pcyc)->last) \
(pcyc)->max = (pcyc)->last; \
}
/* Same as above but restarts counter */
#define perfmon_checkpoint_cycles(pcyc, str) \
{ \
(pcyc)->last = perfmon_read_cyccnt(); \
(pcyc)->total += pcyc->last; \
perfmon_reset_start_cyccnt(); \
}
#endif /* End of !CONFIG_DEBUG_PERFMON_USER */
void platform_measure_cpu_cycles(void);
void perf_measure_getid_ticks(void);
void perf_measure_cpu_cycles(void);
void perf_measure_getid(void);
void perf_measure_tctrl(void);
int perf_measure_exregs(void);
void perf_measure_ipc(void);
void perf_measure_map(void);
void perf_measure_unmap(void);
void perf_measure_mutex(void);
#endif /* __PERF_TESTS_H__ */

View File

@@ -0,0 +1,38 @@
/*
* Example working linker script for this container.
*
* Copyright (C) 2009 B Labs Ltd.
*/
vma_start = 0xa0000000;
lma_start = 0x100000;
offset = vma_start - lma_start;
ENTRY(_start)
SECTIONS
{
. = vma_start;
.text : AT (ADDR(.text) - offset) { *(.text.head) *(.text) }
.rodata : AT (ADDR(.rodata) - offset) { *(.rodata) }
.rodata1 : AT (ADDR(.rodata1) - offset) { *(.rodata1) }
. = ALIGN(4K);
__data_start = .;
.data : AT (ADDR(.data) - offset) { *(.data) }
__data_end = .;
__bss_start = .;
.bss : AT (ADDR(.bss) - offset) { *(.bss) }
__bss_end = .;
__stack_start = .;
.stack : AT (ADDR(.stack) - offset)
{
. += 0x3000;
. = ALIGN(4K);
__stack = .;
}
__stack_end = .;
__end = .;
}

View File

@@ -0,0 +1,18 @@
#ifndef __TESTS_H__
#define __TESTS_H__
/* Abort debugging conditions */
#define DEBUG_TESTS 0
#if DEBUG_TESTS
#define dbg_printf(...) printf(__VA_ARGS__)
#else
#define dbg_printf(...)
#endif
int test_smp();
int test_performance();
int test_api();
int test_cli_serv();
int test_mthread();
#endif /* __TESTS_H__ */

View File

@@ -0,0 +1,19 @@
#ifndef __THREAD_H__
#define __THREAD_H__
#include <l4lib/macros.h>
#include L4LIB_INC_ARCH(syslib.h)
#include L4LIB_INC_ARCH(syscalls.h)
#include <l4lib/exregs.h>
#include <l4/api/thread.h>
int thread_create(int (*func)(void *), void *args, unsigned int flags,
struct task_ids *new_ids);
/* For same space */
#define STACK_SIZE 0x1000
#define THREADS_TOTAL 10
#endif /* __THREAD_H__ */

View File

@@ -0,0 +1,9 @@
#ifndef __PERF_TESTS_TIMER_H__
#define __PERF_TESTS_TIMER_H__
#include <libdev/timer.h>
extern unsigned long timer_base;
void perf_timer_init(void);
#endif /* __PERF_TESTS_TIMER_H__ */

47
conts/test_suite0/main.c Normal file
View File

@@ -0,0 +1,47 @@
/*
* Main function for all tests
*
* Copyright (C) 2009 B Labs Ltd.
*/
#include <l4lib/macros.h>
#include L4LIB_INC_ARCH(syslib.h)
#include L4LIB_INC_ARCH(syscalls.h)
#include <tests.h>
#include <thread.h>
#include <container.h>
#include <l4/api/space.h>
#include <l4/api/errno.h>
void run_tests(void)
{
/* Performance tests */
//if (test_performance() < 0)
// printf("Performance tests failed.\n");
if (test_smp() < 0)
printf("SMP tests failed.\n");
/* API Tests */
if (test_api() < 0)
printf("API tests failed.\n");
/* Container client/server setup test */
if (test_cli_serv() < 0)
printf("Client/server tests failed.\n");
/* Container multithreaded/standalone setup test */
if (test_mthread() < 0)
printf("Multi-threaded tests failed.\n");
}
int main(void)
{
printf("%s: Container %s started\n",
__CONTAINER__, __CONTAINER_NAME__);
run_tests();
return 0;
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright (C) 2010 B Labs Ltd.
*
* API tests
*
* Author: Bahadir Balban
*/
#include <tests.h>
#include <memory.h>
#include <api/api.h>
/*
* Tests all api functions by expected and unexpected input
*/
int test_api(void)
{
int err;
/* Initialize free pages */
page_pool_init();
if ((err = test_api_tctrl()) < 0)
return err;
if ((err = test_api_getid()) < 0)
return err;
if ((err = test_api_exregs()) < 0)
return err;
if ((err = test_api_map_unmap()) < 0)
return err;
if ((err = test_api_ipc()) < 0)
return err;
if ((err = test_api_mutexctrl()) < 0)
return err;
if ((err = test_api_cctrl()) < 0)
return err;
if ((err = test_api_capctrl()) < 0)
return err;
if ((err = test_api_irqctrl()) < 0)
return err;
return 0;
}

View File

@@ -0,0 +1,80 @@
/*
* Test cache control system call
*
* Copyright (C) 2010 B Labs Ltd.
*
* Author: Bahadir Balban
*/
#include INC_GLUE(memory.h)
#include <l4lib/lib/cap.h>
#include <l4/api/cache.h>
#include <linker.h>
#include <stdio.h>
#include <l4lib/macros.h>
#include L4LIB_INC_ARCH(syscalls.h)
/* This simply tests that all cache calls are working */
int test_cctrl_basic(void)
{
struct capability *virtcap = cap_get_by_type(CAP_TYPE_MAP_VIRTMEM);
void *start = (void *)__pfn_to_addr(virtcap->start);
void *end = (void *)__end;
int err;
if ((err = l4_cache_control(start, end, L4_INVALIDATE_ICACHE)) < 0)
return err;
if ((err = l4_cache_control(start, end, L4_INVALIDATE_DCACHE)) < 0)
return err;
if ((err = l4_cache_control(start, end, L4_CLEAN_INVALIDATE_DCACHE)) < 0)
return err;
if ((err = l4_cache_control(start, end, L4_CLEAN_DCACHE)) < 0)
return err;
if ((err = l4_cache_control(start, end, L4_INVALIDATE_TLB)) < 0)
return err;
return 0;
}
int test_cctrl_sync_caches()
{
/*
* Double-map a physical page and fill it with
* mov r0, r0, r0 * PAGE_SIZE - 1
* b return_label
*/
/* Flush the Dcache for that page */
/* Invalidate I cache for that page */
/* Execute the page */
/*
* Create a new address space and execute the page from
* that space
*/
return 0;
}
int test_api_cctrl(void)
{
int err;
if ((err = test_cctrl_basic()) < 0)
goto out_err;
printf("CACHE CONTROL: -- PASSED --\n");
return 0;
out_err:
printf("CACHE CONTROL: -- FAILED --\n");
return err;
}

View File

@@ -0,0 +1,56 @@
/*
* Test capability control system call
*
* Copyright (C) 2010 B Labs Ltd.
*
* Author: Bahadir Balban
*/
#include <l4lib/macros.h>
#include <l4lib/lib/cap.h>
#include L4LIB_INC_ARCH(syscalls.h)
#define TOTAL_CAPS 32
struct capability cap_array[TOTAL_CAPS];
/*
* Read number of capabilities
*/
int test_cap_read(void)
{
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");
return err;
}
/* Read all capabilities */
if ((err = l4_capability_control(CAP_CONTROL_READ,
0, cap_array)) < 0) {
printf("l4_capability resource_control() reading of "
"capabilities failed.\n Could not "
"complete CAP_CONTROL_READ_CAPS request.\n");
return err;
}
//cap_array_print(ncaps, caparray);
return 0;
}
int test_api_capctrl(void)
{
int err;
if ((err = test_cap_read()) < 0)
return err;
return 0;
}

View File

@@ -0,0 +1,96 @@
/*
* Test exchange registers system call.
*
* Copyright (C) 2010 B Labs Ltd.
*
* Author: Bahadir Balban
*/
#include <l4lib/exregs.h>
#include <l4lib/lib/thread.h>
#include <stdio.h>
#include <string.h>
#include <tests.h>
int test_exregs_read_write(void)
{
struct task_ids ids;
struct exregs_data exregs[2];
int err;
/* Get own space id */
l4_getid(&ids);
/*
* Create a thread in the same space.
* Thread is not runnable.
*/
if ((err = l4_thread_control(THREAD_CREATE | TC_SHARE_SPACE,
&ids)) < 0) {
dbg_printf("Thread create failed. "
"err=%d\n", err);
return err;
}
dbg_printf("Thread created successfully. "
"tid=%d\n", ids.tid);
/*
* Prepare a context part full of 0xFF
*/
memset(&exregs[0].context, 0xFF, sizeof(exregs[1].context));
exregs[0].valid_vect = 0xFFFFFFFF;
/* Write to context */
if ((err = l4_exchange_registers(&exregs[0], ids.tid)) < 0)
goto out;
/* Set the other as read-all */
exregs_set_read(&exregs[1]);
exregs[1].valid_vect = 0xFFFFFFFF;
if ((err = l4_exchange_registers(&exregs[1],
ids.tid)) < 0)
goto out;
/*
* Read back all context and compare results
*/
if (memcmp(&exregs[0].context, &exregs[1].context,
sizeof(exregs[0].context))) {
err = -1;
goto out;
}
out:
/*
* Destroy the thread
*/
if ((err = l4_thread_control(THREAD_DESTROY, &ids)) < 0) {
dbg_printf("Thread destroy failed. err=%d\n",
err);
}
return 0;
}
int test_api_exregs(void)
{
int err;
if ((err = test_exregs_read_write()) < 0)
goto out_err;
/*
* TODO: Should add more tests here, e.g. setting
* values of a thread we're not a pager of.
*/
printf("EXCHANGE REGISTERS: -- PASSED --\n");
return 0;
out_err:
printf("EXCHANGE REGISTERS: -- FAILED --\n");
return err;
}

View File

@@ -0,0 +1,123 @@
/*
* Test l4_getid system call.
*
* Copyright (C) 2010 B Labs Ltd.
*
* Author: Bahadir Balban
*/
#include <l4lib/macros.h>
#include L4LIB_INC_ARCH(syslib.h)
#include L4LIB_INC_ARCH(syscalls.h)
#include <l4lib/lib/thread.h>
#include <tests.h>
int thread_getid_nullptr(void *arg)
{
return l4_getid(0);
}
/*
* Pass nullptr to l4_getid syscall
*
* This exercise proves that the kernel does not crash
* and validly sends a page fault to offending thread's
* pager.
*/
int test_getid_nullptr(void)
{
struct l4_thread *thread;
int err;
/*
* Create a new thread who will attempt
* passing null ptr argument
*/
if ((err = thread_create(thread_getid_nullptr,
0, TC_SHARE_SPACE,
&thread)) < 0) {
dbg_printf("Thread create failed. "
"err=%d\n", err);
return err;
}
dbg_printf("Thread created successfully. "
"tid=%d\n", thread->ids.tid);
/*
* Listen on thread for its page fault
* ipc. (Recap: Upon illegal access, the kernel sends
* a page fault ipc message to thread's pager)
*/
if ((err = l4_receive(thread->ids.tid)) < 0) {
dbg_printf("%s: listening on page fault for "
"nullptr thread failed. "
"err = %d\n", __FUNCTION__, err);
return err;
}
/*
* Verify ipc was a page fault ipc
*/
if (l4_get_tag() != L4_IPC_TAG_PFAULT) {
dbg_printf("%s: Nullptr thread ipc does not "
"have expected page fault tag.\n"
"tag=%d, expected=%d\n",
__FUNCTION__, l4_get_tag(),
L4_IPC_TAG_PFAULT);
return -1;
}
/*
* Destroy the thread.
*/
if ((err = thread_destroy(thread)) < 0) {
dbg_printf("%s: Failed destroying thread. "
"err= %d, tid = %d\n",
__FUNCTION__, err,
thread->ids.tid);
return err;
}
return 0;
}
int test_api_getid(void)
{
struct task_ids ids;
int err;
/*
* Test valid getid request
*/
if ((err = l4_getid(&ids)) < 0) {
dbg_printf("Getid request failed. err=%d\n", err);
goto out_err;
}
/* Check returned results */
if (ids.tid != 1 || ids.spid != 1 || ids.tgid != 1) {
dbg_printf("Getid results not as expected. "
"tid=%d, spid=%d, tgid=%d\n",
ids.tid, ids.spid, ids.tgid);
err = -1;
goto out_err;
}
/*
* Test null pointer argument
*/
if ((err = test_getid_nullptr()) < 0) {
dbg_printf("l4_getid() null pointer test failed."
" err=%d\n", err);
goto out_err;
}
printf("GETID: -- PASSED --\n");
return 0;
out_err:
printf("GETID: -- FAILED --\n");
return err;
}

View File

@@ -0,0 +1,547 @@
/*
* Test ipc system call.
*
* Copyright (C) 2010 B Labs Ltd.
*
* Author: Bahadir Balban
*/
#include <l4lib/macros.h>
#include L4LIB_INC_ARCH(syslib.h)
#include L4LIB_INC_ARCH(syscalls.h)
#include <l4lib/lib/thread.h>
#include <l4lib/ipcdefs.h>
#include <tests.h>
#include <macros.h>
#include <fault.h>
#include <memory.h>
struct ipc_ext_data {
void *virtual; /* Virtual address to start ipc from */
l4id_t partner; /* Partner to do extended ipc */
};
int ipc_extended_sender(void *arg)
{
struct ipc_ext_data *data = arg;
int err;
if ((err = l4_send_extended(data->partner, 0,
SZ_2K, data->virtual)) < 0) {
printf("%s: Extended send failed. err=%d\n",
__FUNCTION__, err);
}
return 0;
}
int ipc_extended_receiver(void *arg)
{
struct ipc_ext_data *data = arg;
int err;
if ((err = l4_receive_extended(data->partner, SZ_2K,
data->virtual)) < 0) {
printf("%s: Extended receive failed. err=%d\n",
__FUNCTION__, err);
}
/*
* Test the data received
*/
for (int i = 0; i < SZ_2K; i++) {
if (((char *)data->virtual)[i] != 'A' + i)
printf("%s: Extended receive buffer has unexpected "
"data: Start %p, Offset: %d, "
"Data=%d, expected=%d\n", __FUNCTION__,
data->virtual, i, ((char *)data->virtual)[i],
'A' + i);
return err;
}
return 0;
}
int ipc_ext_handle_pfault(struct ipc_ext_data *ipc_data,
void **virt, void **phys)
{
u32 mr[MR_UNUSED_TOTAL];
struct fault_data fault;
int err;
/* Read mrs not used by syslib */
for (int i = 0; i < MR_UNUSED_TOTAL; i++)
mr[i] = read_mr(MR_UNUSED_START + i);
fault.kdata = (fault_kdata_t *)&mr[0];
fault.sender = l4_get_sender();
/* Convert from arch-specific to generic fault data */
set_generic_fault_params(&fault);
/*
* Handle the fault using a basic logic - if a virtual index
* is faulted, map the corresponding page at same physical index.
*/
if (page_align(fault.address) == (unsigned long)virt[0]) {
if ((err = l4_map(phys[0], virt[0], 1,
MAP_USR_RW, fault.sender)) < 0) {
printf("%s: Error: l4_map failed. "
"phys=%p, virt=%p\n", __FUNCTION__,
phys[0], virt[0]);
return err;
}
} else if (page_align(fault.address) == (unsigned long)virt[1]) {
if ((err = l4_map(phys[1], virt[1], 1,
MAP_USR_RW, fault.sender)) < 0) {
printf("%s: Error: l4_map failed. "
"phys=%p, virt=%p\n", __FUNCTION__,
phys[1], virt[1]);
return err;
}
} else if (page_align(fault.address) == (unsigned long)virt[2]) {
if ((err = l4_map(phys[2], virt[2], 1,
MAP_USR_RW, fault.sender)) < 0) {
printf("%s: Error: l4_map failed. "
"phys=%p, virt=%p\n", __FUNCTION__,
phys[2], virt[2]);
return err;
}
} else if (page_align(fault.address) == (unsigned long)virt[3]) {
if ((err = l4_map(phys[3], virt[3], 1,
MAP_USR_RW, fault.sender)) < 0) {
printf("%s: Error: l4_map failed. "
"phys=%p, virt=%p\n", __FUNCTION__,
phys[3], virt[3]);
return err;
}
} else {
printf("%s: Error, page fault occured on an unexpected "
"address. adress=0x%x\n", __FUNCTION__,
fault.address);
return -1;
}
/* Reply back to fault thread and return */
return l4_ipc_return(0);
}
/*
* Create two threads who will do page-faulting ipc to each other.
* Their parent waits and handles the page faults.
*
* This test allocates 4 virtual page and 4 physical page addresses.
* It fills a total of 2KB of payload starting from the 3rd quarter
* of the first page and until the 2nd quarter of the 2nd page to
* be sent by the sender thread.
*
* The payload is copied and the pages deliberately unmapped so that
* the sender thread will page fault during the send operation.
*
* The receive pages are also set up same as above, so the receiving
* thread also faults during the receive.
*
* The main thread starts both ipc threads, and starts waiting on
* page faults. It handles the faults and the test succeeds if the
* data is transfered safely to receiving end, despite all faults.
*/
int test_ipc_extended(void)
{
struct task_ids self_ids;
struct ipc_ext_data ipc_data[2];
struct l4_thread *thread[2];
void *virt[4], *phys[4];
int err, tag;
l4_getid(&self_ids);
/* Get 4 physical pages */
for (int i = 0; i < 4; i++)
phys[i] = physical_page_new(1);
/* Get 2 pairs of virtual pages */
virt[0] = virtual_page_new(2);
virt[1] = virt[0] + PAGE_SIZE;
virt[2] = virtual_page_new(2);
virt[3] = virt[2] + PAGE_SIZE;
/* Map sender pages to self */
if ((err = l4_map(phys[0], virt[0], 1,
MAP_USR_RW, self_ids.tid)) < 0) {
printf("Error: Mapping Sender pages failed. phys: 0x%p,"
" virt: 0x%p, tid=%d, err=%d\n", phys[0], virt[0],
self_ids.tid, err);
return err;
}
if ((err = l4_map(phys[1], virt[1], 1,
MAP_USR_RW, self_ids.tid)) < 0) {
printf("Error: Mapping Sender pages failed. phys: 0x%p,"
" virt: 0x%p, tid=%d, err=%d\n", phys[0], virt[0],
self_ids.tid, err);
return err;
}
/*
* Fill them with values to be sent
* Filling in 3rd KB of first page to 2nd KB of second page
*/
for (int i = 0; i < SZ_2K; i++)
((char *)virt[0] + SZ_1K * 3)[i] = 'A' + i;
/* Unmap the pages */
l4_unmap(virt[0], 2, self_ids.tid);
/* Create ipc threads but don't start. */
if ((err = thread_create(ipc_extended_sender,
&ipc_data[0],
TC_SHARE_SPACE | TC_NOSTART,
&thread[0])) < 0) {
dbg_printf("Thread create failed. "
"err=%d\n", err);
return err;
}
dbg_printf("Thread created successfully. "
"tid=%d\n", thread[0]->ids.tid);
if ((err = thread_create(ipc_extended_receiver,
&ipc_data[1],
TC_SHARE_SPACE | TC_NOSTART,
&thread[1])) < 0) {
dbg_printf("Thread create failed. "
"err=%d\n", err);
return err;
}
dbg_printf("Thread created successfully. "
"tid=%d\n", thread[1]->ids.tid);
/*
* Set up arguments to sender,
* Send offset at 3rd quarter of first page.
*/
ipc_data[0].virtual = virt[0] + SZ_1K * 3;
ipc_data[0].partner = thread[1]->ids.tid;
/*
* Set up arguments to receiver
* Receive offset at 3rd quarter of first page.
*/
ipc_data[1].virtual = virt[1] + SZ_1K * 3;
ipc_data[1].partner = thread[0]->ids.tid;
/* Start the threads */
l4_thread_control(THREAD_RUN, &thread[0]->ids);
l4_thread_control(THREAD_RUN, &thread[1]->ids);
/* Expecting 4 faults on 4 pages */
for (int i = 0; i < 4; i++) {
/* Wait on page fault */
if ((err = l4_receive(L4_ANYTHREAD)) < 0) {
printf("Error: l4_receive() for page"
" fault has failed. err=%d\n",
err);
}
if ((tag = l4_get_tag()) != L4_IPC_TAG_PFAULT) {
printf("Error: Parent thread received "
"non-page fault ipc tag. tag=%d\n",
tag);
return -1;
}
/* Handle fault */
if ((err = ipc_ext_handle_pfault(ipc_data, virt, phys)) < 0) {
printf("Error: An error occured during ipc "
"page fault handling. err=%d\n", err);
return err;
}
}
/* Wait for the ipc threads */
for (int i = 0; i < 2; i ++)
if ((err = thread_wait(thread[i])) < 0) {
dbg_printf("THREAD_WAIT failed. "
"err=%d\n", err);
return err;
}
/* Unmap and release pages */
for (int i = 0; i < 4; i++) {
l4_unmap(virt[i], 1, self_ids.tid);
virtual_page_free(virt[i], 1);
physical_page_free(phys[i], 1);
}
return 0;
}
int ipc_full_thread(void *arg)
{
l4id_t parent = *((l4id_t *)arg);
int err;
/* Do two full send/receives */
for (int i = 0; i < 2; i++) {
/* Full receive, return positive if error */
if ((err = l4_receive_full(parent)) < 0) {
dbg_printf("Full receive failed on new "
"thread. err=%d", err);
return 1;
}
/* Test full utcb received values */
for (int i = MR_UNUSED_START; i < MR_TOTAL + MR_REST; i++) {
if (read_mr(i) != i) {
dbg_printf("IPC full receive on new thread: "
"Unexpected message register "
"values. MR%d = %d, should be %d\n",
i, read_mr(i), i);
return 1; /* Exit positive without reply */
}
}
/*
* Reset all message registers
*/
for (int i = MR_UNUSED_START; i < MR_TOTAL + MR_REST; i++)
write_mr(i, 0);
/* Send full return reply */
l4_send_full(parent, 0);
}
return 0;
}
int ipc_short_thread(void *arg)
{
l4id_t parent = *((l4id_t *)arg);
int err;
/* Short receive, return positive if error */
if ((err = l4_receive(parent)) < 0) {
dbg_printf("Short receive failed on new "
"thread. err=%d", err);
return 1;
}
/* Test received registers */
for (int i = MR_UNUSED_START; i < MR_TOTAL; i++) {
if (read_mr(i) != i) {
dbg_printf("IPC Receive on new thread: "
"Unexpected message register "
"values.\n"
"read = %d, expected = %d\n",
read_mr(i), i);
l4_print_mrs();
return 1; /* Exit positive without reply */
}
}
/*
* Reset all message registers
*/
for (int i = MR_UNUSED_START; i < MR_TOTAL; i++)
write_mr(i, 0);
/*
* Send return reply and exit
*/
return l4_send(parent, 0);
}
/*
* Create a thread and do a full ipc to it
*/
int test_ipc_full(void)
{
struct task_ids self_ids;
struct l4_thread *thread;
int err;
l4_getid(&self_ids);
/*
* Create a thread in the same space
*/
if ((err = thread_create(ipc_full_thread,
&self_ids.tid,
TC_SHARE_SPACE,
&thread)) < 0) {
dbg_printf("Thread create failed. "
"err=%d\n", err);
return err;
}
dbg_printf("Thread created successfully. "
"tid=%d\n", thread->ids.tid);
/*
* Try one short and one full send/recv
* to test full send/recv occurs on both cases
*/
/*
* Write data to full utcb registers
*/
for (int i = MR_UNUSED_START; i < MR_TOTAL + MR_REST; i++)
write_mr(i, i);
/*
* First, do a full ipc send/recv
*/
if ((err = l4_sendrecv_full(thread->ids.tid,
thread->ids.tid,
0)) < 0) {
dbg_printf("Full IPC send/recv failed. "
"err=%d\n", err);
return err;
}
/*
* Check that payload registers are modified to 0
*/
dbg_printf("%s: After send/recv:\n", __FUNCTION__);
for (int i = MR_UNUSED_START; i < MR_TOTAL + MR_REST; i++) {
if (read_mr(i) != 0) {
dbg_printf("Full IPC send/recv: "
"Received payload is not "
"as expected.\n "
"MR%d = %d, should be %d\n",
i, read_mr(i), 0);
return -1;
}
}
/*
* Write data to full utcb registers
*/
for (int i = MR_UNUSED_START; i < MR_TOTAL + MR_REST; i++)
write_mr(i, i);
/*
* Try a short ipc send/recv. This should still result
* in full ipc since the other side is doing full send/recv.
*/
if ((err = l4_sendrecv(thread->ids.tid,
thread->ids.tid,
0)) < 0) {
dbg_printf("Full IPC send/recv failed. "
"err=%d\n", err);
return err;
}
/*
* Check that payload registers are modified to 0
*/
// dbg_printf("%s: After send/recv:\n", __FUNCTION__);
for (int i = MR_UNUSED_START; i < MR_TOTAL + MR_REST; i++) {
// dbg_printf("MR%d: %d\n", i, read_mr(i));
if (read_mr(i) != 0) {
dbg_printf("Full IPC send/recv: "
"Received payload is not "
"as expected.\n "
"MR%d = %d, should be %d\n",
i, read_mr(i), 0);
return -1;
}
}
/* Wait for the ipc thread to die */
if ((err = thread_wait(thread)) < 0) {
dbg_printf("THREAD_WAIT failed. "
"err=%d\n", err);
return err;
}
dbg_printf("Full IPC send/recv successful.\n");
return 0;
}
/*
* Create a thread and do a short ipc to it
*/
int test_ipc_short(void)
{
struct task_ids self_ids;
struct l4_thread *thread;
int err;
l4_getid(&self_ids);
/*
* Create a thread in the same space
*/
if ((err = thread_create(ipc_short_thread,
&self_ids.tid,
TC_SHARE_SPACE,
&thread)) < 0) {
dbg_printf("Thread create failed. "
"err=%d\n", err);
return err;
}
dbg_printf("Thread created successfully. "
"tid=%d\n", thread->ids.tid);
/*
* Write data to short ipc registers
*/
for (int i = MR_UNUSED_START; i < MR_TOTAL; i++)
write_mr(i, i);
/*
* Do short ipc send/recv and check data is reset
*/
if ((err = l4_sendrecv(thread->ids.tid,
thread->ids.tid,
0)) < 0) {
dbg_printf("Short IPC send/recv failed. "
"err=%d\n", err);
return err;
}
/*
* Check that payload registers are reset
*/
for (int i = MR_UNUSED_START; i < MR_TOTAL; i++) {
if (read_mr(i) != 0) {
dbg_printf("Short IPC send/recv: "
"Received payload is incorrect."
"read = %d, expected=%d\n",
read_mr(i), 0);
return -1;
}
}
/* Wait for the ipc thread */
if ((err = thread_wait(thread)) < 0) {
dbg_printf("THREAD_WAIT failed. "
"err=%d\n", err);
return err;
}
dbg_printf("Short IPC send/recv successful.\n");
return 0;
}
int test_api_ipc(void)
{
int err;
if ((err = test_ipc_extended()) < 0)
goto out_err;
if ((err = test_ipc_short()) < 0)
goto out_err;
if ((err = test_ipc_full()) < 0)
goto out_err;
printf("IPC: -- PASSED --\n");
return 0;
out_err:
printf("IPC: -- FAILED --\n");
return err;
}

View File

@@ -0,0 +1,12 @@
/*
* Test irq control system call
*
* Copyright (C) 2010 B Labs Ltd.
*
* Author: Bahadir Balban
*/
int test_api_irqctrl(void)
{
return 0;
}

View File

@@ -0,0 +1,366 @@
/*
* Test l4_map/unmap system call.
*
* Copyright (C) 2010 B Labs Ltd.
*
* Author: Bahadir Balban
*/
#include <l4lib/macros.h>
#include L4LIB_INC_ARCH(syscalls.h)
#include L4LIB_INC_ARCH(syslib.h)
#include INC_GLUE(memory.h)
#include <l4/api/errno.h>
#include <tests.h>
#define KERNEL_PAGE 0xF0000000UL
#define KIP_PAGE 0xFF000000UL
#define SYSCALL_PAGE 0xFFFFF000UL
#define VECTOR_PAGE 0xFFFF0000UL
int test_api_map(void)
{
int err;
unsigned int flags;
l4id_t self = self_tid();
/*
* Make a valid mapping, a few pages below
* the end of physical and virtual marks
*/
if ((err = l4_map((void *)CONFIG_CONT0_PHYS0_END - PAGE_SIZE * 5,
(void *)CONFIG_CONT0_VIRT0_END - PAGE_SIZE * 5,
1,
MAP_USR_RW,
self)) < 0) {
dbg_printf("sys_map failed on valid request. err=%d\n",
err);
return err;
}
/*
* Redo the same mapping. This should be valid.
*/
if ((err = l4_map((void *)CONFIG_CONT0_PHYS0_END - PAGE_SIZE * 5,
(void *)CONFIG_CONT0_VIRT0_END - PAGE_SIZE * 5,
1,
MAP_USR_RW,
self)) < 0) {
dbg_printf("sys_map failed on re-doing "
"valid request. err=%d\n", err);
return err;
}
/*
* Try mapping outside the virtual range
*/
if ((err = l4_map((void *)CONFIG_CONT0_PHYS0_END - PAGE_SIZE * 5,
(void *)CONFIG_CONT0_VIRT0_END,
1,
MAP_USR_RW,
self)) == 0) {
dbg_printf("sys_map succeeded on invalid "
"virtual range ret=%d\n", err);
return -1;
}
/*
* Try mapping outside the physical range
*/
if ((err = l4_map((void *)CONFIG_CONT0_PHYS0_END,
(void *)CONFIG_CONT0_VIRT0_END - PAGE_SIZE * 5,
1,
MAP_USR_RW,
self)) == 0) {
dbg_printf("sys_map succeeded on invalid "
"physical range ret=%d\n", err);
return -1;
}
/*
* Try having them both out of range
*/
if ((err = l4_map((void *)CONFIG_CONT0_PHYS0_END,
(void *)CONFIG_CONT0_VIRT0_END,
1,
MAP_USR_RW,
self)) == 0) {
dbg_printf("sys_map succeeded when invalid "
"physical and virtual ranges "
"supplied ret=%d\n", err);
return -1;
}
/*
* Try out of range by off-by-one page size excess
*/
if ((err = l4_map((void *)CONFIG_CONT0_PHYS0_END - PAGE_SIZE * 5,
(void *)CONFIG_CONT0_VIRT0_END - PAGE_SIZE * 5,
6,
MAP_USR_RW,
self)) == 0) {
dbg_printf("sys_map succeeded when invalid "
"physical and virtual ranges using "
"off-by-one page size."
"ret=%d\n", err);
return -1;
}
/*
* Try invalid page size
*/
if ((err = l4_map((void *)CONFIG_CONT0_PHYS0_END - PAGE_SIZE * 5,
(void *)CONFIG_CONT0_VIRT0_END - PAGE_SIZE * 5,
0xFFFFFFFF,
MAP_USR_RW,
self)) == 0) {
dbg_printf("sys_map succeeded when invalid "
"page size supplied ret=%d\n", err);
return -1;
}
/*
* Try invalid flags
*/
flags = 0;
if ((err = l4_map((void *)CONFIG_CONT0_PHYS0_END - PAGE_SIZE * 5,
(void *)CONFIG_CONT0_VIRT0_END - PAGE_SIZE * 5,
1,
flags,
self)) == 0) {
dbg_printf("sys_map succeeded when invalid "
"flags supplied flags=%u, ret=%d\n", flags, err);
return -1;
}
flags = MAP_KERN_RWX;
if ((err = l4_map((void *)CONFIG_CONT0_PHYS0_END - PAGE_SIZE * 5,
(void *)CONFIG_CONT0_VIRT0_END - PAGE_SIZE * 5,
1,
0,
self)) == 0) {
dbg_printf("sys_map succeeded when invalid "
"flags supplied flags=%u, ret=%d\n", flags, err);
return -1;
}
flags = MAP_KERN_IO;
if ((err = l4_map((void *)CONFIG_CONT0_PHYS0_END - PAGE_SIZE * 5,
(void *)CONFIG_CONT0_VIRT0_END - PAGE_SIZE * 5,
1,
0,
self)) == 0) {
dbg_printf("sys_map succeeded when invalid "
"flags supplied flags=%u, ret=%d\n", flags, err);
return -1;
}
flags = MAP_KERN_RX;
if ((err = l4_map((void *)CONFIG_CONT0_PHYS0_END - PAGE_SIZE * 5,
(void *)CONFIG_CONT0_VIRT0_END - PAGE_SIZE * 5,
1,
0,
self)) == 0) {
dbg_printf("sys_map succeeded when invalid "
"flags supplied flags=%u, ret=%d\n", flags, err);
return -1;
}
flags = 0xF0F0F01;
if ((err = l4_map((void *)CONFIG_CONT0_PHYS0_END - PAGE_SIZE * 5,
(void *)CONFIG_CONT0_VIRT0_END - PAGE_SIZE * 5,
1,
0,
self)) == 0) {
dbg_printf("sys_map succeeded when invalid "
"flags supplied flags=%u, ret=%d\n", flags, err);
return -1;
}
/*
* Try passing wraparound values
*/
if ((err = l4_map((void *)0xFFFFFFFF,
(void *)CONFIG_CONT0_VIRT0_END - PAGE_SIZE * 5,
1,
MAP_USR_RW,
self)) == 0) {
dbg_printf("sys_map succeeded when invalid "
"wraparound ranges supplied ret=%d\n", err);
return -1;
}
/*
* Try passing wraparound values
*/
if ((err = l4_map((void *)CONFIG_CONT0_PHYS0_END - PAGE_SIZE * 5,
(void *)0xFFFFF000,
2,
MAP_USR_RW,
self)) == 0) {
dbg_printf("sys_map succeeded when invalid "
"wraparound ranges supplied ret=%d\n", err);
return -1;
}
/*
* Try mapping onto kernel
*/
if ((err = l4_map((void *)CONFIG_CONT0_PHYS0_END - PAGE_SIZE * 5,
(void *)0xF0000000,
1,
MAP_USR_RW,
self)) == 0) {
dbg_printf("sys_map succeeded when trying to "
"map onto the kernel ret=%d\n", err);
return -1;
}
/*
* Try mapping to vector page
*/
if ((err = l4_map((void *)CONFIG_CONT0_PHYS0_END - PAGE_SIZE * 5,
(void *)0xFFFF0000,
1,
MAP_USR_RW,
self)) == 0) {
dbg_printf("sys_map succeeded when trying to "
"map to the vectors page ret=%d\n", err);
return -1;
}
/*
* Try mapping to kip
*/
if ((err = l4_map((void *)CONFIG_CONT0_PHYS0_END - PAGE_SIZE * 5,
(void *)0xFF000000,
1,
MAP_USR_RW,
self)) == 0) {
dbg_printf("sys_map succeeded when trying to "
"map to the kip page ret=%d\n", err);
return -1;
}
/*
* Try mapping to syscall page
*/
if ((err = l4_map((void *)CONFIG_CONT0_PHYS0_END - PAGE_SIZE * 5,
(void *)0xFFFFF000,
1,
MAP_USR_RW,
self)) == 0) {
dbg_printf("sys_map succeeded when trying to "
"map to the kip page ret=%d\n", err);
return -1;
}
return 0;
}
int test_api_unmap(void)
{
int err;
l4id_t self = self_tid();
/*
* Try a valid unmap
*/
if ((err = l4_unmap((void *)CONFIG_CONT0_VIRT0_END - PAGE_SIZE * 5,
1,
self)) < 0) {
dbg_printf("sys_unmap failed on valid request. err=%d\n",
err);
return err;
}
/*
* Try the same unmap, should return ENOMAP
*/
if ((err = l4_unmap((void *)CONFIG_CONT0_VIRT0_END - PAGE_SIZE * 5,
1,
self)) != -ENOMAP) {
dbg_printf("sys_unmap did not return ENOMAP "
"on second unmap of same region. err=%d\n",
err);
return -1;
}
/*
* Try unmapping privileged areas
*/
if ((err = l4_unmap((void *)KERNEL_PAGE, 1, self)) == 0) {
dbg_printf("sys_unmap succeeded on invalid "
"unmap region. err=%d\n", err);
return -1;
}
if ((err = l4_unmap((void *)VECTOR_PAGE, 1, self)) == 0) {
dbg_printf("sys_unmap succeeded on invalid "
"unmap region. err=%d\n", err);
return -1;
}
if ((err = l4_unmap((void *)SYSCALL_PAGE, 1, self)) == 0) {
dbg_printf("sys_unmap succeeded on invalid "
"unmap region. err=%d\n", err);
return -1;
}
/*
* Try unmapping with range rollover
*/
if ((err = l4_unmap((void *)KERNEL_PAGE, 0xFFFFFFFF, self)) == 0) {
dbg_printf("sys_unmap succeeded on invalid "
"unmap region. err=%d\n", err);
return -1;
}
if ((err = l4_unmap((void *)CONFIG_CONT0_VIRT0_END - PAGE_SIZE * 5,
0xFFFFFFFF, self)) == 0) {
dbg_printf("sys_unmap succeeded on invalid "
"unmap region. err=%d\n", err);
return -1;
}
/*
* Try unmapping zero pages
*/
if ((err = l4_unmap((void *)CONFIG_CONT0_VIRT0_END - PAGE_SIZE * 5,
0, self)) == 0) {
dbg_printf("sys_unmap succeeded on invalid "
"unmap region. err=%d\n", err);
return -1;
}
/*
* Try unmapping with invalid id
*/
if ((err = l4_unmap((void *)CONFIG_CONT0_VIRT0_END - PAGE_SIZE * 5,
1, 0xFFFFFFFF)) == 0) {
dbg_printf("sys_unmap succeeded on invalid "
"unmap region. err=%d\n", err);
return -1;
}
return 0;
}
int test_api_map_unmap(void)
{
int err;
if ((err = test_api_map()) < 0)
goto out_err;
if ((err = test_api_unmap()) < 0)
goto out_err;
printf("MAP/UNMAP: -- PASSED --\n");
return 0;
out_err:
printf("MAP/UNMAP: -- FAILED --\n");
return err;
}

View File

@@ -0,0 +1,198 @@
/*
* Empty virtual and physical pages for
* creating test scenarios
*
* Copyright (C) 2010 B Labs Ltd.
*
* Author: Bahadir Balban
*/
#include <l4lib/lib/addr.h>
#include INC_GLUE(memory.h)
#include <l4/generic/cap-types.h>
#include <l4lib/lib/cap.h>
#include <l4/lib/math.h>
#include <stdio.h>
#include <memory.h>
#include <linker.h>
#include <tests.h>
/*
* Declare a statically allocated char buffer
* with enough bitmap size to cover given size
*/
#define DECLARE_IDPOOL(name, size) \
char name[(sizeof(struct id_pool) + ((size >> 12) >> 3))]
struct address_pool virtual_page_pool, physical_page_pool;
#define PAGE_POOL_SIZE SZ_16MB
DECLARE_IDPOOL(virtual_idpool, PAGE_POOL_SIZE);
DECLARE_IDPOOL(physical_idpool, PAGE_POOL_SIZE);
#define virt_to_phys(virtual) ((unsigned long)(virtual) - (unsigned long)(offset))
#define phys_to_virt(physical) ((unsigned long)(physical) + (unsigned long)(offset))
#define TEST_POOL_TOTAL 5
/*
* Test page pool
*/
void test_page_pool(void)
{
void *p[TEST_POOL_TOTAL], *v[TEST_POOL_TOTAL];
/* Allocate test pages */
for (int i = 0; i < TEST_POOL_TOTAL; i++) {
v[i] = virtual_page_new(1);
p[i] = physical_page_new(1);
dbg_printf("Test allocated: Virtual%d: 0x%p, "
"Physical%d, 0x%p\n",
i, v[i], i, p[i]);
}
/* Free test pages */
for (int i = 0; i < TEST_POOL_TOTAL; i++) {
virtual_page_free(v[i], 1);
physical_page_free(p[i], 1);
}
/* Re-allocate test pages */
for (int i = 0; i < TEST_POOL_TOTAL; i++) {
v[i] = virtual_page_new(1);
p[i] = physical_page_new(1);
dbg_printf("Test allocated: Virtual%d: 0x%p, "
"Physical%d, 0x%p\n",
i, v[i], i, p[i]);
}
/* Free test pages */
for (int i = 0; i < TEST_POOL_TOTAL; i++) {
virtual_page_free(v[i], 1);
physical_page_free(p[i], 1);
}
/* Allocate in different lengths */
for (int i = 0; i < TEST_POOL_TOTAL; i++) {
v[i] = virtual_page_new(i);
p[i] = physical_page_new(i);
dbg_printf("Test allocated: Virtual%d: 0x%p, "
"Physical%d, 0x%p\n",
i, v[i], i, p[i]);
}
/* Free test pages in different order */
for (int i = TEST_POOL_TOTAL - 1; i >= 0; i--) {
virtual_page_free(v[i], 1);
physical_page_free(p[i], 1);
}
/* Allocate in different lengths */
for (int i = 0; i < TEST_POOL_TOTAL; i++) {
v[i] = virtual_page_new(i);
p[i] = physical_page_new(i);
dbg_printf("Test allocated: Virtual%d: 0x%p, "
"Physical%d, 0x%p\n",
i, v[i], i, p[i]);
}
/* Free test pages in normal order */
for (int i = 0; i < TEST_POOL_TOTAL; i++) {
virtual_page_free(v[i], 1);
physical_page_free(p[i], 1);
}
}
void page_pool_init(void)
{
struct capability *physcap, *virtcap;
unsigned long phys_start, phys_end;
unsigned long virt_start, virt_end;
/*
* Get physmem capability (Must be only one)
*/
if (!(physcap = cap_get_physmem(CAP_TYPE_MAP_PHYSMEM))) {
printf("FATAL: Could not find a physical memory"
"capability to use as a page pool.\n");
BUG();
}
/*
* Get virtmem capability (Must be only one)
*/
if (!(virtcap = cap_get_by_type(CAP_TYPE_MAP_VIRTMEM))) {
printf("FATAL: Could not find a virtual memory"
"capability to use as a page pool.\n");
BUG();
}
/*
* Now initialize physical and virtual page marks
* from unused pages. Linker script will help us
* on this.
*/
/*
printf("__data_start symbol: %lx\n", (unsigned long)__data_start);
printf("__data_end symbol: %lx\n", (unsigned long)__data_end);
printf("__bss_start symbol: %lx\n", (unsigned long)__bss_start);
printf("__bss_end symbol: %lx\n", (unsigned long)__bss_end);
printf("__stack_start symbol: %lx\n", (unsigned long)__stack_start);
printf("__stack_end symbol: %lx\n", (unsigned long)__stack_end);
printf("__end symbol: %lx\n", (unsigned long)__end);
*/
phys_start = page_align_up(virt_to_phys(__end) +
(unsigned long)lma_start);
phys_end = __pfn_to_addr(physcap->end);
dbg_printf("%s: Initializing physical range 0x%lx - 0x%lx\n",
__FUNCTION__, phys_start, phys_end);
virt_start = page_align_up(__end) + (unsigned long)lma_start;
virt_end = __pfn_to_addr(virtcap->end);
dbg_printf("%s: Initializing virtual range 0x%lx - 0x%lx\n",
__FUNCTION__, virt_start, virt_end);
/* Initialize pools, maximum of PAGE_POOL_SIZE size */
address_pool_init(&virtual_page_pool,
(struct id_pool *)&virtual_idpool,
virt_start, min(virt_end,
virt_start + PAGE_POOL_SIZE));
address_pool_init(&physical_page_pool,
(struct id_pool *)&physical_idpool,
phys_start, min(phys_end,
phys_start + PAGE_POOL_SIZE));
// test_page_pool();
}
/*
* Some tests require page-faulting virtual addresses or
* differing virtual addresses that map onto the same
* physical page. These functions provide these pages.
*/
void *virtual_page_new(int npages)
{
return address_new(&virtual_page_pool, npages, PAGE_SIZE);
}
void *physical_page_new(int npages)
{
return address_new(&physical_page_pool, npages, PAGE_SIZE);
}
void virtual_page_free(void *address, int npages)
{
address_del(&virtual_page_pool, address,
npages, PAGE_SIZE);
}
void physical_page_free(void *address, int npages)
{
address_del(&physical_page_pool, address,
npages, PAGE_SIZE);
}

View File

@@ -0,0 +1,204 @@
/*
* Test l4_mutex_control system call.
*
* Copyright (C) 2010 B Labs Ltd.
*
* Author: Bahadir Balban
*/
#include <l4lib/macros.h>
#include L4LIB_INC_ARCH(syslib.h)
#include L4LIB_INC_ARCH(syscalls.h)
#include <l4lib/lib/thread.h>
#include <l4lib/mutex.h>
#include <tests.h>
#define MUTEX_NTHREADS 8
#define MUTEX_INCREMENTS 200
#define MUTEX_VALUE_TOTAL (MUTEX_NTHREADS * MUTEX_INCREMENTS)
struct mutex_test_data {
struct l4_mutex lock;
int val;
};
static struct mutex_test_data tdata;
static void init_test_data(struct mutex_test_data *tdata)
{
l4_mutex_init(&tdata->lock);
tdata->val = 0;
}
int mutex_thread_non_contending(void *arg)
{
struct mutex_test_data *data =
(struct mutex_test_data *)arg;
l4id_t tid = self_tid();
int err = tid;
for (int i = 0; i < MUTEX_INCREMENTS; i++) {
/* Lock the data structure */
if ((err = l4_mutex_lock(&data->lock)) < 0) {
dbg_printf("Thread %d: Acquiring mutex failed. "
"err = %d\n", tid, err);
return -err;
}
/*
* Increment and release lock
*/
data->val++;
/* Unlock the data structure */
if ((err = l4_mutex_unlock(&data->lock)) < 0) {
dbg_printf("Thread %d: Releasing the mutex failed. "
"err = %d\n", tid, err);
return -err;
}
}
return 0;
}
int mutex_thread_contending(void *arg)
{
struct mutex_test_data *data =
(struct mutex_test_data *)arg;
l4id_t tid = self_tid();
int err = tid;
for (int i = 0; i < MUTEX_INCREMENTS; i++) {
/* Lock the data structure */
if ((err = l4_mutex_lock(&data->lock)) < 0) {
dbg_printf("Thread %d: Acquiring mutex failed. "
"err = %d\n", tid, err);
return -err;
}
/*
* Sleep some time to have some
* threads blocked on the mutex
*/
for (int j = 0; j < 3; j++)
l4_thread_switch(0);
/*
* Increment and release lock
*/
data->val++;
/* Unlock the data structure */
if ((err = l4_mutex_unlock(&data->lock)) < 0) {
dbg_printf("Thread %d: Releasing the mutex failed. "
"err = %d\n", tid, err);
return -err;
}
}
return 0;
}
int test_mutex(int (*mutex_thread)(void *))
{
struct l4_thread *thread[MUTEX_NTHREADS];
int err;
/* Init mutex data */
init_test_data(&tdata);
/*
* Lock the mutex so nobody starts working
*/
if ((err = l4_mutex_lock(&tdata.lock)) < 0) {
dbg_printf("Acquiring mutex failed. "
"err = %d\n", err);
return err;
}
/* Create threads */
for (int i = 0; i < MUTEX_NTHREADS; i++) {
if ((err = thread_create(mutex_thread,
&tdata,
TC_SHARE_SPACE,
&thread[i])) < 0) {
dbg_printf("Thread create failed. "
"err=%d\n", err);
return err;
}
}
/* Unlock the mutex and initiate all workers */
if ((err = l4_mutex_unlock(&tdata.lock)) < 0) {
dbg_printf("Releasing the mutex failed. "
"err = %d\n", err);
return -err;
}
/*
* Wait for all threads to exit successfully
*/
for (int i = 0; i < MUTEX_NTHREADS; i++) {
if ((err = thread_wait(thread[i])) < 0) {
dbg_printf("THREAD_WAIT failed. "
"err=%d\n", err);
return err;
}
}
/*
* Test that lock is in correct state
*/
if (tdata.lock.lock != L4_MUTEX_UNLOCKED) {
dbg_printf("MUTEX is not in unlocked condition "
"after tests. lockval = %d, expected = %d\n",
tdata.lock.lock, L4_MUTEX_UNLOCKED);
return -1;
}
/*
* Test that increments have occured correctly
*/
if (tdata.val != MUTEX_VALUE_TOTAL) {
dbg_printf("Lock-protected value incremented incorrectly "
"after mutex worker threads.\n"
"val = %d, expected = %d\n",
tdata.val,
MUTEX_VALUE_TOTAL);
return -1;
}
if (tdata.val != MUTEX_VALUE_TOTAL) {
dbg_printf("Lock-protected value incremented incorrectly "
"after mutex worker threads.\n"
"val = %d, expected = %d\n",
tdata.val,
MUTEX_VALUE_TOTAL);
return -1;
}
dbg_printf("Mutex test successful.\n");
return 0;
}
int test_api_mutexctrl(void)
{
int err;
if ((err = test_mutex(mutex_thread_contending)) < 0)
goto out_err;
if ((err = test_mutex(mutex_thread_non_contending)) < 0)
goto out_err;
printf("USERSPACE MUTEX: -- PASSED --\n");
return 0;
out_err:
printf("USERSPACE MUTEX: -- FAILED --\n");
return err;
}

View File

@@ -0,0 +1,146 @@
/*
* Some minimal tests for SMP functionality
*
* Copyright (C) 2010 B Labs Ltd.
*
* Author: Bahadir Balban
*/
#include <l4lib/exregs.h>
#include <l4lib/lib/thread.h>
#include <stdio.h>
#include <string.h>
#include <tests.h>
static int new_thread_func(void *args)
{
struct exregs_data exregs;
struct task_ids ids;
int err;
l4_getid(&ids);
#if 0
memset(&exregs, 0, sizeof(exregs));
exregs_set_read(&exregs);
if ((err = l4_exchange_registers(&exregs,
ids.tid)) < 0) {
printf("SMP test: Exregs call failed on %s\n",
__FUNCTION__);
}
dbg_printf("New thread running successfully on cpu %d "
"tid=%d. Exiting...\n", self_tid(),
exregs.cpu_affinity);
#endif
dbg_printf("SMP:New thread running successfully"
"tid=%d. Exiting...\n", self_tid());
return 0;
}
/*
* Create 2 threads on different cpus and run them.
*
* Parent then destroys the child. Parent and child
* are on different cpus.
*/
int test_smp_two_threads(void)
{
struct exregs_data exregs;
struct l4_thread *thread;
int err, err2;
dbg_printf("%s: Creating a new thread\n", __FUNCTION__);
/*
* Create new thread but don't start it
*/
if ((err = thread_create(new_thread_func, 0,
TC_SHARE_SPACE | TC_NOSTART,
&thread)) < 0) {
dbg_printf("THREAD_CREATE failed. "
"err=%d\n", err);
return err;
}
#if 0
dbg_printf("%s: Setting child affinity to %d\n", __FUNCTION__, 1);
/*
* Set its cpu affinity to cpu = 1
*/
memset(&exregs, 0, sizeof(exregs));
exregs_set_affinity(&exregs, 1);
/* Write to affinity field */
if ((err = l4_exchange_registers(&exregs,
thread->ids.tid)) < 0) {
printf("%s: Exregs on setting cpu affinity "
"failed on newly created thread. err=%d\n",
__FUNCTION__, err);
goto out_err;
}
dbg_printf("%s: Running child on other cpu\n", __FUNCTION__);
#endif
/* Start the thread */
l4_thread_control(THREAD_RUN, &thread->ids);
dbg_printf("%s: Waiting on child\n", __FUNCTION__);
/* Wait on the thread */
if ((err = thread_wait(thread)) < 0) {
dbg_printf("THREAD_WAIT failed. "
"err=%d\n", err);
goto out_err;
} else {
dbg_printf("Thread %d exited successfully. ret=%d\n",
thread->ids.tid, err);
}
dbg_printf("%s: Child destroyed successfully\n", __FUNCTION__);
return 0;
out_err:
/*
* Destroy the thread from parent
*/
if ((err2 = thread_destroy(thread)) < 0) {
dbg_printf("THREAD_DESTROY failed. "
"err=%d\n", err2);
return err2;
}
return err;
}
int test_smp_two_spaces(void)
{
return 0;
}
int test_smp_ipc(void)
{
return 0;
}
#if defined (CONFIG_SMP)
int test_smp(void)
{
int err;
if ((err = test_smp_two_threads()) < 0)
return err;
if ((err = test_smp_two_spaces()) < 0)
return err;
if ((err = test_smp_ipc()) < 0)
return err;
return 0;
}
#else /* Not CONFIG_SMP */
int test_smp(void)
{
return 0;
}
#endif /* Endif */

View File

@@ -0,0 +1,249 @@
/*
* Test l4_thread_control system call.
*
* Copyright (C) 2010 B Labs Ltd.
*
* Author: Bahadir Balban
*/
#include <l4lib/lib/thread.h>
#include <stdio.h>
#include <tests.h>
/*
* A secondary thread that tests
* various conditions by taking actions
* told by its parent.
*/
int new_thread_func(void *args)
{
dbg_printf("New thread running successfully. "
"tid=%d\n", self_tid());
return 0;
}
/*
* Thread that exits by doing some number of
* thread switches to ensure parent has a chance
* to wait on it or attempt to destroy it
* The purpose is to test parent-wait before self-destroy.
*/
int delayed_exit_func(void *args)
{
int x = 5;
l4id_t parent = *((l4id_t *)args);
dbg_printf("%s: thread running successfully. "
"tid=%d\n", __FUNCTION__, self_tid());
/*
* Switch to parent a few times to ensure it
* runs and begins to wait on us
*/
while (x--)
l4_thread_switch(parent);
return 5;
}
/*
* Thread that exits immediately
* Purpose is to test parent-wait after self-destroy.
*/
int imm_exit_func(void *args)
{
return 5;
}
/*
* We have 3 thread creation scenarios to test.
*/
struct l4_thread *test_thread_create()
{
struct l4_thread *tptr;
int err;
dbg_printf("%s: Creating thread", __FUNCTION__);
/*
* Create a thread in the same space
*/
if ((err = thread_create(new_thread_func, 0,
TC_SHARE_SPACE,
&tptr)) < 0) {
dbg_printf("Thread create failed. "
"err=%d\n", err);
return PTR_ERR(err);
}
dbg_printf("Thread created successfully. "
"tid=%d\n", tptr->ids.tid);
return tptr;
}
/*
* Test thread run/resume, suspend
*
* We don't test recycle as that would delete the current
* address space
*/
int test_thread_actions(struct l4_thread *thread)
{
int err;
dbg_printf("Suspending thread "
"tid=%d\n", thread->ids.tid);
/*
* Suspend/resume the thread
*/
if ((err = l4_thread_control(THREAD_SUSPEND, &thread->ids)) < 0) {
dbg_printf("THREAD_SUSPEND failed. "
"err=%d\n", err);
return err;
}
dbg_printf("Suspend OK. Resuming thread "
"tid=%d\n", thread->ids.tid);
if ((err = l4_thread_control(THREAD_RUN, &thread->ids)) < 0) {
dbg_printf("THREAD_RUN failed. "
"err=%d\n", err);
return err;
}
dbg_printf("Resume OK."
"tid=%d\n", thread->ids.tid);
return 0;
}
/*
* Test thread destruction
*/
int test_thread_destroy(struct l4_thread *thread)
{
int err;
l4id_t id_self = self_tid();
dbg_printf("Destroying thread."
"tid=%d\n", thread->ids.tid);
/*
* Destroy the thread from parent
*/
if ((err = thread_destroy(thread)) < 0) {
dbg_printf("THREAD_DESTROY failed. "
"err=%d\n", err);
return err;
}
dbg_printf("%s: Destroy OK\n", __FUNCTION__);
dbg_printf("%s: Creating new thread\n", __FUNCTION__);
/*
* Create a new thread
* and tell it to destroy itself
* by adding a delay, then wait on it.
*
* Delay ensures we test the case that
* wait occurs before thread is destroyed.
*/
if ((err = thread_create(delayed_exit_func, &id_self,
TC_SHARE_SPACE,
&thread)) < 0) {
dbg_printf("THREAD_CREATE failed. "
"err=%d\n", err);
return err;
}
dbg_printf("Thread created successfully. "
"tid=%d\n", thread->ids.tid);
dbg_printf("Waiting on thread, "
"tid=%d\n", thread->ids.tid);
/* Wait on the thread */
if ((err = thread_wait(thread)) < 0) {
dbg_printf("THREAD_WAIT failed. "
"err=%d\n", err);
return err;
} else {
dbg_printf("Thread %d exited successfully. ret=%d\n",
thread->ids.tid, err);
}
/*
* Create a new thread
* and tell it to destroy itself
* immediately, add a delay and
* then wait on it.
*
* Delay ensures we test the case that
* wait occurs after thread is destroyed.
*/
if ((err = thread_create(imm_exit_func, 0,
TC_SHARE_SPACE,
&thread)) < 0) {
dbg_printf("THREAD_WAIT failed. "
"err=%d\n", err);
return err;
}
/* Wait on the thread */
if ((err = thread_wait(thread)) < 0) {
dbg_printf("THREAD_WAIT failed. "
"err=%d\n", err);
return err;
} else {
dbg_printf("Thread %d exited successfully. ret=%d\n",
thread->ids.tid, err);
}
return 0;
}
/*
* TODO: In order to test null pointers a separate
* thread who is paged by the main one should attempt
* to pass a null ptr.
*/
int test_thread_invalid(struct l4_thread *thread)
{
return 0;
}
int test_api_tctrl(void)
{
struct l4_thread *thread;
int err;
/* Test thread create */
if (IS_ERR(thread = test_thread_create())) {
err = (int)thread;
goto out_err;
}
/* Test thread actions */
if ((err = test_thread_actions(thread)) < 0)
goto out_err;
/* Test thread destruction */
if ((err = test_thread_destroy(thread)) < 0)
goto out_err;
/* Test thread invalid input */
if ((err = test_thread_invalid(thread)) < 0)
goto out_err;
printf("THREAD CONTROL: -- PASSED --\n");
return 0;
out_err:
printf("THREAD CONTROL: -- FAILED --\n");
return err;
}

View File

@@ -0,0 +1,65 @@
/*
* ARMv5 specific functions
*
* Copyright (C) 2008 - 2010 B Labs Ltd.
*/
#include <task.h>
#include <vm_area.h>
#include <l4lib/exregs.h>
#include __INC_ARCH(mm.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 & PTE_PROT_MASK;
unsigned int ro_flags = __MAP_USR_RO & 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;
}
/*
* 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);
}

View File

@@ -0,0 +1,75 @@
/*
* ARMv7 specific functions
*
* Copyright (C) 2008 - 2010 B Labs Ltd.
*/
#include <macros.h>
#include <l4lib/exregs.h>
#include <fault.h>
#include INC_SUBARCH(mm.h)
#include INC_SUBARCH(exception.h)
/* Get simplified access permissions */
int pte_get_access_simple(pte_t pte)
{
/* Place AP[2] and AP[1] in [1:0] positions and return */
return (((pte >> PTE_AP2_BIT) & 1) << 1)
| ((pte >> PTE_AP1_BIT) & 1);
}
int is_translation_fault(u32 fsr)
{
return (fsr & FSR_FS_MASK) == ABORT_TRANSLATION_PAGE;
}
unsigned int vm_prot_flags(pte_t pte, u32 fsr)
{
unsigned int pte_prot_flags = 0;
/* Translation fault means no permissions */
if (is_translation_fault(fsr))
return VM_NONE;
/* Check simplified permission bits */
switch (pte_get_access_simple(pte)) {
case AP_SIMPLE_USER_RW_KERN_RW:
pte_prot_flags |= VM_WRITE;
case AP_SIMPLE_USER_RO_KERN_RO:
pte_prot_flags |= VM_READ;
/* Also, check exec never bit */
if (!(pte & (1 << PTE_XN_BIT)))
pte_prot_flags |= VM_EXEC;
break;
case AP_SIMPLE_USER_NONE_KERN_RW:
case AP_SIMPLE_USER_NONE_KERN_RO:
default:
pte_prot_flags = VM_NONE;
break;
}
return pte_prot_flags;
}
void set_generic_fault_params(struct fault_data *fault)
{
fault->pte_flags = vm_prot_flags(fault->kdata->pte, fault->kdata->fsr);
fault->reason = 0;
/*
* Prefetch fault denotes exec fault.
*/
if (is_prefetch_abort(fault->kdata->fsr)) {
fault->reason |= VM_EXEC;
fault->address = fault->kdata->faulty_pc;
} else {
fault->address = fault->kdata->far;
/* Write-not-read bit determines fault */
if (fault->kdata->fsr & (1 << DFSR_WNR_BIT))
fault->reason |= VM_WRITE;
else
fault->reason |= VM_READ;
}
}

View File

@@ -0,0 +1,106 @@
/*
* Capability-related userspace helpers
*
* Copyright (C) 2009 B Labs Ltd.
*/
#include <stdio.h>
#include <l4lib/lib/cap.h>
#include <l4lib/macros.h>
#include L4LIB_INC_ARCH(syscalls.h)
#if 0
static struct capability cap_array[30];
struct cap_group {
struct cap_list virtmem;
struct cap_list physmem;
struct cap_list threadpool;
struct cap_list tctrl;
struct cap_list exregs;
struct cap_list ipc;
struct cap_list mutex;
struct cap_list sched;
struct cap_list mutexpool;
struct cap_list spacepool;
struct cap_list cappool;
};
static inline struct capability *cap_get_thread()
{
}
static inline struct capability *cap_get_space()
{
}
static inline struct capability *cap_get_ipc()
{
}
static inline struct capability *cap_get_virtmem()
{
}
static inline struct capability *cap_get_physmem()
{
}
static inline struct capability *cap_get_physmem(unsigned long phys)
{
}
static inline struct capability *cap_get_virtmem(unsigned long virt)
{
}
static inline struct capability *cap_get_byid(l4id_t id)
{
}
void cap_share_single(struct capability *orig, struct capability *share, l4id_t target, unsigned int flags)
{
}
void cap_grant_single(struct capability *orig, struct capability *share, l4id_t target, unsigned int flags)
{
}
int caps_read_all(void)
{
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");
BUG();
}
/* Read all capabilities */
if ((err = l4_capability_control(CAP_CONTROL_READ,
0, cap_array)) < 0) {
printf("l4_capability resource_control() reading of "
"capabilities failed.\n Could not "
"complete CAP_CONTROL_READ_CAPS request.\n");
BUG();
}
//cap_array_print(ncaps, caparray);
return 0;
}
#endif

View File

@@ -0,0 +1,148 @@
#include <thread.h>
#include <container.h>
#include <capability.h>
#include <tests.h>
#include <l4/api/errno.h>
#include <l4lib/macros.h>
#include L4LIB_INC_ARCH(syslib.h)
#include <l4/api/capability.h>
int simple_pager_thread(void *arg)
{
int err;
int res = *(int *)arg;
struct task_ids ids;
int testres = 0;
l4_getid(&ids);
printf("Thread spawned from pager, \
trying to create new thread.\n");
err = l4_thread_control(THREAD_CREATE |
TC_SHARE_SPACE, &ids);
if (res == 0)
if (err == -ENOCAP ||
err == -ENOMEM) {
printf("Creation failed with %d "
"as expected.\n", err);
testres = 0;
} else {
printf("Creation was supposed to fail "
"with %d or %d, but err = %d\n",
-ENOMEM, -ENOCAP, err);
testres = 1;
}
else
if (err == 0) {
// printf("Creation succeeded as expected.\n");
testres = 0;
} else {
printf("Creation was supposed to succeed, "
"but err = %d\n", err);
testres = 1;
}
/* Destroy thread we created */
if (err == 0 &&
res == 0)
l4_thread_control(THREAD_DESTROY, &ids);
/* Destroy self */
l4_exit(testres);
return 0;
}
int wait_check_test(struct task_ids *ids)
{
int result;
/* Wait for thread to finish */
result = l4_thread_control(THREAD_WAIT, ids);
if (result < 0) {
printf("Waiting on (%d)'s exit failed.\n", ids->tid);
return -1;
} else if (result > 0) {
printf("Top-level test has failed\n");
}
/* Else it is a success */
return 0;
}
int capability_test(void)
{
int err;
struct task_ids ids;
int TEST_MUST_FAIL = 0;
//int TEST_MUST_SUCCEED = 1;
/* Read pager capabilities */
caps_read_all();
/*
* Create new thread that will attempt
* a pager privileged operation
*/
if ((err = thread_create(simple_pager_thread,
&TEST_MUST_FAIL,
TC_SHARE_SPACE, &ids)) < 0) {
printf("Top-level simple_pager creation failed.\n");
goto out_err;
}
printf("waititng for result\n");
/* Wait for test to finish and check result */
if (wait_check_test(&ids) < 0)
goto out_err;
#if 0
/* Destroy test thread */
if ((err = l4_thread_control(THREAD_DESTROY, &ids)) < 0) {
printf("Destruction of top-level simple_pager failed.\n");
BUG();
}
/*
* Share operations with the same thread
* group
*/
if ((err = l4_capability_control(CAP_CONTROL_SHARE,
CAP_SHARE_CONTAINER, 0)) < 0) {
printf("Sharing capability with thread group failed.\n");
goto out_err;
}
/*
* Create new thread that will attempt a pager privileged
* operation. This should succeed as we shared caps with
* the thread group.
*/
if ((err = thread_create(simple_pager_thread,
&TEST_MUST_SUCCEED,
TC_SHARE_SPACE |
TC_SHARE_GROUP, &ids)) < 0) {
printf("Top-level simple_pager creation failed.\n");
goto out_err;
}
/* Wait for test to finish and check result */
if (wait_check_test(&ids) < 0)
goto out_err;
/* Destroy test thread */
if ((err = l4_thread_control(THREAD_DESTROY, &ids)) < 0) {
printf("Destruction of top-level simple_pager failed.\n");
BUG();
}
#endif
printf("Capability Sharing Test -- PASSED --\n");
return 0;
out_err:
printf("Capability Sharing Test -- FAILED --\n");
return 0;
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright (C) 2010 B Labs Ltd.
*
* Tests client/server style container setup
*
* Author: Bahadir Balban
*/
#include <tests.h>
/*
* A container can be set up in many different combinations
* of hierarchy where the hierarchical differences between
* the threads are determined by a finely grained capability
* configuration.
*
* However, this boils down to two main sets of hierarchical
* setup: client/server or multithreaded/standalone entities.
*
* This test tests the client/server style hierarchical set up.
*/
int test_cli_serv(void)
{
/*
* Create a child thread in a new address space.
* copying current pager's page tables to child
*/
/* Copy current pager's all sections to child pages */
/*
* Set up child's registers to execute the special
* child entry function
*/
/*
* Start the child
*/
/*
* Interact with the child:
*
* Handle short, full, extended ipc
*
* Handle page fault
*/
/*
* Destroy child
*/
return 0;
}

View File

@@ -0,0 +1,220 @@
#if 0
int mutex_user_thread(void *arg)
{
/* TODO: Create and access a mutex */
}
int independent_thread(void *arg)
{
/* TODO: Do whatever syscall available */
}
/*
* This example demonstrates how the capability-based
* security model can be bypassed and taken out of the
* way for the sake of implementing an application that
* doesn't worry too much about security.
*
* The benefit is that the user does neither worry about
* capabilities nor using its api to design correctly
* secure systems. The downside is that the system is
* less security-enforced, i.e. all parties must be
* trusted.
*/
int multi_threaded_nocaps_example(void)
{
/*
* We are the first pager with capabilities to
* create new tasks, spaces, in its own container.
*/
pager_read_caps();
/*
* We have all our capabilities private to us.
*
* If we create a new task, it won't be able to
* any kernel operations that we can do, because
* we hold our capabilities privately.
*
* In order to settle all capability access issues
* once and for all threads we will create and manage,
* we share our capabilities with the most global
* collection possible.
*/
/*
* Share all of our capabilities with all threads
* in the same container.
*
* From this point onwards, any thread we create and
* manage (i.e. whose container id is equal to our
* container id) will have the ability to leverage
* all of our capabilities as defined for us at
* configuration time.
*/
l4_cap_share(0, CAP_SHARE_CONTAINER | CAP_SHARE_ALL, self_tid());
/*
* Lets try it.
*
* Create new thread that we don't have any hieararchical
* relationship, i.e. one that is a pager of itself, one
* that runs in a new address space, and in a new thread
* group. All we share is the container.
*/
if ((err = thread_create(independent_thread, 0,
TC_NO_SHARING, &ids)) < 0) {
printf("mutex_user_thread creation failed.\n");
goto out_err;
}
/*
* We can inspect the new thread by doing an ipc to it.
* NOTE:
*
* We are able to send to this thread from the start,
* as we had a container-wide ipc capability defined at
* config-time.
*
* But we would not be able to receive from it, if we
* did not share this capability with the container. It
* would have no rights to do a send to us. But because
* we're in the same container, and we shared our
* capability, it now can.
*/
if ((err = l4_recv(ids->tid, ids->tid, 0)) < 0) {
print_err("%s: L4 IPC Error: %d.\n", __FUNCTION__, fd);
goto out_err;
}
/*
* From this point onwards we can create more threads
* without worrying about whether they have the caps
* to do certain ops, and the caps api. because we shared
* them all at the beginning.
*/
out_err:
BUG();
}
/*
* This example demonstrates how a pager would
* share part of its capabilities on the system
* with its children.
*
* The example includes sharing of a mutex
* capability with a paged-child.
*/
int multi_threaded_capability_sharing_example(void)
{
struct capability *mutex_cap;
int thread_retval;
/*
* We are the first pager with capabilities to
* create new tasks, spaces, in its own container.
*/
pager_read_caps();
/*
* We have all our capabilities private to us.
*
* If we create a new task, it won't be able to
* create and use userspace mutexes, because we
* hold mutex capabilities privately.
*
* Lets try it.
*/
/*
* Create new thread that will attempt
* a mutex operation, and die on us with a
* negative return code if it fails.
*/
if ((err = thread_create(mutex_user_thread, 0,
TC_SHARE_SPACE |
TC_AS_PAGER, &ids)) < 0) {
printf("mutex_user_thread creation failed.\n");
goto out_err;
}
/* Check on how the thread has done */
if ((err = l4_thread_wait_on(ids, &thread_retval)) < 0) {
print("Waiting on thread %d failed. err = %d\n",
ids->tid, err);
goto out_err;
}
if (thread_retval == 0) {
printf("Thread %d returned with success, where "
"we expected failure.\n", ids->tid);
goto out_err;
}
/*
* Therefore, we share our capabilities with a
* collection so that our capabilities may be also
* used by them.
*/
/* Get our private mutex cap */
mutex_cap = cap_get(CAP_TYPE_MUTEX);
/* We have ability to create and use this many mutexes */
printf("%s: We have ability to create/use %d mutexes\n",
self_tid(), mutex_cap->size);
/* Split it */
cap_new = cap_split(mutex_cap, 10, CAP_SPLIT_SIZE);
/*
* Share the split part with paged-children.
*
* From this point onwards, any thread we create and
* manage (i.e. whose pagerid == self_tid()) will have
* the ability to use mutexes, as defined by cap_new
* we created.
*/
l4_cap_share(cap_new, CAP_SHARE_PGGROUP, self_tid());
/*
* Create new thread that will attempt
* a mutex operation, and die on us with a
* negative return code if it fails.
*/
if ((err = thread_create(mutex_user_thread, 0,
TC_SHARE_SPACE |
TC_AS_PAGER, &ids)) < 0) {
printf("mutex_user_thread creation failed.\n");
goto out_err;
}
/* Check on how the thread has done */
if ((err = l4_thread_wait_on(ids, &thread_retval)) < 0) {
printf("Waiting on thread %d failed. err = %d\n",
ids->tid, err);
goto out_err;
}
if (thread_retval < 0) {
printf("Thread %d returned with failure, where "
"we expected success.\n", ids->tid);
goto out_err;
}
out_err:
BUG();
}
#endif

View File

@@ -0,0 +1,47 @@
/*
* Copyright (C) 2010 B Labs Ltd.
*
* Tests client/server style container setup
*
* Author: Bahadir Balban
*/
#include <tests.h>
/*
* A container can be set up in many different combinations
* of hierarchy where the hierarchical differences between
* the threads are determined by a finely grained capability
* configuration.
*
* However, this boils down to two main sets of hierarchical
* setup: client/server or multithreaded/standalone entities.
*
* This test tests the multithreaded/standalone style container
* set up.
*/
int test_mthread(void)
{
/* Create multiple threads in same space */
/*
* Set up childs' registers to execute the special
* child entry function
*/
/*
* Start the child
*/
/*
* Run child threads and interact
*
* Handle short, full, extended ipc
*/
/*
* Destroy child
*/
return 0;
}

View File

@@ -0,0 +1,58 @@
/*
* Test cpu cycles using platform timer
* ticks.
*
* Copyright (C) 2010 B Labs Ltd.
*
* Author: Bahadir Balban
*/
#include <l4lib/macros.h>
#include L4LIB_INC_ARCH(syslib.h)
#include L4LIB_INC_ARCH(syscalls.h)
#include <l4lib/lib/thread.h>
#include <l4lib/perfmon.h>
#include <perf.h>
#include <tests.h>
#include <string.h>
#include <timer.h>
void platform_measure_cpu_cycles()
{
/* Initialize the timer */
const int load_value = 1000;
int mhz_top, mhz_bot, temp;
int cyccnt;
/* Make sure timer is disabled */
timer_stop(timer_base);
/* Load the timer with ticks value */
timer_load(load_value, timer_base);
/* One shot, 32 bits, no irqs */
timer_init_oneshot(timer_base);
/* Start the timer */
timer_start(timer_base);
/* Start counter */
perfmon_reset_start_cyccnt();
/* Wait until 0 */
while (timer_read(timer_base) != 0)
;
cyccnt = perfmon_read_cyccnt();
/* Fixed-point accuracy on bottom digit */
temp = cyccnt * 64 * 10 / load_value;
mhz_top = temp / 10;
mhz_bot = temp - mhz_top * 10;
//printk("Perfmon: %u cycles/%dMhz\n",
// cyccnt * 64, timer_load);
printk("%s: %d.%d MHz CPU speed measured by timer REFCLK at 1MHz\n",
__KERNELNAME__, mhz_top, mhz_bot);
}

View File

@@ -0,0 +1,134 @@
/*
* Copyright (C) 2010 B Labs Ltd.
*
* l4_exchange_registers performance tests
*
* Author: Bahadir Balban
*/
#include <l4lib/macros.h>
#include L4LIB_INC_ARCH(syslib.h)
#include L4LIB_INC_ARCH(syscalls.h)
#include <l4lib/lib/thread.h>
#include <l4lib/perfmon.h>
#include <l4lib/exregs.h>
#include <string.h>
#include <stdio.h>
#include <tests.h>
#include <perf.h>
struct perfmon_cycles l4_exregs_cycles;
#define PERFTEST_EXREGS_COUNT 100
int perf_measure_exregs(void)
{
struct task_ids ids;
struct exregs_data exregs[2];
int err;
/* Get own space id */
l4_getid(&ids);
/*
* Initialize cycle structures
*/
memset(&l4_exregs_cycles, 0, sizeof (struct perfmon_cycles));
l4_exregs_cycles.min = ~0; /* Init as maximum possible */
/*
* Create a thread in the same space.
* Thread is not runnable.
*/
if ((err = l4_thread_control(THREAD_CREATE | TC_SHARE_SPACE,
&ids)) < 0) {
dbg_printf("Thread create failed. "
"err=%d\n", err);
return err;
}
dbg_printf("Thread created successfully. "
"tid=%d\n", ids.tid);
/*
* Prepare a context part full of 0xFF
*/
memset(&exregs[0].context, 0xFF, sizeof(exregs[1].context));
exregs[0].valid_vect = 0xFFFFFFFF;
dbg_printf("Starting l4_exregs write measurement\n");
for (int i = 0; i < PERFTEST_EXREGS_COUNT; i++) {
perfmon_reset_start_cyccnt();
/* Write to context */
if ((err = l4_exchange_registers(&exregs[0], ids.tid)) < 0)
goto out;
perfmon_record_cycles(&l4_exregs_cycles,
"l4_exchange_registers");
}
/* Calculate average */
l4_exregs_cycles.avg = l4_exregs_cycles.total / l4_exregs_cycles.ops;
/*
* Print results
*/
printf("PERFMON: %s took %llu min, %llu max, %llu avg, "
"%llu total microseconds in %llu ops.\n",
"l4_exchange_registers()/WRITE",
l4_exregs_cycles.min * USEC_MULTIPLIER,
l4_exregs_cycles.max * USEC_MULTIPLIER,
l4_exregs_cycles.avg * USEC_MULTIPLIER,
l4_exregs_cycles.total * USEC_MULTIPLIER,
l4_exregs_cycles.ops);
/*
* Prepare a context part full of 0xFF
*/
memset(&exregs[0].context, 0xFF, sizeof(exregs[1].context));
exregs[0].valid_vect = 0xFFFFFFFF;
dbg_printf("Starting l4_exregs read measurement\n");
for (int i = 0; i < PERFTEST_EXREGS_COUNT; i++) {
/* Set the other as read-all */
exregs_set_read(&exregs[1]);
exregs[1].valid_vect = 0xFFFFFFFF;
if ((err = l4_exchange_registers(&exregs[1],
ids.tid)) < 0)
goto out;
}
/*
* Read back all context and compare results
*/
if (memcmp(&exregs[0].context, &exregs[1].context,
sizeof(exregs[0].context))) {
err = -1;
goto out;
}
/* Calculate average */
l4_exregs_cycles.avg = l4_exregs_cycles.total / l4_exregs_cycles.ops;
/*
* Print results
*/
printf("PERFMON: %s took %llu min, %llu max, %llu avg, "
"%llu total microseconds in %llu ops.\n",
"l4_exchange_registers()/READ",
l4_exregs_cycles.min * USEC_MULTIPLIER,
l4_exregs_cycles.max * USEC_MULTIPLIER,
l4_exregs_cycles.avg * USEC_MULTIPLIER,
l4_exregs_cycles.total * USEC_MULTIPLIER,
l4_exregs_cycles.ops);
out:
/*
* Destroy the thread
*/
if ((err = l4_thread_control(THREAD_DESTROY, &ids)) < 0) {
dbg_printf("Thread destroy failed. err=%d\n",
err);
}
return 0;
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright (C) 2010 B Labs Ltd.
*
* l4_getid performance tests
*
* Author: Bahadir Balban
*/
#include <l4lib/macros.h>
#include L4LIB_INC_ARCH(syslib.h)
#include L4LIB_INC_ARCH(syscalls.h)
#include <l4lib/lib/thread.h>
#include <l4lib/perfmon.h>
#include <perf.h>
#include <tests.h>
#include <string.h>
#include <timer.h>
struct perfmon_cycles l4_getid_cycles;
#define PERFTEST_GETID_COUNT 100
/*
* Measure l4_getid by timer ticks
*/
void perf_measure_getid_ticks(void)
{
const int timer_ldval = 0xFFFFFFFF;
unsigned int timer_val, timer_stamp = 0xFFFFFFFF;
unsigned int min = ~0, max = 0, last = 0, total = 0, ops = 0;
struct task_ids ids;
/* Make sure timer is disabled */
timer_stop(timer_base);
/* Configure timer as one shot */
timer_init_oneshot(timer_base);
/* Load the timer with ticks value */
timer_load(timer_ldval, timer_base);
/* Start the timer */
printf("Starting the l4_getid timer tick test.\n");
timer_start(timer_base);
/* Do the operation */
for (int i = 0; i < PERFTEST_GETID_COUNT; i++) {
l4_getid(&ids);
timer_val = timer_read(timer_base);
last = timer_stamp - timer_val;
timer_stamp = timer_val;
if (min > last)
min = last;
if (max < last)
max = last;
ops++;
total += last;
}
printf("TIMER: l4_getid took each %u min, %u max, %u avg,\n"
"%u total microseconds, and %u total ops\n", min,
max, total/ops, total, ops);
}
/*
* Measure l4_getid by cpu cycles
*/
void perf_measure_getid(void)
{
struct task_ids ids;
/*
* Initialize structures
*/
memset(&l4_getid_cycles, 0, sizeof (l4_getid_cycles));
l4_getid_cycles.min = ~0; /* Init as maximum possible */
/*
* Do the test
*/
printf("Starting the l4_getid cycle counter test.\n");
for (int i = 0; i < PERFTEST_GETID_COUNT; i++) {
perfmon_reset_start_cyccnt();
l4_getid(&ids);
perfmon_record_cycles(&l4_getid_cycles,
"l4_getid");
}
/*
* Calculate average
*/
l4_getid_cycles.avg = l4_getid_cycles.total / l4_getid_cycles.ops;
/*
* Print results
*/
printf("PERFMON: %s took %llu min, %llu max, %llu avg, "
"%llu total microseconds in %llu ops.\n",
"l4_getid()",
l4_getid_cycles.min * USEC_MULTIPLIER,
l4_getid_cycles.max * USEC_MULTIPLIER,
l4_getid_cycles.avg * USEC_MULTIPLIER,
l4_getid_cycles.total * USEC_MULTIPLIER,
l4_getid_cycles.ops);
}

View File

@@ -0,0 +1,12 @@
/*
* Copyright (C) 2010 B Labs Ltd.
*
* l4_ipc performance tests
*
* Author: Bahadir Balban
*/
void perf_measure_ipc(void)
{
}

View File

@@ -0,0 +1,17 @@
/*
* Copyright (C) 2010 B Labs Ltd.
*
* l4_map/l4_unmap performance tests
*
* Author: Bahadir Balban
*/
void perf_measure_map(void)
{
}
void perf_measure_unmap(void)
{
}

View File

@@ -0,0 +1,11 @@
/*
* Copyright (C) 2010 B Labs Ltd.
*
* l4_mutex performance tests
*
* Author: Bahadir Balban
*/
void perf_measure_mutex(void)
{
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2010 B Labs Ltd.
*
* API performance tests
*
* Author: Bahadir Balban
*/
#include <tests.h>
#include <perf.h>
#include <timer.h>
/*
* Tests all api functions by performance
*/
int test_performance(void)
{
perf_timer_init();
platform_measure_cpu_cycles();
perf_measure_getid_ticks();
perf_measure_getid();
perf_measure_tctrl();
perf_measure_exregs();
perf_measure_ipc();
perf_measure_map();
perf_measure_unmap();
perf_measure_mutex();
return 0;
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright (C) 2010 B Labs Ltd.
*
* l4_getid performance tests
*
* Author: Bahadir Balban
*/
#include <l4lib/macros.h>
#include L4LIB_INC_ARCH(syslib.h)
#include L4LIB_INC_ARCH(syscalls.h)
#include <l4lib/lib/thread.h>
#include <l4lib/perfmon.h>
#include <perf.h>
#include <tests.h>
#include <string.h>
struct perfmon_cycles simple_cycles;
#define PERFTEST_SIMPLE_LOOP 2000
void perf_test_simple(void)
{
dbg_printf("%s: This will test the cycle count of basic loops.\n",
__FUNCTION__);
/*
* Initialize structures
*/
memset(&simple_cycles, 0, sizeof(struct perfmon_cycles));
simple_cycles.min = ~0; /* Init as maximum possible */
/*
* Do the test
*/
perfmon_reset_start_cyccnt();
for (int i = 0; i < PERFTEST_SIMPLE_LOOP; i++)
;
perfmon_record_cycles(&simple_cycles,"empty_loop");
/*
* Calculate average
*/
simple_cycles.avg = simple_cycles.total / simple_cycles.ops;
/*
* Print results
*/
printf("%s took %llu min, %llu max, %llu avg, in %llu ops.\n",
"simple loop",
simple_cycles.min * USEC_MULTIPLIER,
simple_cycles.max * USEC_MULTIPLIER,
simple_cycles.avg * USEC_MULTIPLIER,
simple_cycles.ops);
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright (C) 2010 B Labs Ltd.
*
* l4_thread_control performance tests
*
* Author: Bahadir Balban
*/
#include <l4lib/macros.h>
#include L4LIB_INC_ARCH(syslib.h)
#include L4LIB_INC_ARCH(syscalls.h)
#include <l4lib/lib/thread.h>
#include <l4lib/perfmon.h>
#include <perf.h>
#include <tests.h>
#include <string.h>
struct perfmon_cycles tctrl_cycles;
#define PERFTEST_THREAD_CREATE 50
void perf_measure_tctrl(void)
{
struct task_ids ids[PERFTEST_THREAD_CREATE];
struct task_ids selfids;
l4_getid(&selfids);
/*
* Initialize structures
*/
memset(&tctrl_cycles, 0, sizeof (struct perfmon_cycles));
tctrl_cycles.min = ~0; /* Init as maximum possible */
/*
* Thread create test
*/
for (int i = 0; i < PERFTEST_THREAD_CREATE; i++) {
perfmon_reset_start_cyccnt();
l4_thread_control(THREAD_CREATE | TC_SHARE_SPACE, &selfids);
perfmon_record_cycles(&tctrl_cycles, "THREAD_CREATE");
/* Copy ids of created task */
memcpy(&ids[i], &selfids, sizeof(struct task_ids));
}
/*
* Calculate average
*/
tctrl_cycles.avg = tctrl_cycles.total / tctrl_cycles.ops;
/*
* Print results
*/
printf("%s took %llu min, %llu max, %llu avg, in %llu ops.\n",
"THREAD_CREATE",
tctrl_cycles.min * USEC_MULTIPLIER,
tctrl_cycles.max * USEC_MULTIPLIER,
tctrl_cycles.avg * USEC_MULTIPLIER,
tctrl_cycles.ops);
/*
* Thread destroy test
*/
for (int i = 0; i < PERFTEST_THREAD_CREATE; i++) {
perfmon_reset_start_cyccnt();
l4_thread_control(THREAD_DESTROY, &ids[i]);
perfmon_record_cycles(&tctrl_cycles,"THREAD_DESTROY");
}
/*
* Calculate average
*/
tctrl_cycles.avg = tctrl_cycles.total / tctrl_cycles.ops;
/*
* Print results
*/
printf("%s took %llu min, %llu max, %llu avg, in %llu ops.\n",
"THREAD_DESTROY",
tctrl_cycles.min * USEC_MULTIPLIER,
tctrl_cycles.max * USEC_MULTIPLIER,
tctrl_cycles.avg * USEC_MULTIPLIER,
tctrl_cycles.ops);
}

View File

@@ -0,0 +1,40 @@
/*
* Initialize platform timer virtual address
*
* Copyright (C) 2010 B Labs Ltd.
*
* Bahadir Balban
*/
#include <perf.h>
#include <linker.h>
#include <l4lib/macros.h>
#include L4LIB_INC_ARCH(syslib.h)
#include L4LIB_INC_ARCH(syscalls.h)
/* Note this must be obtained from the capability */
#define TIMER_PHYSICAL_BASE 0x10012000
unsigned long timer_base;
void perf_timer_init(void)
{
int err;
struct task_ids ids;
l4_getid(&ids);
/* Initialize timer base */
timer_base = page_align_up(__stack);
/* Map timer base */
if ((err = l4_map((void *)TIMER_PHYSICAL_BASE,
(void *)timer_base,
1, MAP_USR_IO, ids.tid)) < 0) {
printf("FATAL: Performance tests: Could not map "
"timer.\ntimer must be selected as a "
"container capability. err=%d\n",
err);
BUG();
}
}

View File

View File

@@ -0,0 +1,114 @@
TESTING HOWTO
By Bahadir Balban
HOW TO ADD TESTS
Tests in this project are meant to test basic functionality of the microkernel
but cover test cases comprehensively. The special trait with these tests is
that each is meant to test only one aspect of the microkernel, rather than
trying to test multiple aspects at once.
A benefit of having this approach is that when a test fails, it is easier to
spot the problem as opposed to a failure that is caused by a complex
interaction of multiple aspects of the microkernel.
The software must be also tested for more complex interactions, but these
tests are not the place to create them. When a new test is added, it must cover
a valid test case but the test itself must be as shallow as possible.
EXAMPLE
A thread creation test, while using other aspects of the microkernel such as
the l4_exchange_registers system call, should only test thread creation aspect
of the microkernel. A separate l4_exchange_registers test would need to be
created who then uses the thread creation aspect of the microkernel, but only
testing the parameters to l4_exchange_registers system call, covering it
exhaustively. In essence, each test must be shallow in itself, and have a single
focus.
Here's a step-by-step approach to testing:
1.) Create one test that tests one thing well.
This is about the above paragraphs. Test one aspect, so we know that that
aspect is functional. When there are lots of changes to code and many
invariables, this will make sure that these tests are one solid invariable
in the equation. They should be as deterministic and simple as possible
so that other variants can be debugged using the tests as anchor points.
Each test must be significantly simple. E.g. If I have 2 test cases that
require slightly differing input to a test function, I would not use a
loop of i = 0..2 where parameters change when i == 0 and i == 1. That
would be difficult to understand, and complicated. Instead, each of those
inputs should be 2 test cases. This way, I focus %100 on the debugged code,
I don't put a tiny fraction of my focus trying to understand the test.
2.) Number (1) is unexpectedly hard to achieve. Especially a person who is
accustomed to design complex systems that work will have a hard time in
applying an approach that is completely the opposite. Make sure to stop
thinking smart and get used to thinking not moderately dumb but very dumb.
3.) Make sure to escalate errors properly.
When porting to a new architecture or where there are lots of variables,
your tests will fail. It is an extra burden to find out how. Error
escalation provides a quick and easy means to provide error details.
4.) Make sure to print only when necessary.
Tests should print out values only when a tested aspect goes wrong.
a) It should describe what the test was trying to do, i.e. describe what
was expected to happen. This way it is quicker to see expected results.
b) It should describe what has happend, contrary to expected. This should
tell what went wrong.
c) It should tell no more. Printout clutter is a huge negative factor in
making tests effective.
4.) Complicating the tests:
It is often necessary that simple tests must be the building blocks for
more complicated tests so that complicated interactions in the system
can be tested.
Complicating the test cases should be done with a systematic approach:
a) Tests are like building blocks. Each test that tests one aspect of the
software should be designed as a rock solid way of testing that one
aspect. See (1) above for the first step. Design extremely simple tests.
b) A design should be carefully made such that multiple aspects as
described in (a) provide a highly deterministic and reproducable
sequence of events when combined together. While this significantly
narrows down the tested domain of functionality, it equally much ensures
that the test case is covered in full control, thus should anything
go wrong and test fails, it is much easier to find out what went wrong.
E.g. a test that randomly creates threads would be almost useless since
any failure of the test would be irreproducable and very hard to use
to recover any bug.
c) Test cases should slowly and surely grow into a larger domain of
functionality by rigorously and systematically following steps (a) and
(b). If these steps are not rigorously followed, the tests would be
diluted in quality, and the testing structure would quickly become a
mess.
Some examples:
~~~~~~~~~~~~~~
1) Test 1: Extended ipc on already mapped pages.
2) Test 2: Extended ipc on page faulting pages.
3) Test 3: Extended ipc on page faulting pages, but non contiguous
physical pages.
4) Test 4: Extended ipc on page faulting pages, but non contiguous
physical pages, and between threads in different address spaces.
5) Test 5: Extended ipc on page faulting pages, but non contiguous
physical pages, and between threads in different address spaces,
but using the same virtual addresses on both threads.
You see, a single ipc test is systematically growing into a more complex
test, but at each step, only one possibly variable aspect is added and
tested. The other test aspects have already been validated by earlier,
simpler combinations of test cases.