Initial commit

This commit is contained in:
Bahadir Balban
2008-01-13 13:53:52 +00:00
commit e2b791a3d8
789 changed files with 95825 additions and 0 deletions

10
src/glue/arm/SConscript Normal file
View File

@@ -0,0 +1,10 @@
# Inherit global environment
Import('env')
# The set of source files associated with this SConscript file.
src_local = ['init.c', 'memory.c', 'systable.c']
obj = env.Object(src_local)
Return('obj')

396
src/glue/arm/init.c Normal file
View File

@@ -0,0 +1,396 @@
/*
* Main initialisation code for the ARM kernel
*
* Copyright (C) 2007 Bahadir Balban
*/
#include <l4/lib/mutex.h>
#include <l4/lib/printk.h>
#include <l4/lib/string.h>
#include <l4/lib/idpool.h>
#include <l4/generic/kmalloc.h>
#include <l4/generic/platform.h>
#include <l4/generic/physmem.h>
#include <l4/generic/scheduler.h>
#include <l4/generic/space.h>
#include <l4/generic/tcb.h>
#include INC_ARCH(linker.h)
#include INC_ARCH(asm.h)
#include INC_ARCH(bootdesc.h)
#include INC_SUBARCH(mm.h)
#include INC_SUBARCH(mmu_ops.h)
#include INC_GLUE(memlayout.h)
#include INC_GLUE(memory.h)
#include INC_GLUE(utcb.h)
#include INC_GLUE(syscall.h)
#include INC_PLAT(platform.h)
#include INC_PLAT(printascii.h)
#include INC_API(syscall.h)
#include INC_API(kip.h)
unsigned int kernel_mapping_end;
void init_locks(void)
{
}
/* Maps the early memory regions needed to bootstrap the system */
void init_kernel_mappings(void)
{
init_clear_ptab();
/* Map kernel area to its virtual region */
add_section_mapping_init(virt_to_phys(_start_text),
(unsigned int)_start_text, 1,
cacheable | bufferable);
/* Map kernel one-to-one to its physical region */
add_section_mapping_init(virt_to_phys(_start_text),
virt_to_phys(_start_text),
1, 0);
/* Map page table to its virtual region */
add_section_mapping_init(virt_to_phys(_start_kspace),
(unsigned int)_start_kspace,
1, 0);
/* Clean current before first time access. */
memset(current, 0, sizeof(struct ktcb));
/*
* Setup a dummy current ktcb over the bootstack, so that generic
* mapping functions can use this as the pgd source.
*/
current->pgd = &kspace;
}
void print_sections(void)
{
dprintk("_start_kernel: ",(unsigned int)_start_kernel);
dprintk("_start_text: ",(unsigned int)_start_text);
dprintk("_end_text: ", (unsigned int)_end_text);
dprintk("_start_data: ", (unsigned int)_start_data);
dprintk("_end_data: ", (unsigned int)_end_data);
dprintk("_start_vectors: ",(unsigned int)_start_vectors);
dprintk("arm_high_vector: ",(unsigned int)arm_high_vector);
dprintk("_end_vectors: ",(unsigned int)_end_vectors);
dprintk("_start_kip: ", (unsigned int) _start_kip);
dprintk("_end_kip: ", (unsigned int) _end_kip);
dprintk("_bootstack: ", (unsigned int)_bootstack);
dprintk("_end_kernel: ", (unsigned int)_end_kernel);
dprintk("_start_kspace: ", (unsigned int)_start_kspace);
dprintk("_start_pmd: ", (unsigned int)_start_pmd);
dprintk("_end_pmd: ", (unsigned int)_end_pmd);
dprintk("_end_kspace: ", (unsigned int)_end_kspace);
dprintk("_end: ", (unsigned int)_end);
}
/* Enable virtual memory using kernel's first level table
* and continue execution on virtual addresses.*/
void start_vm()
{
/*
* TTB must be 16K aligned. This is because first level tables are
* sized 16K.
*/
if ((unsigned int)&kspace & 0x3FFF)
dprintk("kspace not properly aligned for ttb:",
(u32)&kspace);
memset((void *)&kspace, 0, sizeof(pgd_table_t));
arm_set_ttb(virt_to_phys(&kspace));
/*
* This sets all 16 domains to zero and domain 0 to 1. The outcome
* is that page table access permissions are in effect for domain 0.
* All other domains have no access whatsoever.
*/
arm_set_domain(1);
/* Enable everything before mmu permissions are in place */
arm_enable_caches();
arm_enable_wbuffer();
/*
* Leave the past behind. Tlbs are invalidated, write buffer is drained.
* The whole of I + D caches are invalidated unconditionally. This is
* important to ensure that the cache is free of previously loaded
* values. Otherwise unpredictable data aborts may occur at arbitrary
* times, each time a load/store operation hits one of the invalid
* entries and those entries are cleaned to main memory.
*/
arm_invalidate_cache();
arm_drain_writebuffer();
arm_invalidate_tlb();
arm_enable_mmu();
/* Jump to virtual memory addresses */
__asm__ __volatile__ (
"add sp, sp, %0 \n" /* Update stack pointer */
#ifndef __OPTIMIZED_FP__ /* If fp not optimised away */
"add fp, fp, %0 \n" /* Update frame pointer */
#endif
/* On the next instruction below, r0 gets
* current PC + KOFFSET + 2 instructions after itself. */
"add r0, pc, %0 \n"
/* Special symbol that is extracted and included in the loader.
* Debuggers can break on it to load the virtual symbol table */
".global bkpt_phys_to_virt;\n"
"bkpt_phys_to_virt:\n"
"mov pc, r0 \n" /* (r0 has next instruction) */
:
: "r" (KERNEL_OFFSET)
: "r0"
);
/* At this point, execution is on virtual addresses. */
remove_section_mapping(virt_to_phys(_start_kernel));
/*
* Restore link register (LR) for this function.
*
* NOTE: LR values are pushed onto the stack at each function call,
* which means the restored return values will be physical for all
* functions in the call stack except this function. So the caller
* of this function must never return but initiate scheduling etc.
*/
__asm__ __volatile__ (
"add %0, %0, %1 \n"
"mov pc, %0 \n"
:: "r" (__builtin_return_address(0)), "r" (KERNEL_OFFSET)
);
while(1);
}
/* This calculates what address the kip field would have in userspace. */
#define KIP_USR_OFFSETOF(kip, field) ((void *)(((unsigned long)&kip.field - \
(unsigned long)&kip) + USER_KIP_PAGE))
/* The kip is non-standard, using 0xBB to indicate mine for now ;-) */
void kip_init()
{
struct utcb **utcb_ref;
memset(&kip, 0, PAGE_SIZE);
memcpy(&kip, "L4\230K", 4); /* Name field = l4uK */
kip.api_version = 0xBB;
kip.api_subversion = 1;
kip.api_flags = 0; /* LE, 32-bit architecture */
kip.kdesc.subid = 0x1;
kip.kdesc.id = 0xBB;
kip.kdesc.gendate = (__YEAR__ << 9)|(__MONTH__ << 5)|(__DAY__);
kip.kdesc.subsubver = 0x00000001; /* Consider as .00000001 */
kip.kdesc.ver = 0;
memcpy(&kip.kdesc.supplier, "BBB", 3);
kip_init_syscalls();
/* KIP + 0xFF0 is pointer to UTCB area for this thread group. */
utcb_ref = (struct utcb **)((unsigned long)&kip + UTCB_KIP_OFFSET);
/* All thread groups have their utcb mapped at UTCB_AREA_START */
*utcb_ref = (struct utcb *)UTCB_AREA_START;
add_mapping(virt_to_phys(&kip), USER_KIP_PAGE, PAGE_SIZE,
MAP_USR_RO_FLAGS);
}
void vectors_init()
{
unsigned int size = ((u32)_end_vectors - (u32)arm_high_vector);
/* Map the vectors in high vector page */
add_mapping(virt_to_phys(arm_high_vector),
ARM_HIGH_VECTOR, size, 0);
arm_enable_high_vectors();
/* Kernel memory trapping is enabled at this point. */
}
void abort()
{
printk("Aborting on purpose to halt system.\n");
#if 0
/* Prefetch abort */
__asm__ __volatile__ (
"mov pc, #0x0\n"
::
);
#endif
/* Data abort */
__asm__ __volatile__ (
"mov r0, #0 \n"
"ldr r0, [r0] \n"
::
);
}
void jump(struct ktcb *task)
{
__asm__ __volatile__ (
"mov lr, %0\n" /* Load pointer to context area */
"ldr r0, [lr]\n" /* Load spsr value to r0 */
"msr spsr, r0\n" /* Set SPSR as ARM_MODE_USR */
"ldmib lr, {r0-r14}^\n" /* Load all USR registers */
"nop \n" /* Spec says dont touch banked registers
* right after LDM {no-pc}^ for one instruction */
"add lr, lr, #64\n" /* Manually move to PC location. */
"ldr lr, [lr]\n" /* Load the PC_USR to LR */
"movs pc, lr\n" /* Jump to userspace, also switching SPSR/CPSR */
:
: "r" (task)
);
}
void switch_to_user(struct ktcb *task)
{
arm_clean_invalidate_cache();
arm_invalidate_tlb();
arm_set_ttb(virt_to_phys(task->pgd));
arm_invalidate_tlb();
jump(task);
}
void init_inittask(char *name, struct task_ids *ids)
{
struct svc_image *taskimg;
struct ktcb *task;
int task_pages;
/*
* NOTE: Inittask uses the kernel bootstack as its PAGE_SIZE'd kernel
* stack. There is no problem with this as the inittask always exists.
* This also solves the problem of freeing the bootstack and making use
* of the initial kspace pgd.
*/
if (!strcmp(name, "mm0"))
task = current; /* mm0 is the mockup current during init */
else
task = (struct ktcb *)zalloc_page();
/*
* Search the compile-time generated boot descriptor for information on
* available task images.
*/
for (int i = 0; i < bootdesc->total_images; i++) {
if (!strcmp(name, bootdesc->images[i].name)) {
BUG_ON(!(taskimg = &bootdesc->images[i]));
break;
}
}
printk("\nInitialising %s.\n", name);
if (taskimg->phys_start & PAGE_MASK)
printk("Warning, image start address not page aligned.\n");
/* Calculate the number of pages the task sections occupy. */
task_pages = __pfn((page_align_up(taskimg->phys_end) -
page_align(taskimg->phys_start)));
task->context.pc = INITTASK_AREA_START;
/* Stack starts one page above the end of image. */
task->context.sp = INITTASK_AREA_END - 8;
task->context.spsr = ARM_MODE_USR;
set_task_ids(task, ids);
if (!task->pgd) {
BUG(); /* Inittask won't come here */
task->pgd = alloc_pgd();
/* Tasks with no pgd copy from the inittask's pgd. */
memcpy(task->pgd, current->pgd, sizeof(pgd_table_t));
}
/*
* This task's userspace mapping. This should allocate a new pmd, if not
* existing, and a new page entry on its private pgd.
*/
add_mapping_pgd(taskimg->phys_start, INITTASK_AREA_START,
task_pages * PAGE_SIZE, MAP_USR_DEFAULT_FLAGS,
task->pgd);
printk("Mapping %d pages from 0x%x to 0x%x for %s\n", task_pages,
taskimg->phys_start, INITTASK_AREA_START, name);
/* Add the physical pages used by the task to the page map */
set_page_map(taskimg->phys_start, task_pages, 1);
/* Task's rendezvous point */
waitqueue_head_init(&task->wqh_send);
waitqueue_head_init(&task->wqh_recv);
/* Tasks' rendezvous blocked list */
spin_lock_init(&task->ipc_block_lock);
INIT_LIST_HEAD(&task->ipc_block_list);
/* Global hashlist that keeps all existing tasks */
add_task_global(task);
/* Scheduler initialises the very first task itself */
}
void init_tasks()
{
struct task_ids ids;
/* Initialise thread and space id pools */
thread_id_pool = id_pool_new_init(THREAD_IDS_MAX);
space_id_pool = id_pool_new_init(SPACE_IDS_MAX);
ids.tid = id_new(thread_id_pool);
ids.spid = id_new(space_id_pool);
/* Initialise the global task list head */
INIT_LIST_HEAD(&global_task_list);
/*
* This must come last so that other tasks can copy its pgd before it
* modifies it for its own specifics.
*/
init_inittask("mm0", &ids);
}
void start_kernel(void)
{
printascii("\nstart_kernel...\n");
/* Print section boundaries for kernel image */
//print_sections();
/* Initialise section mappings for the kernel area */
init_kernel_mappings();
/* Enable virtual memory and jump to virtual addresses */
start_vm();
/* PMD tables initialised */
init_pmd_tables();
/* Initialise platform-specific page mappings, and peripherals */
platform_init();
/* Map and enable high vector page. Faults can be handled after here. */
vectors_init();
/* Remap 1MB kernel sections as 4Kb pages. */
remap_as_pages(_start_kernel, _end_kernel);
/* Move the initial pgd into a more convenient place, mapped as pages. */
relocate_page_tables();
/* Initialise memory allocators */
paging_init();
/* Initialise kip and map for userspace access */
kip_init();
/* Initialise system call page */
syscall_init();
/* Initialise everything else, e.g. locks, lists... */
init_locks();
/* Setup inittask's ktcb and push it to scheduler runqueue */
init_tasks();
/* Start the scheduler with available tasks in the runqueue */
scheduler_start();
BUG();
}

4
src/glue/arm/irq.c Normal file
View File

@@ -0,0 +1,4 @@
/*
* ARM Generic irq handler
*/

135
src/glue/arm/memory.c Normal file
View File

@@ -0,0 +1,135 @@
/*
* ARM virtual memory implementation
*
* Copyright (C) 2007 Bahadir Balban
*/
#include <l4/lib/list.h>
#include <l4/lib/string.h>
#include <l4/lib/printk.h>
#include <l4/generic/physmem.h>
#include <l4/generic/space.h>
#include <l4/generic/tcb.h>
#include INC_SUBARCH(mm.h)
#include INC_GLUE(memlayout.h)
#include INC_GLUE(memory.h)
#include INC_PLAT(printascii.h)
#include INC_PLAT(offsets.h)
#include INC_ARCH(linker.h)
/*
* Conversion from generic protection flags to arch-specific
* pte flags.
*/
unsigned int space_flags_to_ptflags(unsigned int flags)
{
switch (flags) {
case MAP_USR_RW_FLAGS:
return __MAP_USR_RW_FLAGS;
case MAP_USR_RO_FLAGS:
return __MAP_USR_RO_FLAGS;
case MAP_SVC_RW_FLAGS:
return __MAP_SVC_RW_FLAGS;
case MAP_USR_IO_FLAGS:
return __MAP_USR_IO_FLAGS;
case MAP_SVC_IO_FLAGS:
return __MAP_SVC_IO_FLAGS;
default:
BUG();
}
BUG(); return 0;
}
#define NUM_PMD_TABLES 6
#define NUM_PGD_TABLES 8
/* Initial first level page table to provide startup mappings */
SECTION(".kspace.pgd") pgd_table_t kspace;
SECTION(".kspace.pmd") pmd_table_t pmd_tables[NUM_PMD_TABLES];
/* A mini bitmap for boot pmd allocations */
static int pmd_cnt;
pmd_table_t *pmd_array;
pmd_table_t *alloc_boot_pmd(void)
{
pmd_table_t *pt;
if (pmd_cnt == NUM_PMD_TABLES)
return 0;
pt = &pmd_array[pmd_cnt++];
BUG_ON((unsigned long)pt & (sizeof(pmd_table_t) - 1));
return pt;
}
/*
* Initialises pmd allocation cache, this is called before page allocator
* initialises. After this call one can add page mappings via add_mapping().
* This also sets the alloc_pmd() global function to this boot-time version.
*/
void init_pmd_tables(void)
{
pmd_cnt = 0;
pmd_array = pmd_tables;
memset(pmd_array, 0, NUM_PMD_TABLES * sizeof(pmd_table_t));
}
/* Clears out all entries in the initial page table */
void init_clear_ptab(void)
{
memset((void *)virt_to_phys(&kspace), 0, sizeof(pgd_table_t));
}
/* Sets up struct page array and the physical memory descriptor. */
void paging_init(void)
{
read_bootdesc();
physmem_init();
memory_init();
copy_bootdesc();
}
/*
* Copies global kernel entries into another pgd. Even for sub-pmd ranges
* the associated pmd entries are copied, assuming any pmds copied are
* applicable to all tasks in the system.
*/
void copy_pgd_kern_by_vrange(pgd_table_t *to, pgd_table_t *from,
unsigned long start, unsigned long end)
{
/* Extend sub-pmd ranges to their respective pmd boundaries */
start = align(start, PMD_MAP_SIZE);
if (end < start)
end = 0;
/* Aligning would overflow if mapping the last virtual pmd */
if (end < align(~0, PMD_MAP_SIZE) ||
start > end) /* end may have already overflown as input */
end = align_up(end, PMD_MAP_SIZE);
else
end = 0;
copy_pgds_by_vrange(to, from, start, end);
}
/* Copies all standard bits that a user process should have in its pgd */
void copy_pgd_kern_all(pgd_table_t *to)
{
pgd_table_t *from = current->pgd;
copy_pgd_kern_by_vrange(to, from, KERNEL_AREA_START, KERNEL_AREA_END);
copy_pgd_kern_by_vrange(to, from, IO_AREA_START, IO_AREA_END);
copy_pgd_kern_by_vrange(to, from, USER_KIP_PAGE,
USER_KIP_PAGE + PAGE_SIZE);
copy_pgd_kern_by_vrange(to, from, ARM_HIGH_VECTOR,
ARM_HIGH_VECTOR + PAGE_SIZE);
copy_pgd_kern_by_vrange(to, from, ARM_SYSCALL_VECTOR,
ARM_SYSCALL_VECTOR + PAGE_SIZE);
/* We temporarily map uart registers to every process */
copy_pgd_kern_by_vrange(to, from, USERSPACE_UART_BASE,
USERSPACE_UART_BASE + PAGE_SIZE);
}

82
src/glue/arm/systable.c Normal file
View File

@@ -0,0 +1,82 @@
/*
* System Calls
*
* Copyright (C) 2007 Bahadir Balban
*/
#include <l4/lib/mutex.h>
#include <l4/lib/printk.h>
#include <l4/generic/space.h>
#include <l4/api/errno.h>
#include INC_GLUE(memlayout.h)
#include INC_GLUE(syscall.h)
#include INC_SUBARCH(mm.h)
#include INC_API(syscall.h)
#include INC_API(kip.h)
void kip_init_syscalls(void)
{
kip.space_control = ARM_SYSCALL_PAGE + sys_space_control_offset;
kip.thread_control = ARM_SYSCALL_PAGE + sys_thread_control_offset;
kip.ipc_control = ARM_SYSCALL_PAGE + sys_ipc_control_offset;
kip.map = ARM_SYSCALL_PAGE + sys_map_offset;
kip.ipc = ARM_SYSCALL_PAGE + sys_ipc_offset;
kip.kread = ARM_SYSCALL_PAGE + sys_kread_offset;
kip.unmap = ARM_SYSCALL_PAGE + sys_unmap_offset;
kip.exchange_registers = ARM_SYSCALL_PAGE + sys_exchange_registers_offset;
kip.thread_switch = ARM_SYSCALL_PAGE + sys_thread_switch_offset;
kip.schedule = ARM_SYSCALL_PAGE + sys_schedule_offset;
kip.getid = ARM_SYSCALL_PAGE + sys_getid_offset;
kip.kmem_grant = ARM_SYSCALL_PAGE + sys_kmem_grant_offset;
kip.kmem_reclaim = ARM_SYSCALL_PAGE + sys_kmem_reclaim_offset;
}
/* Jump table for all system calls. */
syscall_fn_t syscall_table[SYSCALLS_TOTAL];
/*
* Initialises the system call jump table, for kernel to use.
* Also maps the system call page into userspace.
*/
void syscall_init()
{
syscall_table[sys_ipc_offset >> 2] = (syscall_fn_t)sys_ipc;
syscall_table[sys_thread_switch_offset >> 2] = (syscall_fn_t)sys_thread_switch;
syscall_table[sys_thread_control_offset >> 2] = (syscall_fn_t)sys_thread_control;
syscall_table[sys_exchange_registers_offset >> 2] = (syscall_fn_t)sys_exchange_registers;
syscall_table[sys_schedule_offset >> 2] = (syscall_fn_t)sys_schedule;
syscall_table[sys_getid_offset >> 2] = (syscall_fn_t)sys_getid;
syscall_table[sys_unmap_offset >> 2] = (syscall_fn_t)sys_unmap;
syscall_table[sys_space_control_offset >> 2] = (syscall_fn_t)sys_space_control;
syscall_table[sys_ipc_control_offset >> 2] = (syscall_fn_t)sys_ipc_control;
syscall_table[sys_map_offset >> 2] = (syscall_fn_t)sys_map;
syscall_table[sys_kread_offset >> 2] = (syscall_fn_t)sys_kread;
syscall_table[sys_kmem_grant_offset >> 2] = (syscall_fn_t)sys_kmem_grant;
syscall_table[sys_kmem_reclaim_offset >> 2] = (syscall_fn_t)sys_kmem_reclaim;
add_mapping(virt_to_phys(&__syscall_page_start),
ARM_SYSCALL_PAGE, PAGE_SIZE, MAP_USR_RO_FLAGS);
}
/* Checks a syscall is legitimate and dispatches to appropriate handler. */
int syscall(struct syscall_args *regs, unsigned long swi_addr)
{
/* Check if genuine system call, coming from the syscall page */
if ((swi_addr & ARM_SYSCALL_PAGE) == ARM_SYSCALL_PAGE) {
/* Check within syscall offset boundary */
if (((swi_addr & syscall_offset_mask) >= 0) &&
((swi_addr & syscall_offset_mask) <= syscalls_end_offset)) {
/* Quick jump, rather than compare each */
return (*syscall_table[(swi_addr & 0xFF) >> 2])(regs);
} else {
printk("System call received from call @ 0x%lx."
"Instruction: 0x%lx.\n", swi_addr,
*((unsigned long *)swi_addr));
return -ENOSYS;
}
} else {
printk("System call exception from unknown location 0x%lx."
"Discarding.\n", swi_addr);
return -ENOSYS;
}
}

View File

@@ -0,0 +1,9 @@
# Inherit global environment
Import('env')
# The set of source files associated with this SConscript file.
src_local = ['main.c', 'test_kmalloc.c', 'test_memcache.c', 'test_allocpage.c', 'test_alloc_generic.c', 'debug.c', 'memory.c', 'clz.c']
obj = env.Object(src_local)
Return('obj')

15
src/glue/tests/clz.c Normal file
View File

@@ -0,0 +1,15 @@
#include <macros.h>
#include <types.h>
#include <config.h>
/* Emulation of CLZ (count leading zeroes) instruction */
unsigned int __clz(unsigned int bitvector)
{
unsigned int x = 0;
while((!(bitvector & ((unsigned)1 << 31))) && (x < 32)) {
bitvector <<= 1;
x++;
}
return x;
}

7
src/glue/tests/clz.h Normal file
View File

@@ -0,0 +1,7 @@
#ifndef __CLZ_H__
#define __CLZ_H__
unsigned int __clz(unsigned int bitvector);
#endif /* __CLZ_H__ */

59
src/glue/tests/debug.c Normal file
View File

@@ -0,0 +1,59 @@
#include <generic/physmem.h>
#include "debug.h"
#include <stdio.h>
void print_page_area_list(struct page_area *p)
{
struct page_area *current_item = p;
struct list_head *begin = &p->list;
if (!current_item) {
printf("%-20s\n", "Null list.");
return;
}
printf("%-20s", "Page area:");
printf("%s", (list_empty(&current_item->list) ? "(Single Item.)\n" : "\n"));
printf("%-20s\n","-------------------------");
printf("%-20s %d\n", "Index:", current_item->index);
printf("%-20s %d\n", "Used:", current_item->used);
printf("%-20s %d\n\n", "Number of pages:", current_item->numpages);
list_for_each_entry (current_item, begin, list) {
printf("%-20s\n%-20s\n", "Page area:","-------------------------");
printf("%-20s %d\n", "Index:", current_item->index);
printf("%-20s %d\n", "Used:", current_item->used);
printf("%-20s %d\n\n", "Number of pages:", current_item->numpages);
}
}
void print_subpage_area(struct subpage_area *s)
{
printf("%-20s\n%-20s\n", "Subpage area:","-------------------------");
printf("%-20s 0x%x\n", "Addr:", s->vaddr);
printf("%-20s 0x%x\n", "Size:", s->size);
printf("%-20s %d\n", "Used:", s->used);
printf("%-20s %d\n\n", "Head_of_pages:", s->head_of_pages);
}
void print_subpage_area_list(struct subpage_area *s)
{
struct subpage_area *current_item = s;
struct list_head *begin = &s->list;
if (!current_item) {
printf("Null list.\n");
return;
}
printf("%-20s", "Subpage area:");
printf("%s", (list_empty(&current_item->list) ? "(Single Item.)\n" : "\n"));
printf("%-20s\n","-------------------------");
printf("%-20s 0x%x\n", "Addr:", current_item->vaddr);
printf("%-20s 0x%x\n", "Size:", current_item->size);
printf("%-20s %d\n", "Used:", current_item->used);
printf("%-20s %d\n\n", "Head_of_pages:", current_item->head_of_pages);
list_for_each_entry (current_item, begin, list) {
print_subpage_area(current_item);
}
}

13
src/glue/tests/debug.h Normal file
View File

@@ -0,0 +1,13 @@
#ifndef __DEBUG_H__
#define __DEBUG_H__
#include <generic/physmem.h>
#include <generic/kmalloc.h>
#include <generic/alloc_page.h>
#include <lib/list.h>
void print_physmem(struct memdesc *m);
void print_page_area_list(struct page_area *p);
void print_subpage_area_list(struct subpage_area *s);
void print_subpage_area(struct subpage_area *s);
#endif /* DEBUG_H */

0
src/glue/tests/linker.c Normal file
View File

215
src/glue/tests/main.c Normal file
View File

@@ -0,0 +1,215 @@
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include <macros.h>
#include <config.h>
#include <generic/physmem.h>
#include <generic/kmalloc.h>
#include <generic/alloc_page.h>
#include INC_SUBARCH(mm.h)
#include INC_ARCH(linker.h)
#include INC_PLAT(printascii.h)
#include INC_PLAT(offsets.h)
#include INC_GLUE(basiclayout.h)
#include "tests.h"
#include "test_kmalloc.h"
#include "test_allocpage.h"
#include "test_memcache.h"
#include "clz.h"
unsigned int TEST_PHYSMEM_TOTAL_PAGES = 250;
unsigned int TEST_PHYSMEM_TOTAL_SIZE;
void *malloced_test_memory;
/* Allocating memory from the host C library, and
* it is used as if it is the physical memory available
* on the system.
*/
void alloc_test_memory()
{
TEST_PHYSMEM_TOTAL_SIZE = (PAGE_SIZE * TEST_PHYSMEM_TOTAL_PAGES);
if (!(malloced_test_memory = malloc(TEST_PHYSMEM_TOTAL_SIZE)))
printf("Host system out of memory.\n");
PHYS_MEM_START = (unsigned int)malloced_test_memory;
PHYS_MEM_END = PHYS_MEM_START + TEST_PHYSMEM_TOTAL_SIZE;
PHYS_MEM_START = page_align_up(PHYS_MEM_START);
PHYS_MEM_END = page_align(PHYS_MEM_END);
/* Normally _end is to know where the loaded kernel image
* ends in physical memory, so the system can start allocating
* physical memory from there. Because in our mock-up there's no
* used space in the malloc()'ed memory, _end is the same as the
* beginning of malloc()'ed memory.
*/
_end = PHYS_MEM_START;
}
struct cmdline_opts {
char run_allocator;
int allocations;
int alloc_size_max;
int physmem_pages;
int page_size;
int no_of_pages;
char *finit_path;
char *fexit_path;
} options;
void print_options(struct cmdline_opts *opts)
{
printf("Running: %s\n",
((opts->run_allocator == 'p') ? "page allocator" :
((opts->run_allocator == 'k') ? "kmalloc/kfree" :
"memcache allocator")));
printf("Total suggested allocations: %d\n", opts->allocations);
printf("Maximum allocation size: %d, 0x%x(hex)\n\n",
opts->alloc_size_max, opts->alloc_size_max);
printf("Initial state file: %s\n", opts->finit_path);
printf("Exit state file: %s\n", opts->fexit_path);
}
void display_help(void)
{
printf("Main:\n");
printf("\tUsage:\n");
printf("\tmain\t-a=<p>|<k>|<m> [-n=<number of allocations>] [-s=<maximum size for any allocation>]\n"
"\t\t[-fi=<file to dump init state>] [-fx=<file to dump exit state>]\n"
"\t\t[-ps=<page size>] [-pn=<total number of pages>]\n");
printf("\n");
}
int get_cmdline_opts(int argc, char *argv[], struct cmdline_opts *opts)
{
int parsed = 0;
memset(opts, 0, sizeof (struct cmdline_opts));
if (argc <= 1)
return -1;
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-' && argv[i][2] == '=') {
if (argv[i][1] == 'a') {
if (argv[i][3] == 'k' ||
argv[i][3] == 'm' ||
argv[i][3] == 'p') {
opts->run_allocator = argv[i][3];
parsed = 1;
}
}
if (argv[i][1] == 'n') {
opts->allocations = atoi(&argv[i][3]);
parsed = 1;
}
if (argv[i][1] == 's') {
opts->alloc_size_max = atoi(&argv[i][3]);
parsed = 1;
}
}
if (argv[i][0] == '-' && argv[i][1] == 'f'
&& argv[i][3] == '=') {
if (argv[i][2] == 'i') {
opts->finit_path = &argv[i][4];
parsed = 1;
}
if (argv[i][2] == 'x') {
opts->fexit_path = &argv[i][4];
parsed = 1;
}
}
if (argv[i][0] == '-' && argv[i][1] == 'p'
&& argv[i][3] == '=') {
if (argv[i][2] == 's') {
opts->page_size = atoi(&argv[i][4]);
parsed = 1;
}
if (argv[i][2] == 'n') {
opts->no_of_pages = atoi(&argv[i][4]);
parsed = 1;
}
}
}
if (!parsed)
return -1;
return 0;
}
void get_output_files(FILE **out1, FILE **out2,
char *alloc_func_name, char *rootpath)
{
char pathbuf[150];
char *root = "/tmp/";
char *initstate_prefix = "test_initstate_";
char *endstate_prefix = "test_endstate_";
char *extention = ".out";
if (!rootpath)
rootpath = root;
/* File path manipulations */
sprintf(pathbuf, "%s%s%s%s", rootpath, initstate_prefix, alloc_func_name, extention);
*out1 = fopen(pathbuf,"w+");
sprintf(pathbuf, "%s%s%s%s", rootpath, endstate_prefix, alloc_func_name, extention);
*out2 = fopen(pathbuf, "w+");
return;
}
int main(int argc, char *argv[])
{
FILE *finit, *fexit;
int output_files = 0;
if (get_cmdline_opts(argc, argv, &options) < 0) {
display_help();
return 1;
}
print_options(&options);
if (options.finit_path && options.fexit_path) {
finit = fopen(options.finit_path, "w+");
fexit = fopen(options.fexit_path, "w+");
output_files = 1;
}
if (options.page_size) {
PAGE_SIZE = options.page_size;
PAGE_MASK = PAGE_SIZE - 1;
PAGE_BITS = 32 - __clz(PAGE_MASK);
printf("Using: Page Size: %d\n", PAGE_SIZE);
printf("Using: Page Mask: 0x%x\n", PAGE_MASK);
printf("Using: Page Bits: %d\n", PAGE_BITS);
}
if (options.no_of_pages) {
printf("Using: Total pages: %d\n", options.no_of_pages);
TEST_PHYSMEM_TOTAL_PAGES = options.no_of_pages;
}
alloc_test_memory();
printf("Initialising physical memory\n");
physmem_init();
printf("Initialising allocators:\n");
memory_init();
if (options.run_allocator == 'p') {
if (!output_files)
get_output_files(&finit, &fexit, "alloc_page", 0);
test_allocpage(options.allocations, options.alloc_size_max,
finit, fexit);
} else if (options.run_allocator == 'k') {
if (!output_files)
get_output_files(&finit, &fexit, "kmalloc", 0);
test_kmalloc(options.allocations, options.alloc_size_max,
finit, fexit);
} else if (options.run_allocator == 'm') {
if (!output_files)
get_output_files(&finit, &fexit, "memcache", 0);
test_memcache(options.allocations, options.alloc_size_max,
finit, fexit, 1);
} else {
printf("Invalid allocator option.\n");
}
free((void *)malloced_test_memory);
fclose(finit);
fclose(fexit);
return 0;
}

7
src/glue/tests/memory.c Normal file
View File

@@ -0,0 +1,7 @@
#include <macros.h>
#include <config.h>
#include INC_GLUE(memory.h)
unsigned int PAGE_SIZE = TEST_PAGE_SIZE;
unsigned int PAGE_MASK = TEST_PAGE_MASK;
unsigned int PAGE_BITS = TEST_PAGE_BITS;

View File

@@ -0,0 +1,205 @@
/*
* Generic random allocation/deallocation test
*
* Copyright 2005 (C) Bahadir Balban
*
*/
#include <macros.h>
#include <config.h>
#include INC_GLUE(memory.h)
#include <lib/printk.h>
#include <lib/list.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include "test_alloc_generic.h"
#include "debug.h"
void print_test_state(unsigned int title,
print_alloc_state_t print_allocator_state)
{
switch (title) {
case TEST_STATE_BEGIN:
printf( "=================\n"
"===== BEGIN =====\n"
"=================\n\n");
break;
case TEST_STATE_MIDDLE:
printf( "==================\n"
"===== MIDDLE =====\n"
"==================\n\n");
break;
case TEST_STATE_END:
printf( "===========\n"
"=== END ===\n"
"===========\n\n");
break;
case TEST_STATE_ERROR:
printf( "=================\n"
"===== ERROR =====\n"
"=================\n\n");
break;
default:
printf("Title error.\n");
}
print_allocator_state();
}
void get_output_filepaths(FILE **out1, FILE **out2,
char *alloc_func_name)
{
char pathbuf[150];
char *rootpath = "/tmp/";
char *initstate_prefix = "test_initstate_";
char *endstate_prefix = "test_endstate_";
char *extention = ".out";
/* File path manipulations */
sprintf(pathbuf, "%s%s%s%s", rootpath, initstate_prefix, alloc_func_name, extention);
*out1 = fopen(pathbuf,"w+");
sprintf(pathbuf, "%s%s%s%s", rootpath, endstate_prefix, alloc_func_name, extention);
*out2 = fopen(pathbuf, "w+");
return;
}
/* This function is at the heart of generic random allocation testing.
* It is made as simple as possible, and can be used for testing all
* allocators. It randomly allocates/deallocates data and prints out
* the outcome of the action. Here are a few things it does and doesn't
* do:
* - It does not test false input on the allocators, e.g. attempting
* to free an address that hasn't been allocated, or attempting to
* free address 0.
* - It does capture and compare initial and final states of the
* allocators' internal structures after all allocations are freed.
* This is done by comparing two files filled with allocator state
* by functions supplied by the allocators themselves.
* - It expects the allocator NOT to run out of memory.
*/
int
test_alloc_free_random_order(const int MAX_ALLOCATIONS,
const int ALLOC_SIZE_MAX,
alloc_func_t alloc,
free_func_t free,
print_alloc_state_t print_allocator_state,
FILE *state_init_file, FILE *state_end_file)
{
/* The last element in full_state that tells about any full index.
* This is the limit the random deallocation would use to find a full
* index */
int random_size;
int random_action;
int random_index;
int alloc_so_far = 0;
int full_state_last = -1;
int halfway_through = 0;
FILE * const default_stdout = stdout;
/* Memory pointers */
void *mem[MAX_ALLOCATIONS];
/* Each element keeps track of one currently full index number */
int full_state[MAX_ALLOCATIONS];
memset(mem, 0, MAX_ALLOCATIONS * sizeof(void *));
memset(full_state, 0, MAX_ALLOCATIONS * sizeof(int));
print_test_state(TEST_STATE_BEGIN, print_allocator_state);
stdout = state_init_file;
print_test_state(TEST_STATE_BEGIN, print_allocator_state);
stdout = default_stdout;
/* Randomly either allocate/deallocate at a random
* index, of random size */
srand(time(0));
/* Constraints */
while (1) {
if (alloc_so_far < (MAX_ALLOCATIONS / 2)) {
/* Give more chance to allocations at the beginning */
if ((rand() % 4) == 0) /* 1/4 chance */
random_action = FREE;
else /* 3/4 chance */
random_action = ALLOCATE;
} else {
if (!halfway_through) {
print_test_state(TEST_STATE_MIDDLE,
print_allocator_state);
halfway_through = 1;
}
/* Give more chane to freeing after halfway-through */
if ((rand() % 3) == 0) /* 1/3 chance */
random_action = ALLOCATE;
else /* 2/3 chance */
random_action = FREE;
}
random_size = (rand() % (ALLOC_SIZE_MAX-1)) + 1;
if (random_action == ALLOCATE) {
if (alloc_so_far < MAX_ALLOCATIONS) {
alloc_so_far++;
for (int i = 0; i < MAX_ALLOCATIONS; i++) {
if (mem[i] == 0) {
int allocation_error =
((mem[i] = alloc(random_size)) <= 0);
printf("%-12s%-8s%-12p%-8s%-10d\n",
"alloc:", "addr:", mem[i],
"size:", random_size);
if (allocation_error) {
print_test_state(TEST_STATE_ERROR,
print_allocator_state);
if (mem[i] < 0) {
printf("Error: alloc() returned negative value\n");
BUG();
} else if (mem[i] == 0) {
printf("Error: Allocator is out of memory.\n");
return 1;
}
}
full_state_last++;
full_state[full_state_last] = i;
break;
}
}
} else
random_action = FREE;
}
if (random_action == FREE) {
/* all are free, can't free anymore */
if (full_state_last < 0)
continue;
else if (full_state_last > 0)
random_index = rand() % full_state_last;
else
random_index = 0; /* Last item */
if(mem[full_state[random_index]] == 0)
BUG();
free(mem[full_state[random_index]]);
printf("%-12s%-8s%-12p\n","free:",
"addr:", mem[full_state[random_index]]);
mem[full_state[random_index]] = 0;
/* Fill in the empty gap with last element */
full_state[random_index] = full_state[full_state_last];
/* Last element now in the gap
* (somewhere inbetween first and last) */
full_state[full_state_last] = 0;
/* One less in the number of full items */
full_state_last--;
}
/* Check that all allocations and deallocations took place */
if (alloc_so_far == MAX_ALLOCATIONS && full_state_last < 0)
break;
}
print_test_state(TEST_STATE_END, print_allocator_state);
stdout = state_end_file;
print_test_state(TEST_STATE_BEGIN, print_allocator_state);
stdout = default_stdout;
return 0;
}

View File

@@ -0,0 +1,29 @@
#ifndef __TEST_ALLOC_GENERIC_H__
#define __TEST_ALLOC_GENERIC_H__
enum test_state_title {
TEST_STATE_BEGIN = 0,
TEST_STATE_MIDDLE,
TEST_STATE_END,
TEST_STATE_ERROR
};
typedef void (*print_alloc_state_t)(void);
typedef void *(*alloc_func_t)(int size);
typedef int (*free_func_t)(void *addr);
enum alloc_action {
FREE = 0,
ALLOCATE = 1,
};
void get_output_filepaths(FILE **out1, FILE **out2,
char *alloc_func_name);
int test_alloc_free_random_order(const int MAX_ALLOCATIONS,
const int ALLOC_SIZE_MAX,
alloc_func_t alloc, free_func_t free,
print_alloc_state_t print_allocator_state,
FILE *init_state, FILE *exit_state);
#endif /* __TEST_ALLOC_GENERIC_H__ */

View File

@@ -0,0 +1,99 @@
#include <macros.h>
#include <config.h>
#include INC_GLUE(memory.h)
#include <lib/printk.h>
#include <lib/list.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include "test_allocpage.h"
#include "test_alloc_generic.h"
#include "debug.h"
unsigned int PAGE_ALLOCATIONS = 30;
unsigned int PAGE_ALLOC_SIZE_MAX = 8;
extern struct page_area *areas;
extern struct mem_cache *primary_cache;
extern struct mem_cache *secondary_cache;
extern struct mem_cache *spare_cache;
void print_page_area(struct page_area *ar, int areano)
{
printf("Area starts @: 0x%x, %s, numpages: %d\n",
ar->index << PAGE_BITS,
(ar->used) ? "used" : "unused", ar->numpages);
return;
}
void print_areas(struct page_area *ar)
{
struct page_area *cur = ar;
int areano = 1;
printf("Page areas:\n-------------\n");
if (!ar) {
printf("None.\n");
return;
}
print_page_area(cur, areano++);
list_for_each_entry(cur, &ar->list, list) {
print_page_area(cur, areano++);
}
return;
}
void print_cache(struct mem_cache *c, int cacheno)
{
printf("Cache %d state:\n-------------\n", cacheno);
printf("Total: %d\n", c->total);
printf("Free: %d\n", c->free);
printf("Start: 0x%x\n", c->start);
return;
}
void print_caches(struct mem_cache *c)
{
int caches = 1;
struct mem_cache *cur = c;
if (!c) {
printf("None.\n");
return;
}
print_cache(cur, caches++);
list_for_each_entry(cur, &c->list, list) {
print_cache(cur, caches++);
}
return;
}
void print_page_allocator_state(void)
{
print_areas(areas);
printf("PRIMARY:\n--------\n");
print_caches(primary_cache);
printf("SECONDARY:\n----------\n");
print_caches(secondary_cache);
}
/* FIXME: with current default parameters (allocations = 30, sizemax = 8),
* for some odd reason, we got the bug at line 280 in alloc_page.c.
* Very weird. Find out why.
*/
void test_allocpage(int page_allocations, int page_alloc_size_max,
FILE *init_state, FILE *exit_state)
{
if (!page_allocations)
page_allocations = PAGE_ALLOCATIONS;
if (!page_alloc_size_max)
page_alloc_size_max = PAGE_ALLOC_SIZE_MAX;
printf("\nPAGE ALLOCATOR TEST:====================================\n\n");
test_alloc_free_random_order(page_allocations, page_alloc_size_max,
alloc_page, free_page,
print_page_allocator_state,
init_state, exit_state);
}

View File

@@ -0,0 +1,13 @@
#ifndef __TEST_ALLOCPAGE_H__
#define __TEST_ALLOCPAGE_H__
#include <generic/alloc_page.h>
#include "tests.h"
void test_allocpage(int num_allocs, int alloc_max, FILE *init, FILE *exit);
void print_page_area(struct page_area *a, int no);
void print_caches(struct mem_cache *c);
void print_cache(struct mem_cache *c, int cacheno);
void print_areas(struct page_area *ar);
void print_page_area(struct page_area *ar, int areano);
#endif

View File

@@ -0,0 +1,39 @@
#include <macros.h>
#include <config.h>
#include <lib/list.h>
#include <lib/printk.h>
#include INC_GLUE(memory.h)
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "test_alloc_generic.h"
#include "test_allocpage.h"
#include "debug.h"
#include "tests.h"
extern struct subpage_area *km_areas;
extern struct page_area *areas;
void print_kmalloc_state(void)
{
print_subpage_area_list(km_areas);
}
void test_kmalloc(int kmalloc_allocations, int kmalloc_alloc_size_max,
FILE *init_state, FILE *exit_state)
{
unsigned int KMALLOC_ALLOCATIONS = 20;
unsigned int KMALLOC_ALLOC_SIZE_MAX = (PAGE_SIZE * 3);
if (!kmalloc_allocations)
kmalloc_allocations = KMALLOC_ALLOCATIONS;
if (!kmalloc_alloc_size_max)
kmalloc_alloc_size_max = KMALLOC_ALLOC_SIZE_MAX;
test_alloc_free_random_order(kmalloc_allocations, kmalloc_alloc_size_max,
kmalloc, kfree, print_kmalloc_state,
init_state, exit_state);
}

View File

@@ -0,0 +1,8 @@
#ifndef __TEST_KMALLOC_H__
#define __TEST_KMALLOC_H__
#include <generic/kmalloc.h>
void test_kmalloc(int num_allocs, int allocs_max, FILE *initstate, FILE *exitstate);
#endif

View File

@@ -0,0 +1,112 @@
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <lib/list.h>
#include <lib/printk.h>
#include <generic/memcache.h>
#include "test_memcache.h"
#include "test_alloc_generic.h"
#include "debug.h"
#include "tests.h"
#include <macros.h>
#include <config.h>
#include INC_GLUE(memory.h)
unsigned int MEM_CACHE_SIZE;
struct mem_cache *this;
void *buffer;
void *mem_cache_alloc_wrapped(int size)
{
return mem_cache_alloc(this);
}
int mem_cache_free_wrapped(void *addr)
{
return mem_cache_free(this, addr);
}
void print_memcache_state(void)
{
printf("%-15s%d\n","Total:", this->total);
printf("%-15s%d\n","Free:", this->free);
printf("Bitmap has %d words:\n", BITWISE_GETWORD(this->total) + 1);
for (int i = 0; i <= BITWISE_GETWORD(this->total); i++)
printf("0x%x\n", this->bitmap[i]);
}
int test_memcache_init_aligned(int *items_max, int item_size)
{
if (item_size * 10 > MEM_CACHE_SIZE)
MEM_CACHE_SIZE = item_size * 10;
if (!(buffer = calloc(1, MEM_CACHE_SIZE))) {
printf("System out of memory.\n");
BUG();
}
if ((this = mem_cache_init((unsigned int)buffer,
MEM_CACHE_SIZE,
item_size, 1)) == 0) {
printf("Unable to initialise cache.\n");
return -1;
}
*items_max = mem_cache_total_free(this);
printf("\nMEMCACHE TEST: ALIGNED ELEMENTS\n==========================\n");
printf("%-30s%d\n", "Item size:", item_size);
printf("%-30s0x%x\n", "Cache occupied space:", MEM_CACHE_SIZE);
printf("%-30s%d\n","Total items in cache:", *items_max);
printf("%-30s0x%x\n","Total items space:", (*items_max * item_size));
return 0;
}
int test_memcache_init(int *items_max, int item_size)
{
if (item_size * 10 > MEM_CACHE_SIZE)
MEM_CACHE_SIZE = item_size * 10;
printf("%s: Allocating cache memory.\n",__FUNCTION__);
if (!(buffer = calloc(1, MEM_CACHE_SIZE))) {
printf("System out of memory.\n");
BUG();
}
if ((this = mem_cache_init((unsigned int)buffer,
MEM_CACHE_SIZE,
item_size, 0)) == 0) {
printf("Unable to initialise cache.\n");
return -1;
}
*items_max = mem_cache_total_free(this);
printf("\nMEMCACHE TEST:\n========================\n");
printf("%-30s%d\n", "Item size:", item_size);
printf("%-30s0x%x\n", "Cache occupied space:", MEM_CACHE_SIZE);
printf("%-30s%d\n","Total items in cache:", *items_max);
printf("%-30s0x%x\n","Total items space:", (*items_max * item_size));
return 0;
}
int test_memcache(int items_max, int item_size, FILE *init_state, FILE *exit_state, int aligned)
{
const unsigned int TEST_CACHE_ITEM_SIZE = 5;
MEM_CACHE_SIZE = PAGE_SIZE * 5;
if (!item_size)
item_size = TEST_CACHE_ITEM_SIZE;
/* items_max value is ignored and overwritten because caches have fixed size. */
test_memcache_init(&items_max, item_size);
test_alloc_free_random_order(items_max, /* unused */ 2, mem_cache_alloc_wrapped,
mem_cache_free_wrapped, print_memcache_state,
init_state, exit_state);
free(buffer);
if (aligned) {
test_memcache_init_aligned(&items_max, item_size);
test_alloc_free_random_order(items_max, /* unused */ 2, mem_cache_alloc_wrapped,
mem_cache_free_wrapped, print_memcache_state,
init_state, exit_state);
}
free(buffer);
return 0;
}

View File

@@ -0,0 +1,10 @@
#ifndef __TEST_MEMCACHE__H__
#define __TEST_MEMCACHE__H__
#include <generic/memcache.h>
int test_memcache(int num_alloc, int alloc_size_max, FILE *initstate, FILE *exitstate, int aligned);
#endif /* __TEST_MEMCACHE__H__ */

21
src/glue/tests/tests.h Normal file
View File

@@ -0,0 +1,21 @@
#ifndef __TESTS_H__
#define __TESTS_H__
/* Mock-up physical memory */
extern unsigned int TEST_PHYSMEM_TOTAL_PAGES;
extern unsigned int TEST_PHYSMEM_TOTAL_SIZE;
/* Allocator test */
extern unsigned int PAGE_ALLOCATIONS;
extern unsigned int PAGE_ALLOC_SIZE_MAX;
/* Memcache test */
extern unsigned int MEMCACHE_ALLOCS_MAX;
extern unsigned int TEST_CACHE_ITEM_SIZE;
/* Kmalloc */
extern unsigned int KMALLOC_ALLOCATIONS;
extern unsigned int KMALLOC_ALLOC_SIZE_MAX;
#endif /* __TESTS_H__ */