Kernel updates since December 2009

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,65 @@
/*
* ARMv5 specific functions
*
* Copyright (C) 2008 - 2010 B Labs Ltd.
*/
#include <task.h>
#include <vm_area.h>
#include <l4lib/exregs.h>
#include __INC_ARCH(mm.h)
/* Extracts generic protection flags from architecture-specific pte */
unsigned int vm_prot_flags(pte_t pte)
{
unsigned int vm_prot_flags = 0;
unsigned int rw_flags = __MAP_USR_RW & PTE_PROT_MASK;
unsigned int ro_flags = __MAP_USR_RO & PTE_PROT_MASK;
/* Clear non-protection flags */
pte &= PTE_PROT_MASK;
if (pte == ro_flags)
vm_prot_flags = VM_READ | VM_EXEC;
else if (pte == rw_flags)
vm_prot_flags = VM_READ | VM_WRITE | VM_EXEC;
else
vm_prot_flags = VM_NONE;
return vm_prot_flags;
}
/*
* PTE STATES:
* PTE type field: 00 (Translation fault)
* PTE type field correct, AP bits: None (Read or Write access fault)
* PTE type field correct, AP bits: RO (Write access fault)
*/
/*
* Extracts arch-specific fault parameters
* and puts them into generic format
*/
void set_generic_fault_params(struct fault_data *fault)
{
unsigned int prot_flags = vm_prot_flags(fault->kdata->pte);
fault->reason = 0;
fault->pte_flags = prot_flags;
if (is_prefetch_abort(fault->kdata->fsr)) {
fault->reason |= VM_READ;
fault->address = fault->kdata->faulty_pc;
} else {
fault->address = fault->kdata->far;
/* Always assume read fault first */
if (prot_flags & VM_NONE)
fault->reason |= VM_READ;
else if (prot_flags & VM_READ)
fault->reason |= VM_WRITE;
else
BUG();
}
arch_print_fault_params(fault);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File