mirror of
https://github.com/drasko/codezero.git
synced 2026-03-20 03:01:48 +01:00
Kernel updates since December 2009
This commit is contained in:
71
conts/test_suite0/SConstruct
Normal file
71
conts/test_suite0/SConstruct
Normal 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
34
conts/test_suite0/TODO
Normal 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
|
||||
112
conts/test_suite0/build.readme
Normal file
112
conts/test_suite0/build.readme
Normal 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.
|
||||
|
||||
|
||||
28
conts/test_suite0/container.c
Normal file
28
conts/test_suite0/container.c
Normal 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
83
conts/test_suite0/exit.ct
Normal 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;
|
||||
}
|
||||
|
||||
16
conts/test_suite0/include/api/api.h
Normal file
16
conts/test_suite0/include/api/api.h
Normal 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__ */
|
||||
6
conts/test_suite0/include/capability.h
Normal file
6
conts/test_suite0/include/capability.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef __CAPABILITY_H__
|
||||
#define __CAPABILITY_H__
|
||||
|
||||
int caps_read_all();
|
||||
|
||||
#endif /* __CAPABILITY_H__ */
|
||||
13
conts/test_suite0/include/container.h
Normal file
13
conts/test_suite0/include/container.h
Normal 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__ */
|
||||
44
conts/test_suite0/include/debug.h
Normal file
44
conts/test_suite0/include/debug.h
Normal 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__ */
|
||||
44
conts/test_suite0/include/fault.h
Normal file
44
conts/test_suite0/include/fault.h
Normal 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__ */
|
||||
16
conts/test_suite0/include/linker.h
Normal file
16
conts/test_suite0/include/linker.h
Normal 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__ */
|
||||
30
conts/test_suite0/include/linker.lds
Normal file
30
conts/test_suite0/include/linker.lds
Normal 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 = .;
|
||||
}
|
||||
9
conts/test_suite0/include/macros.h
Normal file
9
conts/test_suite0/include/macros.h
Normal 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__ */
|
||||
12
conts/test_suite0/include/memory.h
Normal file
12
conts/test_suite0/include/memory.h
Normal 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__ */
|
||||
69
conts/test_suite0/include/perf.h
Normal file
69
conts/test_suite0/include/perf.h
Normal 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__ */
|
||||
38
conts/test_suite0/include/project_linker.lds
Normal file
38
conts/test_suite0/include/project_linker.lds
Normal 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 = .;
|
||||
}
|
||||
18
conts/test_suite0/include/tests.h
Normal file
18
conts/test_suite0/include/tests.h
Normal 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__ */
|
||||
19
conts/test_suite0/include/thread.h
Normal file
19
conts/test_suite0/include/thread.h
Normal 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__ */
|
||||
9
conts/test_suite0/include/timer.h
Normal file
9
conts/test_suite0/include/timer.h
Normal 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
47
conts/test_suite0/main.c
Normal 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;
|
||||
}
|
||||
|
||||
51
conts/test_suite0/src/api/api.c
Normal file
51
conts/test_suite0/src/api/api.c
Normal 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;
|
||||
}
|
||||
|
||||
80
conts/test_suite0/src/api/cache.c
Normal file
80
conts/test_suite0/src/api/cache.c
Normal 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;
|
||||
|
||||
}
|
||||
|
||||
56
conts/test_suite0/src/api/cap.c
Normal file
56
conts/test_suite0/src/api/cap.c
Normal 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;
|
||||
}
|
||||
|
||||
96
conts/test_suite0/src/api/exregs.c
Normal file
96
conts/test_suite0/src/api/exregs.c
Normal 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;
|
||||
|
||||
}
|
||||
|
||||
123
conts/test_suite0/src/api/getid.c
Normal file
123
conts/test_suite0/src/api/getid.c
Normal 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;
|
||||
|
||||
}
|
||||
|
||||
547
conts/test_suite0/src/api/ipc.c
Normal file
547
conts/test_suite0/src/api/ipc.c
Normal 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;
|
||||
|
||||
}
|
||||
|
||||
12
conts/test_suite0/src/api/irq.c
Normal file
12
conts/test_suite0/src/api/irq.c
Normal 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;
|
||||
}
|
||||
|
||||
366
conts/test_suite0/src/api/map.c
Normal file
366
conts/test_suite0/src/api/map.c
Normal 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;
|
||||
|
||||
}
|
||||
|
||||
198
conts/test_suite0/src/api/memory.c
Normal file
198
conts/test_suite0/src/api/memory.c
Normal 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);
|
||||
}
|
||||
|
||||
204
conts/test_suite0/src/api/mutex.c
Normal file
204
conts/test_suite0/src/api/mutex.c
Normal 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;
|
||||
}
|
||||
|
||||
146
conts/test_suite0/src/api/smp.c
Normal file
146
conts/test_suite0/src/api/smp.c
Normal 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 */
|
||||
|
||||
249
conts/test_suite0/src/api/thread.c
Normal file
249
conts/test_suite0/src/api/thread.c
Normal 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;
|
||||
}
|
||||
|
||||
65
conts/test_suite0/src/arch/arm/v5/mm.c
Normal file
65
conts/test_suite0/src/arch/arm/v5/mm.c
Normal 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);
|
||||
}
|
||||
|
||||
75
conts/test_suite0/src/arch/arm/v7/mm.c
Normal file
75
conts/test_suite0/src/arch/arm/v7/mm.c
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
106
conts/test_suite0/src/capability.c
Normal file
106
conts/test_suite0/src/capability.c
Normal 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
|
||||
148
conts/test_suite0/src/captest.c
Normal file
148
conts/test_suite0/src/captest.c
Normal 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;
|
||||
}
|
||||
53
conts/test_suite0/src/cli_serv/cli_serv.c
Normal file
53
conts/test_suite0/src/cli_serv/cli_serv.c
Normal 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;
|
||||
}
|
||||
|
||||
220
conts/test_suite0/src/example.c
Normal file
220
conts/test_suite0/src/example.c
Normal 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
|
||||
|
||||
47
conts/test_suite0/src/mthread/mthread.c
Normal file
47
conts/test_suite0/src/mthread/mthread.c
Normal 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;
|
||||
}
|
||||
|
||||
58
conts/test_suite0/src/perf/cycles.c
Normal file
58
conts/test_suite0/src/perf/cycles.c
Normal 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);
|
||||
}
|
||||
|
||||
134
conts/test_suite0/src/perf/exregs.c
Normal file
134
conts/test_suite0/src/perf/exregs.c
Normal 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;
|
||||
|
||||
}
|
||||
104
conts/test_suite0/src/perf/getid.c
Normal file
104
conts/test_suite0/src/perf/getid.c
Normal 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);
|
||||
}
|
||||
12
conts/test_suite0/src/perf/ipc.c
Normal file
12
conts/test_suite0/src/perf/ipc.c
Normal file
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright (C) 2010 B Labs Ltd.
|
||||
*
|
||||
* l4_ipc performance tests
|
||||
*
|
||||
* Author: Bahadir Balban
|
||||
*/
|
||||
|
||||
void perf_measure_ipc(void)
|
||||
{
|
||||
|
||||
}
|
||||
17
conts/test_suite0/src/perf/map.c
Normal file
17
conts/test_suite0/src/perf/map.c
Normal 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)
|
||||
{
|
||||
|
||||
}
|
||||
11
conts/test_suite0/src/perf/mutex.c
Normal file
11
conts/test_suite0/src/perf/mutex.c
Normal file
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright (C) 2010 B Labs Ltd.
|
||||
*
|
||||
* l4_mutex performance tests
|
||||
*
|
||||
* Author: Bahadir Balban
|
||||
*/
|
||||
|
||||
void perf_measure_mutex(void)
|
||||
{
|
||||
}
|
||||
32
conts/test_suite0/src/perf/perf.c
Normal file
32
conts/test_suite0/src/perf/perf.c
Normal 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;
|
||||
}
|
||||
|
||||
56
conts/test_suite0/src/perf/simple.c
Normal file
56
conts/test_suite0/src/perf/simple.c
Normal 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);
|
||||
}
|
||||
|
||||
85
conts/test_suite0/src/perf/tctrl.c
Normal file
85
conts/test_suite0/src/perf/tctrl.c
Normal 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);
|
||||
}
|
||||
|
||||
40
conts/test_suite0/src/perf/timer.c
Normal file
40
conts/test_suite0/src/perf/timer.c
Normal 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();
|
||||
}
|
||||
}
|
||||
|
||||
0
conts/test_suite0/src/perf/tswitch.c
Normal file
0
conts/test_suite0/src/perf/tswitch.c
Normal file
114
conts/test_suite0/tests_howto.txt
Normal file
114
conts/test_suite0/tests_howto.txt
Normal 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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user