summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libddekit/Makefile9
-rw-r--r--libddekit/condvar.c108
-rw-r--r--libddekit/config.h13
-rw-r--r--libddekit/init.c35
-rw-r--r--libddekit/initcall.c8
-rw-r--r--libddekit/interrupt.c229
-rw-r--r--libddekit/lock.c62
-rw-r--r--libddekit/malloc.c94
-rw-r--r--libddekit/memory.c467
-rw-r--r--libddekit/panic.c29
-rw-r--r--libddekit/pci.c392
-rw-r--r--libddekit/pgtab-old.c219
-rw-r--r--libddekit/pgtab.c219
-rw-r--r--libddekit/printf.c46
-rw-r--r--libddekit/resources.c57
-rw-r--r--libddekit/semaphore.c48
-rw-r--r--libddekit/thread.c195
-rw-r--r--libddekit/timer.c329
18 files changed, 2559 insertions, 0 deletions
diff --git a/libddekit/Makefile b/libddekit/Makefile
new file mode 100644
index 00000000..67d58733
--- /dev/null
+++ b/libddekit/Makefile
@@ -0,0 +1,9 @@
+PKGDIR ?= ..
+L4DIR ?= $(PKGDIR)/../..
+
+# the default is to build the listed directories, provided that they
+# contain a Makefile. If you need to change this, uncomment the following
+# line and adapt it.
+# TARGET = idl src lib server examples doc
+
+include $(L4DIR)/mk/subdir.mk
diff --git a/libddekit/condvar.c b/libddekit/condvar.c
new file mode 100644
index 00000000..a495cf92
--- /dev/null
+++ b/libddekit/condvar.c
@@ -0,0 +1,108 @@
+/**
+ * Unchecked (no BSD invariants) condition variable implementation for
+ * dde-internal use. Written from scratch.
+ *
+ * \author Thomas Friebel <tf13@os.inf.tu-dresden.de>
+ */
+#include <l4/dde/ddekit/condvar.h>
+#include <l4/dde/ddekit/lock.h>
+#include <l4/dde/ddekit/memory.h>
+
+#include <l4/log/l4log.h>
+#include <l4/semaphore/semaphore.h>
+#include <l4/lock/lock.h>
+
+struct ddekit_condvar {
+ unsigned waiters;
+ unsigned signals;
+ l4lock_t lock;
+ l4semaphore_t sem;
+ l4semaphore_t handshake;
+};
+
+ddekit_condvar_t *ddekit_condvar_init() {
+ ddekit_condvar_t *cvp;
+
+ cvp = ddekit_simple_malloc(sizeof(*cvp));
+
+ cvp->waiters = 0;
+ cvp->signals = 0;
+ cvp->lock = L4LOCK_UNLOCKED;
+ cvp->sem = L4SEMAPHORE_INIT(0);
+ cvp->handshake = L4SEMAPHORE_INIT(0);
+
+ return cvp;
+}
+
+void ddekit_condvar_wait(ddekit_condvar_t *cvp, ddekit_lock_t *mp) {
+ ddekit_condvar_wait_timed(cvp, mp, -1);
+}
+
+int ddekit_condvar_wait_timed(ddekit_condvar_t *cvp, ddekit_lock_t *mp, int timo) {
+ int rval;
+
+ l4lock_lock(&cvp->lock);
+ cvp->waiters++;
+ l4lock_unlock(&cvp->lock);
+
+ ddekit_lock_unlock(mp);
+
+ if (timo == -1) {
+ l4semaphore_down(&cvp->sem);
+ rval = 0;
+ } else {
+ rval = l4semaphore_down_timed(&cvp->sem, timo);
+ }
+
+ l4lock_lock(&cvp->lock);
+ if (cvp->signals > 0) {
+ /* if we timed out, but there is a signal now, consume it */
+ if (rval) l4semaphore_down(&cvp->sem);
+
+ l4semaphore_up(&cvp->handshake);
+ cvp->signals--;
+ }
+ cvp->waiters--;
+ l4lock_unlock(&cvp->lock);
+
+ ddekit_lock_lock(mp);
+
+ return rval;
+}
+
+void ddekit_condvar_signal(ddekit_condvar_t *cvp)
+{
+ l4lock_lock(&cvp->lock);
+
+ if (cvp->waiters > cvp->signals) {
+ cvp->signals++;
+ l4semaphore_up(&cvp->sem);
+ l4lock_unlock(&cvp->lock);
+ l4semaphore_down(&cvp->handshake);
+ } else {
+ /* nobody left to wakeup */
+ l4lock_unlock(&cvp->lock);
+ }
+}
+
+void ddekit_condvar_broadcast(ddekit_condvar_t *cvp) {
+ int waiters;
+
+ l4lock_lock(&cvp->lock);
+
+ waiters = cvp->waiters - cvp->signals;
+ if (waiters > 0) {
+ int i;
+
+ cvp->signals = cvp->waiters;
+ for (i=0; i<waiters; i++) {
+ l4semaphore_up(&cvp->sem);
+ }
+ l4lock_unlock(&cvp->lock);
+ for (i=0; i<waiters; i++) {
+ l4semaphore_down(&cvp->handshake);
+ }
+ } else {
+ l4lock_unlock(&cvp->lock);
+ }
+}
diff --git a/libddekit/config.h b/libddekit/config.h
new file mode 100644
index 00000000..fdf63562
--- /dev/null
+++ b/libddekit/config.h
@@ -0,0 +1,13 @@
+/**
+ * \file ddekit/include/config.h
+ * \brief Configuration file for ddekit.
+ *
+ * FIXME this file is empty
+ */
+
+#ifndef __ddekit_config_h
+#define __ddekit_config_h
+
+#define DEBUG_MSG(msg, ...) ddekit_printf("%s: \033[35m"msg"\033[0m\n", __FUNCTION__, ##__VA_ARGS__)
+
+#endif /* __ddekit_config_h */
diff --git a/libddekit/init.c b/libddekit/init.c
new file mode 100644
index 00000000..0532caed
--- /dev/null
+++ b/libddekit/init.c
@@ -0,0 +1,35 @@
+/**
+ * The functions regarding DDE/BSD initialization are found here.
+ *
+ * \author Thomas Friebel <tf13@os.inf.tu-dresden.de>
+ */
+#include <l4/dde/ddekit/panic.h>
+#include <l4/dde/ddekit/thread.h>
+#include <l4/dde/ddekit/memory.h>
+
+#include <l4/dde/dde.h>
+#include <l4/log/l4log.h>
+#include <l4/env/errno.h>
+#include <l4/generic_io/libio.h>
+
+/* FIXME this must be initialized explicitly as some users may not need l4io,
+ * e.g., l4io's own pcilib. */
+static void ddekit_init_l4io(void)
+{
+ int err;
+ l4io_info_t *ioip = NULL;
+
+ LOGd(0, "mapping io info page to %p", ioip);
+ err = l4io_init(&ioip, L4IO_DRV_INVALID);
+ if ( err | !ioip ) {
+ LOG("error initializing io lib: %s (err=%d, ioip=%p)", l4env_errstr(err), err, ioip);
+ ddekit_panic("fatal error");
+ }
+}
+
+void ddekit_init(void)
+{
+ ddekit_init_l4io();
+ ddekit_init_threads();
+}
+
diff --git a/libddekit/initcall.c b/libddekit/initcall.c
new file mode 100644
index 00000000..49e1fec1
--- /dev/null
+++ b/libddekit/initcall.c
@@ -0,0 +1,8 @@
+#include <l4/dde/ddekit/initcall.h>
+
+#include <l4/crtx/crt0.h>
+
+void ddekit_do_initcalls() {
+ crt0_dde_construction();
+}
+
diff --git a/libddekit/interrupt.c b/libddekit/interrupt.c
new file mode 100644
index 00000000..557611a6
--- /dev/null
+++ b/libddekit/interrupt.c
@@ -0,0 +1,229 @@
+/*
+ * \brief Hardware-interrupt subsystem
+ * \author Thomas Friebel <tf13@os.inf.tu-dresden.de>
+ * \author Christian Helmuth <ch12@os.inf.tu-dresden.de>
+ * \date 2007-01-22
+ *
+ * FIXME could intloop_param freed after startup?
+ * FIXME use consume flag to indicate IRQ was handled
+ */
+
+#include <l4/dde/ddekit/interrupt.h>
+#include <l4/dde/ddekit/semaphore.h>
+#include <l4/dde/ddekit/thread.h>
+#include <l4/dde/ddekit/memory.h>
+#include <l4/dde/ddekit/panic.h>
+
+#include <l4/omega0/client.h>
+#include <l4/log/l4log.h>
+#include <l4/thread/thread.h>
+
+#include <stdio.h>
+
+#define DEBUG_INTERRUPTS 0
+
+#define MAX_INTERRUPTS 32
+
+#define BLOCK_IRQ 0
+
+/*
+ * Internal type for interrupt loop parameters
+ */
+struct intloop_params
+{
+ unsigned irq; /* irq number */
+ int shared; /* irq sharing supported? */
+ void(*thread_init)(void *); /* thread initialization */
+ void(*handler)(void *); /* IRQ handler function */
+ void *priv; /* private token */
+ ddekit_sem_t *started;
+
+ int start_err;
+};
+
+static struct
+{
+ int handle_irq; /* nested irq disable count */
+ ddekit_sem_t *irqsem; /* synch semaphore */
+ ddekit_thread_t *irq_thread; /* thread ID for detaching from IRQ later on */
+ omega0_irqdesc_t irq_desc; /* Omega0-style IRQ descriptor */
+} ddekit_irq_ctrl[MAX_INTERRUPTS];
+
+
+static void ddekit_irq_exit_fn(l4thread_t thread, void *data)
+{
+ int idx = (int)data;
+
+ // * detach from IRQ
+ omega0_detach(ddekit_irq_ctrl[idx].irq_desc);
+}
+
+L4THREAD_EXIT_FN_STATIC(exit_fn, ddekit_irq_exit_fn);
+
+
+/**
+ * Interrupt service loop
+ *
+ */
+static void intloop(void *arg)
+{
+ struct intloop_params *params = arg;
+
+ l4thread_set_prio(l4thread_myself(), DDEKIT_IRQ_PRIO);
+
+ omega0_request_t req = { .i = 0 };
+ int o0handle;
+ int my_index = params->irq;
+ omega0_irqdesc_t *desc = &ddekit_irq_ctrl[my_index].irq_desc;
+
+ /* setup interrupt descriptor */
+ desc->s.num = params->irq + 1;
+ desc->s.shared = params->shared;
+
+ /* allocate irq */
+ o0handle = omega0_attach(*desc);
+ if (o0handle < 0) {
+ /* inform thread creator of error */
+ /* XXX does omega0 error code have any meaning to DDEKit users? */
+ params->start_err = o0handle;
+ ddekit_sem_up(params->started);
+ return;
+ }
+
+ /*
+ * Setup an exit fn. This will make sure that we clean up everything,
+ * before shutting down an IRQ thread.
+ */
+ if (l4thread_on_exit(&exit_fn, (void *)my_index) < 0)
+ ddekit_panic("Could not set exit handler for IRQ fn.");
+
+ /* after successful initialization call thread_init() before doing anything
+ * else here */
+ if (params->thread_init) params->thread_init(params->priv);
+
+ /* save handle + inform thread creator of success */
+ params->start_err = 0;
+ ddekit_sem_up(params->started);
+
+ /* prepare request structure */
+ req.s.param = params->irq + 1;
+ req.s.wait = 1;
+ req.s.consume = 0;
+ req.s.unmask = 1;
+
+ while (1) {
+ int err;
+
+ /* wait for int */
+ err = omega0_request(o0handle, req);
+ if (err != params->irq + 1)
+ LOG("omega0_request returned %d, %d (irq+1) expected", err, params->irq + 1);
+
+ LOGd(DEBUG_INTERRUPTS, "received irq 0x%X", err - 1);
+
+ /* unmask only the first time */
+ req.s.unmask = 0;
+
+ /* only call registered handler function, if IRQ is not disabled */
+ ddekit_sem_down(ddekit_irq_ctrl[my_index].irqsem);
+ if (ddekit_irq_ctrl[my_index].handle_irq > 0) {
+ LOGd(DEBUG_INTERRUPTS, "IRQ %x, handler %p", my_index,params->handler);
+ params->handler(params->priv);
+ }
+ else
+ LOGd(DEBUG_INTERRUPTS, "not handling IRQ %x, because it is disabled.", my_index);
+
+ ddekit_sem_up(ddekit_irq_ctrl[my_index].irqsem);
+ LOGd(DEBUG_INTERRUPTS, "after irq handler");
+ }
+}
+
+
+/**
+ * Attach to hardware interrupt
+ *
+ * \param irq IRQ number to attach to
+ * \param shared set to 1 if interrupt sharing is supported; set to 0
+ * otherwise
+ * \param thread_init called just after DDEKit internal init and before any
+ * other function
+ * \param handler IRQ handler for interrupt irq
+ * \param priv private token (argument for thread_init and handler)
+ *
+ * \return pointer to interrupt thread created
+ */
+ddekit_thread_t *ddekit_interrupt_attach(int irq, int shared,
+ void(*thread_init)(void *),
+ void(*handler)(void *), void *priv)
+{
+ struct intloop_params *params;
+ ddekit_thread_t *thread;
+ char thread_name[10];
+
+ /* initialize info structure for interrupt loop */
+ params = ddekit_simple_malloc(sizeof(*params));
+ if (!params) return NULL;
+
+ params->irq = irq;
+ params->thread_init = thread_init;
+ params->handler = handler;
+ params->priv = priv;
+ params->started = ddekit_sem_init(0);
+ params->start_err = 0;
+ params->shared = shared;
+
+ /* construct name */
+ snprintf(thread_name, 10, "irq%02X", irq);
+
+ /* create interrupt loop thread */
+ thread = ddekit_thread_create(intloop, params, thread_name);
+ if (!thread) {
+ ddekit_simple_free(params);
+ return NULL;
+ }
+
+ ddekit_irq_ctrl[irq].handle_irq = 1; /* IRQ nesting level is initially 1 */
+ ddekit_irq_ctrl[irq].irq_thread = thread;
+ ddekit_irq_ctrl[irq].irqsem = ddekit_sem_init(1);
+
+
+ /* wait for intloop initialization result */
+ ddekit_sem_down(params->started);
+ ddekit_sem_deinit(params->started);
+ if (params->start_err) {
+ ddekit_simple_free(params);
+ return NULL;
+ }
+
+ return thread;
+}
+
+/**
+ * Detach from interrupt by disabling it and then shutting down the IRQ
+ * thread.
+ */
+void ddekit_interrupt_detach(int irq)
+{
+ ddekit_interrupt_disable(irq);
+ ddekit_thread_terminate(ddekit_irq_ctrl[irq].irq_thread);
+}
+
+
+void ddekit_interrupt_disable(int irq)
+{
+ if (ddekit_irq_ctrl[irq].irqsem) {
+ ddekit_sem_down(ddekit_irq_ctrl[irq].irqsem);
+ --ddekit_irq_ctrl[irq].handle_irq;
+ ddekit_sem_up(ddekit_irq_ctrl[irq].irqsem);
+ }
+}
+
+
+void ddekit_interrupt_enable(int irq)
+{
+ if (ddekit_irq_ctrl[irq].irqsem) {
+ ddekit_sem_down(ddekit_irq_ctrl[irq].irqsem);
+ ++ddekit_irq_ctrl[irq].handle_irq;
+ ddekit_sem_up(ddekit_irq_ctrl[irq].irqsem);
+ }
+}
diff --git a/libddekit/lock.c b/libddekit/lock.c
new file mode 100644
index 00000000..0f451dfd
--- /dev/null
+++ b/libddekit/lock.c
@@ -0,0 +1,62 @@
+#include <l4/dde/ddekit/lock.h>
+#include <l4/dde/ddekit/memory.h>
+
+#include <l4/lock/lock.h>
+#include <l4/util/macros.h>
+
+#define DDEKIT_DEBUG_LOCKS 0
+
+struct ddekit_lock {
+ l4lock_t lock;
+};
+
+void _ddekit_lock_init(struct ddekit_lock **mtx) {
+ *mtx = (struct ddekit_lock *) ddekit_simple_malloc(sizeof(struct ddekit_lock));
+ (*mtx)->lock = L4LOCK_UNLOCKED;
+}
+
+void _ddekit_lock_deinit(struct ddekit_lock **mtx) {
+ ddekit_simple_free(*mtx);
+ *mtx = NULL;
+}
+
+void _ddekit_lock_lock(struct ddekit_lock **mtx) {
+#if DDEKIT_DEBUG_LOCKS
+ if (&(*mtx)->lock == 0x35ac)
+ LOG("DOWN %p: "l4util_idfmt" <-> "l4util_idfmt,
+ &(*mtx)->lock,
+ l4util_idstr(l4_myself()),
+ l4util_idstr(l4thread_l4_id(l4lock_owner(&((*mtx)->lock)))));
+#endif
+ l4lock_lock(&(*mtx)->lock);
+#if DDEKIT_DEBUG_LOCKS
+ if (&(*mtx)->lock == 0x35ac)
+ LOG("DOWN %p! "l4util_idfmt, &(*mtx)->lock, l4util_idstr(l4_myself()));
+#endif
+}
+
+/* returns 0 on success, != 0 if it would block */
+int _ddekit_lock_try_lock(struct ddekit_lock **mtx) {
+ return l4lock_try_lock(&(*mtx)->lock) ? 0 : 1;
+}
+
+void _ddekit_lock_unlock(struct ddekit_lock **mtx) {
+#if DDEKIT_DEBUG_LOCKS
+ if (&(*mtx)->lock == 0x35ac)
+ LOG("UP %p: "l4util_idfmt" <-> "l4util_idfmt,
+ &(*mtx)->lock,
+ l4util_idstr(l4_myself()),
+ l4util_idstr(l4thread_l4_id(l4lock_owner(&((*mtx)->lock)))));
+#endif
+ l4lock_unlock(&(*mtx)->lock);
+#if DDEKIT_DEBUG_LOCKS
+ if (&(*mtx)->lock == 0x35ac)
+ LOG("UP %p! "l4util_idfmt, &(*mtx)->lock, l4util_idstr(l4_myself()));
+#endif
+}
+
+
+int _ddekit_lock_owner(struct ddekit_lock **mtx) {
+ return (int)l4lock_owner(&(*mtx)->lock);
+}
+
diff --git a/libddekit/malloc.c b/libddekit/malloc.c
new file mode 100644
index 00000000..5f0ae0fc
--- /dev/null
+++ b/libddekit/malloc.c
@@ -0,0 +1,94 @@
+/*
+ * \brief Simple allocator implementation
+ * \author Christian Helmuth
+ * \date 2006-10-30
+ *
+ * This simple allocator provides malloc() and free() using dm_mem dataspaces
+ * as backing store. The actual list-based allocator implementation is from
+ * l4util resp. Fiasco.
+ *
+ * For large allocations and slab-based OS-specific allocators
+ * ddekit_large_malloc and ddekit_slab_*() should be used. The blocks
+ * allocated via this allocator CANNOT be used for DMA or other device
+ * operations, i.e., there exists no virt->phys mapping.
+ *
+ * FIXME check thread-safety and add locks where appropriate
+ */
+
+#include <l4/dde/ddekit/memory.h>
+#include <l4/dde/ddekit/printf.h>
+#include <l4/dde/ddekit/panic.h>
+
+#include <l4/sys/consts.h>
+#include <l4/util/list_alloc.h>
+#include <l4/dm_mem/dm_mem.h>
+#include <l4/lock/lock.h>
+
+/* configuration */
+#define ALLOC_SIZE (4 * L4_PAGESIZE)
+
+/* malloc pool is a list allocator */
+static l4la_free_t *malloc_pool;
+static l4lock_t malloc_lock = L4LOCK_UNLOCKED;
+
+
+/**
+ * Allocate memory block via simple allocator
+ *
+ * \param size block size
+ * \return pointer to new memory block
+ *
+ * The blocks allocated via this allocator CANNOT be used for DMA or other
+ * device operations, i.e., there exists no virt->phys mapping.
+ *
+ * Each chunk stores its size in the first word for free() to work.
+ */
+void *ddekit_simple_malloc(unsigned size)
+{
+ l4lock_lock(&malloc_lock);
+ /* we store chunk size in the first word of the chunk */
+ size += sizeof(unsigned);
+
+ /* try to allocate */
+ unsigned *p = l4la_alloc(&malloc_pool, size, 0);
+
+ /* fill pool if allocation fails */
+ if (!p) {
+ /* size of allocated dataspace is at least ALLOC_SIZE */
+ unsigned ds_size = l4_round_page(size);
+ ds_size = (ds_size > ALLOC_SIZE) ? ds_size : ALLOC_SIZE;
+
+ void *res = l4dm_mem_allocate_named(ds_size, L4RM_MAP, "ddekit malloc");
+ if (!res)
+ p = NULL;
+ else
+ {
+ l4la_free(&malloc_pool, res, ds_size);
+ p = l4la_alloc(&malloc_pool, size, 0);
+ }
+ }
+
+ /* store chunk size */
+ if (p) {
+ *p = size;
+ p++;
+ }
+
+ l4lock_unlock(&malloc_lock);
+ return p;
+}
+
+
+/**
+ * Free memory block via simple allocator
+ *
+ * \param p pointer to memory block
+ */
+void ddekit_simple_free(void *p)
+{
+ l4lock_lock(&malloc_lock);
+ unsigned *chunk = (unsigned *)p - 1;
+ if (p)
+ l4la_free(&malloc_pool, chunk, *chunk);
+ l4lock_unlock(&malloc_lock);
+}
diff --git a/libddekit/memory.c b/libddekit/memory.c
new file mode 100644
index 00000000..b6db0cac
--- /dev/null
+++ b/libddekit/memory.c
@@ -0,0 +1,467 @@
+/*
+ * \brief Memory subsystem
+ * \author Thomas Friebel <tf13@os.inf.tu-dresden.de>
+ * \author Christian Helmuth <ch12@os.inf.tu-dresden.de>
+ * \date 2006-11-03
+ *
+ * The memory subsystem provides the backing store for DMA-able memory via
+ * large malloc and slabs.
+ *
+ * FIXME check thread-safety and add locks where appropriate
+ */
+
+#include <l4/dde/ddekit/memory.h>
+#include <l4/dde/ddekit/panic.h>
+#include <l4/dde/ddekit/pgtab.h>
+#include <l4/dde/ddekit/printf.h>
+
+#include <l4/lock/lock.h>
+#include <l4/slab/slab.h>
+#include <l4/dm_mem/dm_mem.h>
+#include <l4/util/atomic.h>
+#include <l4/util/util.h>
+
+
+/****************
+ ** Page cache **
+ ****************/
+
+/* FIXME revisit two-linked-lists approach - we could get rid of the
+ * pcache_free lists, because these descriptors can also be allocated
+ * whenever the need arises. (XXX: Check how this hurts performance.)
+ */
+
+/*****************************************************************************
+ DDEKit maintains a list of free pages that can be used for growing
+ existing DDEKit slabs. We distinguish between 2 types of pages: the ones
+ that have been allocated from physically contiguous dataspaces and the the
+ ones that have not.
+
+ For each type of cache, we maintain two lists: a list of free pages that
+ can be used to grow a cache (pcache_used) and a list of free pcache entries
+ that can be used to store pages when they get freed (pcache_free). The
+ reason for this is that by using these two lists, cache operations can be
+ done atomically using cmpxchg.
+
+ The function ddekit_slab_setup_page_cache() is used to tune the number of
+ cached pages.
+ ******************************************************************************/
+
+/* page cache to minimize allocations from external servers */
+struct ddekit_pcache
+{
+ struct ddekit_pcache *next;
+ void *page;
+ int contig;
+};
+
+
+/* head of single-linked list containing used page-cache entries, non-contiguous */
+static struct ddekit_pcache *pcache_used;
+/* head of single-linked list containing free page-cache entries, non-contiguous */
+static struct ddekit_pcache *pcache_free;
+
+/* head of single-linked list containing used page-cache entries, contiguous */
+static struct ddekit_pcache *pcache_used_contig;
+/* head of single-linked list containing free page-cache entries, contiguous */
+static struct ddekit_pcache *pcache_free_contig;
+
+/* maximum number of pages to cache. defaults to a minimum of 1 page
+ * because having none hits the performance too hard. */
+static l4_uint32_t pcache_num_entries = 1;
+
+
+/**
+ * Setup page cache for all slabs
+ *
+ * \param pages maximal number of memory pages
+ *
+ * If the maximal number of unused pages is exceeded, subsequent deallocation
+ * will be freed at the memory server. This page cache caches pages from all
+ * slabs.
+ */
+void ddekit_slab_setup_page_cache(unsigned pages)
+{
+ /* FIXME just allowing to grow at the moment */
+ while (pcache_num_entries < pages) {
+ struct ddekit_pcache *new_entry, *new_contig;
+
+ /* create new list element */
+ new_entry = (struct ddekit_pcache *) ddekit_simple_malloc(sizeof(*new_entry));
+ new_contig = (struct ddekit_pcache *) ddekit_simple_malloc(sizeof(*new_contig));
+
+ /* insert into lists of unused cache entries */
+ do {
+ new_entry->next = pcache_free;
+ } while (!l4util_cmpxchg32((l4_uint32_t*)&pcache_free,
+ (l4_uint32_t)new_entry->next,
+ (l4_uint32_t)new_entry));
+
+ do {
+ new_contig->next = pcache_free_contig;
+ } while (!l4util_cmpxchg32((l4_uint32_t*)&pcache_free_contig,
+ (l4_uint32_t)new_entry->next,
+ (l4_uint32_t)new_entry));
+
+ /* increment number of list elements */
+ l4util_inc32(&pcache_num_entries);
+ }
+}
+
+
+/**
+ * Try to allocate a new page from the pcache.
+ */
+static inline struct ddekit_pcache *_try_from_cache(int contig)
+{
+ struct ddekit_pcache *head = NULL;
+ struct ddekit_pcache *the_cache = contig ? pcache_used_contig : pcache_used;
+
+ do {
+ head = the_cache;
+ if (!head) break;
+ } while (!l4util_cmpxchg32((l4_uint32_t*)&the_cache, (l4_uint32_t)head,
+ (l4_uint32_t)head->next));
+
+ return head;
+}
+
+
+static inline void _add_to_cache(struct ddekit_pcache *entry, int contig)
+{
+ struct ddekit_pcache *the_cache = contig ? pcache_used_contig : pcache_used;
+ do {
+ entry->next = the_cache;
+ } while (! l4util_cmpxchg32((l4_uint32_t*)&the_cache, (l4_uint32_t)entry->next,
+ (l4_uint32_t)entry));
+}
+
+/**
+ * Return free entry to cached entry list.
+ */
+static inline void _free_cache_entry(struct ddekit_pcache *entry, int contig)
+{
+ struct ddekit_pcache *the_cache = contig ? pcache_free_contig : pcache_free;
+ do {
+ entry->next = the_cache;
+ } while (!l4util_cmpxchg32((l4_uint32_t*)&the_cache, (l4_uint32_t)entry->next,
+ (l4_uint32_t)entry));
+}
+
+
+static inline struct ddekit_pcache *_get_free_cache_entry(int contig)
+{
+ struct ddekit_pcache *the_cache = contig ? pcache_free_contig : pcache_free;
+ struct ddekit_pcache *head = NULL;
+
+ do {
+ head = the_cache;
+ if (!head) break;
+ } while (!l4util_cmpxchg32((l4_uint32_t*)&the_cache, (l4_uint32_t)head,
+ (l4_uint32_t) head->next));
+
+ return head;
+}
+
+
+/*******************************
+ ** Slab cache implementation **
+ *******************************/
+
+/* ddekit slab facilitates l4slabs */
+struct ddekit_slab
+{
+ l4slab_cache_t cache;
+ /*
+ * Lock to prevent concurrent access to the slab's grow() and
+ * shrink() functions.
+ */
+ l4lock_t lock;
+ int contiguous;
+};
+
+
+/**
+ * Get the ddekit slab for a given L4 slab.
+ *
+ * We cheat here, because we know that the L4 slab is the first member
+ * of the ddekit slab.
+ */
+static inline struct ddekit_slab *ddekit_slab_from_l4slab(l4slab_cache_t *s)
+{
+ return (struct ddekit_slab *)s;
+}
+
+/**
+ * Grow slab cache
+ */
+static void *_slab_grow(l4slab_cache_t *cache, void **data)
+{
+ /* the page(s) to be returned */
+ void *res = NULL;
+ /* whether this cache needs physically contiguous pages */
+ int is_contig = ddekit_slab_from_l4slab(cache)->contiguous;
+
+ /* free cache entry, will be used afterwards */
+ struct ddekit_pcache *head = NULL;
+
+ /* We don't reuse pages for slabs > 1 page, because this makes caching
+ * somewhat harder. */
+ if (cache->slab_size <= L4_PAGESIZE)
+ /* try to aquire cached page */
+ head = _try_from_cache(is_contig);
+
+ if (head) {
+ /* use cached page */
+ res = head->page;
+ /* this cache entry is now available */
+ _free_cache_entry(head, is_contig);
+ } else {
+ /* allocate new page at memory server */
+ int err;
+ l4_size_t tmp;
+ l4dm_mem_addr_t dm_paddr;
+ int num_pages = cache->slab_size / L4_PAGESIZE;
+ int flags = L4DM_PINNED | L4RM_MAP | L4RM_LOG2_ALIGNED;
+
+ if (is_contig)
+ flags |= L4DM_CONTIGUOUS;
+
+ /* allocate and map new page(s) */
+ res = l4dm_mem_allocate_named(num_pages * L4_PAGESIZE,
+ flags,
+ "ddekit slab");
+ if (res == NULL)
+ ddekit_debug("__grow: error allocating a new page");
+
+ /* physically contiguous pages need some special treatment */
+ if (is_contig) {
+ err = l4dm_mem_phys_addr(res, num_pages, &dm_paddr, 1, &tmp);
+ if (err != 1)
+ ddekit_debug("__grow: error getting physical address of new page!");
+
+ ddekit_pgtab_set_region(res, dm_paddr.addr, num_pages, PTE_TYPE_UMA);
+ }
+ }
+
+ /* save pointer to cache in page for ddekit_slab_get_slab() */
+ *data = cache;
+
+ return res;
+}
+
+/**
+ * Shrink slab cache
+ */
+static void _slab_shrink(l4slab_cache_t *cache, void *page, void *data)
+{
+ /* cache deallocated page here */
+ struct ddekit_pcache *head = NULL;
+ /* whether this cache needs physically contiguous pages */
+ int is_contig = ddekit_slab_from_l4slab(cache)->contiguous;
+
+ /* we do not return slabs to the page cache, if they are larger than one page */
+ if (cache->slab_size <= L4_PAGESIZE)
+ /* try to aquire free cache entry */
+ head = _get_free_cache_entry(is_contig);
+
+ if (head) {
+ /* use free cache entry to cache page */
+
+ /* set the page info */
+ head->page = page;
+
+ /* this cache entry contains a free page */
+ _add_to_cache(head, is_contig);
+ } else {
+ /* cache is full */
+
+ if (is_contig)
+ /* unset pte */
+ ddekit_pgtab_clear_region(page, PTE_TYPE_UMA);
+
+ /* free page */
+ l4dm_mem_release(page);
+ }
+}
+
+
+/**
+ * Allocate object in slab
+ */
+void *ddekit_slab_alloc(struct ddekit_slab * slab)
+{
+ void *ret = NULL;
+ l4lock_lock(&slab->lock);
+ ret = l4slab_alloc(&slab->cache);
+ l4lock_unlock(&slab->lock);
+
+ return ret;
+}
+
+
+/**
+ * Free object in slab
+ */
+void ddekit_slab_free(struct ddekit_slab * slab, void *objp)
+{
+ l4lock_lock(&slab->lock);
+ l4slab_free(&slab->cache, objp);
+ l4lock_unlock(&slab->lock);
+}
+
+
+/**
+ * Store user pointer in slab cache
+ */
+void ddekit_slab_set_data(struct ddekit_slab * slab, void *data)
+{
+ l4slab_set_data(&slab->cache, data);
+}
+
+
+/**
+ * Read user pointer from slab cache
+ */
+void *ddekit_slab_get_data(struct ddekit_slab * slab)
+{
+ return l4slab_get_data(&slab->cache);
+}
+
+
+/**
+ * Destroy slab cache
+ *
+ * \param slab pointer to slab cache structure
+ */
+void ddekit_slab_destroy (struct ddekit_slab * slab)
+{
+ l4slab_destroy(&slab->cache);
+ ddekit_simple_free(slab);
+}
+
+
+/**
+ * Initialize slab cache
+ *
+ * \param size size of cache objects
+ * \param contiguous make this slab use physically contiguous memory
+ *
+ * \return pointer to new slab cache or 0 on error
+ */
+struct ddekit_slab * ddekit_slab_init(unsigned size, int contiguous)
+{
+ struct ddekit_slab * slab;
+ int err;
+
+ slab = (struct ddekit_slab *) ddekit_simple_malloc(sizeof(*slab));
+ slab->lock = L4LOCK_UNLOCKED;
+ err = l4slab_cache_init(&slab->cache, size, 0, _slab_grow, _slab_shrink);
+ if (err) {
+ ddekit_debug("error initializing cache");
+ ddekit_simple_free(slab);
+ return 0;
+ }
+
+ slab->contiguous = contiguous;
+
+ return slab;
+}
+
+
+/**********************************
+ ** Large block memory allocator **
+ **********************************/
+
+/**
+ * Free large block of memory
+ *
+ * This is no useful for allocation < page size.
+ */
+void ddekit_large_free(void *objp)
+{
+ /* clear PTEs */
+ ddekit_pgtab_clear_region(objp, PTE_TYPE_LARGE);
+
+ /* release */
+ l4dm_mem_release(objp);
+}
+
+
+/**
+ * Allocate large block of memory
+ *
+ * This is no useful for allocation < page size.
+ */
+void *ddekit_large_malloc(int size)
+{
+ void *res;
+ int err;
+ l4_size_t tmp;
+ int pages;
+ l4dm_mem_addr_t dm_paddr;
+
+ size = l4_round_page(size);
+ pages = size >> L4_PAGESHIFT;
+
+ res = l4dm_mem_allocate_named(size,
+ L4DM_CONTIGUOUS | L4DM_PINNED |
+ L4RM_MAP | L4RM_LOG2_ALIGNED,
+ "ddekit mem");
+ if (! res)
+ return NULL;
+
+ err = l4dm_mem_phys_addr(res, 1, &dm_paddr, 1, &tmp);
+ if (err != 1)
+ ddekit_debug("ddekit_large_malloc: error getting physical address of new memory!\n");
+
+ ddekit_pgtab_set_region(res, dm_paddr.addr, pages, PTE_TYPE_LARGE);
+
+ return res;
+}
+
+
+/**
+ * Allocate large block of memory (special interface)
+ *
+ * This is no useful for allocation < page size.
+ *
+ * FIXME implementation missing...
+ */
+void *ddekit_contig_malloc(unsigned long size,
+ unsigned long low, unsigned long high,
+ unsigned long alignment, unsigned long boundary)
+{
+#if 0
+ void *res;
+ int err;
+ int pages;
+ l4_size_t tmp;
+ l4dm_mem_addr_t dm_paddr;
+
+ size = l4_round_page(size);
+ pages = size >> L4_PAGESHIFT;
+
+ res = l4dm_mem_allocate_named(size, L4DM_CONTIGUOUS | L4DM_PINNED | L4RM_MAP | L4RM_LOG2_ALIGNED);
+
+ if (res) {
+ /* check if res meets low/high/alignment/boundary
+ * and panic if it is not the case
+ * XXXY
+ */
+
+ /* get physical address */
+ err = l4dm_mem_phys_addr(res, 1, &dm_paddr, 1, &tmp);
+ if (err != 1)
+ ddekit_debug("contigmalloc: error getting physical address of new page!\n");
+
+ /* set PTE */
+ ddekit_set_ptes(res, dm_paddr.addr, pages, PTE_TYPE_CONTIG);
+ }
+
+ return res;
+#else
+ ddekit_debug("%s: not implemented\n", __func__);
+ return 0;
+#endif
+}
diff --git a/libddekit/panic.c b/libddekit/panic.c
new file mode 100644
index 00000000..24ace989
--- /dev/null
+++ b/libddekit/panic.c
@@ -0,0 +1,29 @@
+#include <l4/dde/ddekit/panic.h>
+#include <l4/dde/ddekit/printf.h>
+
+#include <l4/sys/kdebug.h>
+#include <stdarg.h>
+
+void ddekit_panic(char *fmt, ...) {
+ va_list va;
+
+ va_start(va, fmt);
+ ddekit_vprintf(fmt, va);
+ va_end(va);
+ ddekit_printf("\n");
+
+ while (1)
+ enter_kdebug("ddekit_panic()");
+}
+
+void ddekit_debug(char *fmt, ...) {
+ va_list va;
+
+ va_start(va, fmt);
+ ddekit_vprintf(fmt, va);
+ va_end(va);
+ ddekit_printf("\n");
+
+ enter_kdebug("ddekit_debug()");
+}
+
diff --git a/libddekit/pci.c b/libddekit/pci.c
new file mode 100644
index 00000000..35fb7aea
--- /dev/null
+++ b/libddekit/pci.c
@@ -0,0 +1,392 @@
+#include <l4/dde/ddekit/pci.h>
+#include <l4/dde/ddekit/printf.h>
+#include <l4/dde/ddekit/panic.h>
+#include <l4/dde/ddekit/memory.h>
+#include <l4/dde/ddekit/assert.h>
+
+#include <l4/generic_io/types.h>
+#include <l4/generic_io/libio.h>
+#include <l4/env/errno.h>
+#include <l4/log/l4log.h>
+
+#include "config.h"
+
+#define dbg_this 0
+
+#define MAX_PCI_DEVS 32
+
+/** PCI descriptor */
+typedef struct ddekit_pci_dev {
+ int bus; /**< bus ID */
+ int slot; /**< slot ID */
+ int func; /**< function */
+ l4io_pci_dev_t l4dev; /**< L4IO handle */
+ struct ddekit_pci_dev *next; /**< chaining info */
+} ddekit_pci_dev_t;
+
+static ddekit_pci_dev_t ddekit_pci_bus[MAX_PCI_DEVS];
+
+static inline int invalid_device(ddekit_pci_dev_t *d)
+{
+ return d->slot == -1;
+}
+
+
+/** Initialize DDEKit PCI module.
+ *
+ * \ingroup DDEKit_pci
+ *
+ * This function builds a list of devices by querying L4IO.
+ */
+void ddekit_pci_init(void)
+{
+ l4io_pdev_t start = 0;
+
+ int slots_found = 0;
+ int i;
+
+ /* Init device list */
+ for (i = 0; i < MAX_PCI_DEVS; i++)
+ ddekit_pci_bus[i].slot = -1;
+
+ while (1) {
+ l4io_pci_dev_t l4dev;
+ int err;
+
+ /* search next device */
+ err = l4io_pci_find_device(~0, ~0, start, &l4dev);
+ if (err) {
+ if (err == -L4_ENOTFOUND) {
+ LOGd(dbg_this, "no more pci devices");
+ } else {
+ LOGd(dbg_this, "error: scanning pci devices: %s (%d)", l4env_errstr(err), err);
+ }
+ break;
+ }
+
+ /* next search start from here */
+ start = l4dev.handle;
+
+ /* print info */
+
+ /* Pretend all our devices are chained to exactly one bus. */
+ ddekit_pci_bus[slots_found].bus = 0; /*l4dev.bus;*/
+ ddekit_pci_bus[slots_found].slot = slots_found;
+ ddekit_pci_bus[slots_found].func = 0;
+ ddekit_pci_bus[slots_found].l4dev = l4dev;
+
+ LOGd(dbg_this, "pcib_identify: found device (%x, %x, %x), mapped to (%x, %x, %x)",
+ l4dev.bus, l4dev.devfn >> 3, l4dev.devfn & 0x07,
+ ddekit_pci_bus[slots_found].bus,
+ ddekit_pci_bus[slots_found].slot,
+ ddekit_pci_bus[slots_found].func);
+
+ ++slots_found;
+ }
+}
+
+
+/**
+ * Get PCI device (bus, slot, func) for handle.
+ *
+ * \ingroup DDEKit_pci
+ */
+int ddekit_pci_get_device(int nr, int *bus, int *slot, int *func)
+{
+ ddekit_pci_dev_t *dev;
+
+ LOGd(dbg_this, "searching for dev #%d", nr);
+
+ if (nr >= 0 && nr < MAX_PCI_DEVS && !invalid_device(&ddekit_pci_bus[nr])) {
+ dev = &ddekit_pci_bus[nr];
+ *bus = dev->bus;
+ *slot = dev->slot;
+ *func = dev->func;
+ return 0;
+ }
+
+ return -1;
+}
+
+
+ddekit_pci_dev_t *ddekit_pci_find_device(int *bus, int *slot, int *func,
+ ddekit_pci_dev_t *start)
+{
+ Assert(bus);
+ Assert(slot);
+ Assert(func);
+
+ LOGd(dbg_this, "start %p (slot %d)", start, start ? start->slot : -1);
+ int idx = start ? start->slot + 1 : 0;
+
+ for ( ; idx < MAX_PCI_DEVS; ++idx) {
+ ddekit_pci_dev_t *dev = &ddekit_pci_bus[idx];
+ if (!invalid_device(dev) &&
+ (*bus == DDEKIT_PCI_ANY_ID || dev->bus == *bus) &&
+ (*slot == DDEKIT_PCI_ANY_ID || dev->slot == *slot) &&
+ (*func == DDEKIT_PCI_ANY_ID || dev->func == *func)) {
+ *bus = dev->bus;
+ *slot = dev->slot;
+ *func = dev->func;
+ return dev;
+ }
+ }
+
+ return NULL;
+}
+
+int ddekit_pci_read(int bus, int slot, int func, int pos, int len, ddekit_uint32_t *val)
+{
+ switch(len)
+ {
+ case 1:
+ return ddekit_pci_readb(bus, slot, func, pos, (ddekit_uint8_t *)val);
+ case 2:
+ return ddekit_pci_readw(bus, slot, func, pos, (ddekit_uint16_t *)val);
+ case 4:
+ return ddekit_pci_readl(bus, slot, func, pos, val);
+ }
+
+ return -1;
+}
+
+int ddekit_pci_write(int bus, int slot, int func, int pos, int len, ddekit_uint32_t val)
+{
+ switch(len)
+ {
+ case 1:
+ return ddekit_pci_writeb(bus, slot, func, pos, val);
+ case 2:
+ return ddekit_pci_writew(bus, slot, func, pos, val);
+ case 4:
+ return ddekit_pci_writel(bus, slot, func, pos, val);
+ }
+
+ return -1;
+}
+
+
+/** Find PCI device for a given (bus, slot, func) tuple.
+*
+* \ingroup DDEKit_pci
+ *
+ * This function is only used internally by the pci_{read,write}{b,l,w} functions.
+ * It was once called pci_find_device, but this caused confusion with the "real"
+ * find_device() functionality provided by L4IO and the Linux PCI subsystem.
+ */
+static inline ddekit_pci_dev_t *ddekit_pci_find_device_fixed(int bus, int slot, int func)
+{
+ return &ddekit_pci_bus[slot];
+}
+
+
+int ddekit_pci_readb (int bus, int slot, int func, int pos, ddekit_uint8_t *val)
+{
+ ddekit_pci_dev_t *dev = ddekit_pci_find_device_fixed(bus, slot, func);
+ if (dev)
+ return l4io_pci_readb_cfg(dev->l4dev.handle, pos, val);
+ else
+ return -1;
+}
+
+
+int ddekit_pci_readw (int bus, int slot, int func, int pos, ddekit_uint16_t *val)
+{
+ ddekit_pci_dev_t *dev = ddekit_pci_find_device_fixed(bus, slot, func);
+ if (dev)
+ return l4io_pci_readw_cfg(dev->l4dev.handle, pos, val);
+ else
+ return -1;
+}
+
+
+int ddekit_pci_readl (int bus, int slot, int func, int pos, ddekit_uint32_t *val)
+{
+ ddekit_pci_dev_t *dev = ddekit_pci_find_device_fixed(bus, slot, func);
+ if (dev)
+ return l4io_pci_readl_cfg(dev->l4dev.handle, pos, val);
+ else
+ return -1;
+}
+
+
+int ddekit_pci_writeb(int bus, int slot, int func, int pos, ddekit_uint8_t val)
+{
+ ddekit_pci_dev_t *dev = ddekit_pci_find_device_fixed(bus, slot, func);
+ if (dev)
+ return l4io_pci_writeb_cfg(dev->l4dev.handle, pos, val);
+ else
+ return -1;
+}
+
+
+int ddekit_pci_writew(int bus, int slot, int func, int pos, ddekit_uint16_t val)
+{
+ ddekit_pci_dev_t *dev = ddekit_pci_find_device_fixed(bus, slot, func);
+ if (dev)
+ return l4io_pci_writew_cfg(dev->l4dev.handle, pos, val);
+ else
+ return -1;
+}
+
+
+int ddekit_pci_writel(int bus, int slot, int func, int pos, ddekit_uint32_t val)
+{
+ ddekit_pci_dev_t *dev = ddekit_pci_find_device_fixed(bus, slot, func);
+ if (dev)
+ return l4io_pci_writel_cfg(dev->l4dev.handle, pos, val);
+ else
+ return -1;
+}
+
+int ddekit_pci_enable_device(struct ddekit_pci_dev *dev)
+{
+ return l4io_pci_enable(dev->l4dev.handle);
+}
+
+int ddekit_pci_disable_device(struct ddekit_pci_dev *dev)
+{
+ return l4io_pci_disable(dev->l4dev.handle);
+}
+
+/********************************************************************************
+ ** Accessor functions for the various kinds of information that are available **
+ ** from struct l4io_pci_dev (which is not handed through to the DDEs, because **
+ ** it still contains L4 types). **
+ ********************************************************************************/
+
+/**
+ * Get PCI device vendor ID.
+ *
+ * \param dev device
+ *
+ * \ingroup DDEKit_pci
+ */
+unsigned short ddekit_pci_get_vendor(struct ddekit_pci_dev *dev)
+{
+ return dev->l4dev.vendor;
+}
+
+
+/**
+ * Get PCI device ID.
+ *
+ * \param dev device
+ *
+ * \ingroup DDEKit_pci
+ */
+unsigned short ddekit_pci_get_device_id(struct ddekit_pci_dev *dev)
+{
+ return dev->l4dev.device;
+}
+
+
+/**
+ * Get PCI device subvendor ID.
+ *
+ * \param dev device
+ *
+ * \ingroup DDEKit_pci
+ */
+unsigned short ddekit_pci_get_sub_vendor(struct ddekit_pci_dev *dev)
+{
+ return dev->l4dev.sub_vendor;
+}
+
+
+/**
+ * Get PCI device subdevice ID.
+ *
+ * \param dev device
+ *
+ * \ingroup DDEKit_pci
+ */
+unsigned short ddekit_pci_get_sub_device(struct ddekit_pci_dev *dev)
+{
+ return dev->l4dev.sub_device;
+}
+
+
+/**
+ * Get PCI device class ID.
+ *
+ * \param dev device
+ *
+ * \ingroup DDEKit_pci
+ */
+unsigned ddekit_pci_get_dev_class(struct ddekit_pci_dev *dev)
+{
+ return dev->l4dev.dev_class;
+}
+
+
+/**
+ * Get PCI device IRQ number.
+ *
+ * \param dev device
+ *
+ * \ingroup DDEKit_pci
+ */
+unsigned long ddekit_pci_get_irq(struct ddekit_pci_dev *dev)
+{
+ return dev->l4dev.irq;
+}
+
+
+/**
+ * Get PCI device name.
+ *
+ * \param dev device
+ *
+ * \ingroup DDEKit_pci
+ */
+char *ddekit_pci_get_name(struct ddekit_pci_dev *dev)
+{
+ return dev->l4dev.name;
+}
+
+
+/**
+ * Get PCI device slot name.
+ *
+ * \param dev device
+ *
+ * \ingroup DDEKit_pci
+ */
+char *ddekit_pci_get_slot_name(struct ddekit_pci_dev *dev)
+{
+ return dev->l4dev.slot_name;
+}
+
+
+/**
+ * Get PCI device resource at index.
+ *
+ * \param dev device
+ * \param idx resource index
+ *
+ * \ingroup DDEKit_pci
+ */
+ddekit_pci_res_t *ddekit_pci_get_resource(struct ddekit_pci_dev *dev, unsigned int idx)
+{
+ if (idx > L4IO_PCIDEV_RES)
+ return NULL;
+
+ return (ddekit_pci_res_t *)(&(dev->l4dev.res[idx]));
+}
+
+
+/**
+ * Set PCI bus mastering for device.
+ *
+ * \ingroup DDEKit_pci
+ */
+void ddekit_pci_set_master(struct ddekit_pci_dev *dev)
+{
+ l4io_pci_set_master(dev->l4dev.handle);
+}
+
+int ddekit_pci_irq_enable(int bus, int slot, int func, int pin, int *irq)
+{
+ return 0;
+}
diff --git a/libddekit/pgtab-old.c b/libddekit/pgtab-old.c
new file mode 100644
index 00000000..2b13698a
--- /dev/null
+++ b/libddekit/pgtab-old.c
@@ -0,0 +1,219 @@
+/*
+ * \brief Virtual page-table facility
+ * \author Thomas Friebel <tf13@os.inf.tu-dresden.de>
+ * \author Christian Helmuth <ch12@os.inf.tu-dresden.de>
+ * \date 2006-11-01
+ *
+ * Implementation of page tables for saving virt->phys assignments.
+ *
+ * FIXME: This works for 32-bit architectures only! (Mostly because of pgtab.h.)
+ */
+
+#include <l4/dde/ddekit/pgtab.h>
+
+#include <l4/sys/l4int.h>
+#include <l4/sys/consts.h>
+#include <l4/util/macros.h>
+#include <l4/dm_mem/dm_mem.h>
+#include <l4/log/l4log.h>
+
+
+/**********************************
+ ** Page-table utility functions **
+ **********************************/
+
+/* some useful consts */
+enum
+{
+ PDIR_SHIFT = L4_SUPERPAGESHIFT, /* readable alias */
+ PDIR_MASK = L4_SUPERPAGEMASK, /* readable alias */
+ PDIR_ENTRIES = (1 << (L4_MWORD_BITS - PDIR_SHIFT)),
+ PTAB_ENTRIES = (1 << (PDIR_SHIFT - L4_PAGESHIFT)),
+ PTAB_SIZE = (sizeof(void*) * PTAB_ENTRIES)
+};
+
+/**
+ * Page table entry.
+ * bit 00-01: type, 0->largemalloc 1->slab page 2->contigmalloc (PTE_TYPE_*)
+ * bit 02-11: size in number of pages
+ * bit 12-31: physical page address
+ */
+typedef union
+{
+ unsigned compact;
+ struct
+ {
+ unsigned type : 2;
+ unsigned pages : 10;
+ unsigned phys : 20;
+ } components;
+} ddekit_pte;
+
+/**
+ * Calculate offset of address in page directory - page-table number
+ */
+static inline unsigned pt_num(l4_addr_t addr) {
+ return addr >> PDIR_SHIFT; }
+
+
+/**
+ * Calculate offset of address in page table - page number
+ */
+static inline unsigned pg_num(l4_addr_t addr) {
+ return (addr & ~PDIR_MASK) >> L4_PAGESHIFT; }
+
+
+/* page directory */
+static ddekit_pte *page_dir[PDIR_ENTRIES];
+
+
+/**
+ * Get page-table entry
+ */
+static inline ddekit_pte ddekit_get_pte(void *p)
+{
+ l4_addr_t addr = (l4_mword_t)p;
+ ddekit_pte *tab;
+
+ tab = page_dir[pt_num(addr)];
+ if (!tab) return (ddekit_pte) 0U;
+
+ return tab[pg_num(addr)];
+}
+
+
+/**
+ * Set page-table entry
+ */
+static inline void ddekit_set_pte(void *p, ddekit_pte pte)
+{
+ l4_addr_t addr = (l4_addr_t) p;
+ ddekit_pte *tab;
+
+ tab = page_dir[pt_num(addr)];
+ if (!tab) {
+ /* create new page table */
+ tab = (ddekit_pte *)
+ l4dm_mem_allocate_named(PTAB_SIZE, L4DM_CONTIGUOUS|L4DM_PINNED|L4RM_MAP,
+ "ddekit ptab");
+ Assert(tab);
+
+ memset(tab, 0, PTAB_SIZE);
+ page_dir[pt_num(addr)] = tab;
+
+ if (1) {
+ l4_addr_t a = l4_trunc_superpage(addr);
+ LOG("created page table for range [0x%08lx,0x%08lx) @ %p\n",
+ a, a + L4_SUPERPAGESIZE, tab);
+ }
+
+ }
+
+ tab[pg_num(addr)] = pte;
+}
+
+
+/*****************************
+ ** Page-table facility API **
+ *****************************/
+
+/**
+ * Set virtual->physical mapping for VM region
+ *
+ * \param virtual virtual start address for region
+ * \param physical physical start address for region
+ * \param pages number of pages in region
+ * \param type pte type for region
+ */
+void ddekit_pte_set_region(void *virtual, ddekit_addr_t physical, int pages, int type)
+{
+ ddekit_pte new;
+
+#ifdef INVARIANTS
+ ddekit_pte pte;
+ /* assert pte not set yet */
+ pte = ddekit_get_pte(virtual);
+ LOGd(pte.compact, "first pte already set! pte=0x%04x", pte.compact);
+#endif
+
+ /* build pte */
+ new.components.type = type;
+ new.components.pages = pages;
+ new.components.phys = physical >> L4_PAGESHIFT;
+
+ /* set first pte */
+ ddekit_set_pte(virtual, new);
+
+ /* continuation pte-s don't have pages set */
+ new.components.pages = 0;
+
+ /* set continuation pte-s */
+ for (pages--; pages; pages--) {
+ /* prepare continuation pte */
+ virtual += L4_PAGESIZE;
+ new.components.phys++;
+
+#ifdef INVARIANTS
+ /* assert pte not set yet */
+ pte = ddekit_get_pte(virtual);
+ LOGd(pte.compact, "continuation pte already set! pte=0x%04x", pte.compact);
+#endif
+
+ /* set continuation pte */
+ ddekit_set_pte(virtual, new);
+ }
+}
+
+
+/**
+ * Clear virtual->physical mapping for VM region
+ *
+ * \param virtual virtual start address for region
+ * \param type pte type for region
+ *
+ * XXX unused page-tables not free'd
+ */
+void ddekit_pte_clear_region(void *virtual, int type)
+{
+ ddekit_pte pte;
+ int pages = 0;
+ pte = ddekit_get_pte(virtual);
+ /* get number of pages from pte */
+ pages = pte.components.pages;
+ /* assert pages != 0 */
+ if (pages==0) {
+ LOG("continuation pte found at base");
+ return;
+ }
+ /* assert pte set and of correct type */
+#ifdef INVARIANTS
+ LOGd(! pte.compact, "pte is already clear");
+ LOGd(pte.components.type!=type, "pte of wrong type");
+#endif
+ /* clear first pte */
+ ddekit_set_pte(virtual, (ddekit_pte) 0U);
+ /* clear continuation pte-s */
+ for (pages--; pages; pages--) {
+ virtual += L4_PAGESIZE;
+
+#ifdef INVARIANTS
+ /* assert pte set and of correct type */
+ pte = ddekit_get_pte(virtual);
+ if (! pte.compact) {
+ LOG("pte is already clear");
+ break;
+ }
+ if (pte.components.type!=type) {
+ LOG("pte of wrong type");
+ break;
+ }
+ if (pte.components.pages) {
+ LOG("unexpected non-continuation pte found");
+ break;
+ }
+#endif
+
+ ddekit_set_pte(virtual, (ddekit_pte) 0U);
+ }
+}
+
diff --git a/libddekit/pgtab.c b/libddekit/pgtab.c
new file mode 100644
index 00000000..3c39f54f
--- /dev/null
+++ b/libddekit/pgtab.c
@@ -0,0 +1,219 @@
+/*
+ * \brief Virtual page-table facility
+ * \author Thomas Friebel <tf13@os.inf.tu-dresden.de>
+ * \author Christian Helmuth <ch12@os.inf.tu-dresden.de>
+ * \date 2006-11-01
+ *
+ * This implementation uses l4rm (especially the AVL tree and userptr) to
+ * manage virt->phys mappings. Each mapping region is represented by one
+ * pgtab_object that is kept in the l4rm region userptr.
+ *
+ * For this to work, dataspaces must be attached to l4rm regions!
+ */
+
+#include <l4/dde/ddekit/pgtab.h>
+#include <l4/dde/ddekit/memory.h>
+#include <l4/dde/ddekit/panic.h>
+
+#include <l4/l4rm/l4rm.h>
+#include <l4/lock/lock.h>
+#include <l4/util/macros.h>
+
+#include "config.h"
+
+
+/**
+ * "Page-table" object
+ */
+struct pgtab_object
+{
+ l4_addr_t va; /* virtual start address */
+ l4_addr_t pa; /* physical start address */
+
+ /* FIXME reconsider the following members */
+ l4_size_t size;
+ unsigned type; /* pgtab region type */
+
+ struct pgtab_object * next;
+ struct pgtab_object * prev;
+};
+
+/**
+ * pa_list_head of page-table object list (for get_virtaddr())
+ */
+static struct pgtab_object pa_list_head =
+ {
+ .va = 0,
+ .pa = 0,
+ .size = 0,
+ .next = &pa_list_head,
+ .prev = &pa_list_head
+ };
+
+static l4lock_t pa_list_lock = L4LOCK_UNLOCKED;
+
+/*****************************
+ ** Page-table facility API **
+ *****************************/
+
+/**
+ * Get physical address for virtual address
+ *
+ * \param virtual virtual address
+ * \return physical address or 0
+ */
+ddekit_addr_t ddekit_pgtab_get_physaddr(const void *virtual)
+{
+ /* find pgtab object */
+ struct pgtab_object *p = l4rm_get_userptr(virtual);
+ if (!p) {
+ /* XXX this is verbose */
+ LOG_Error("no virt->phys mapping for virtual address %p", virtual);
+ return 0;
+ }
+
+ /* return virt->phys mapping */
+ l4_size_t offset = (l4_addr_t) virtual - p->va;
+
+ return p->pa + offset;
+}
+
+/**
+ * Get virtual address for physical address
+ *
+ * \param physical physical address
+ * \return virtual address or 0
+ */
+ddekit_addr_t ddekit_pgtab_get_virtaddr(const ddekit_addr_t physical)
+{
+ /* find pgtab object */
+ struct pgtab_object *p;
+ ddekit_addr_t retval = 0;
+
+ /* find phys->virt mapping */
+ l4lock_lock(&pa_list_lock);
+ for (p = pa_list_head.next ; p != &pa_list_head ; p = p->next) {
+ if (p->pa <= (l4_addr_t)physical &&
+ (l4_addr_t)physical < p->pa + p->size) {
+ l4_size_t offset = (l4_addr_t) physical - p->pa;
+ retval = p->va + offset;
+ break;
+ }
+ }
+ l4lock_unlock(&pa_list_lock);
+
+ if (!retval)
+ LOG_Error("no phys->virt mapping for physical address %p", (void*)physical);
+
+ return retval;
+}
+
+
+
+int ddekit_pgtab_get_type(const void *virtual)
+{
+ /* find pgtab object */
+ struct pgtab_object *p = l4rm_get_userptr(virtual);
+ if (!p) {
+ /* XXX this is verbose */
+ LOG_Error("no virt->phys mapping for %p", virtual);
+ return -1;
+ }
+
+ return p->type;
+}
+
+
+int ddekit_pgtab_get_size(const void *virtual)
+{
+ /* find pgtab object */
+ struct pgtab_object *p = l4rm_get_userptr(virtual);
+ if (!p) {
+ /* XXX this is verbose */
+ LOG_Error("no virt->phys mapping for %p", virtual);
+ return -1;
+ }
+
+ return p->size;
+}
+
+
+/**
+ * Clear virtual->physical mapping for VM region
+ *
+ * \param virtual virtual start address for region
+ * \param type pgtab type for region
+ */
+void ddekit_pgtab_clear_region(void *virtual, int type)
+{
+ struct pgtab_object *p;
+
+ /* find pgtab object */
+ p = (struct pgtab_object *)l4rm_get_userptr(virtual);
+ if (!p) {
+ /* XXX this is verbose */
+ LOG_Error("no virt->phys mapping for %p", virtual);
+ return;
+ }
+
+ /* reset userptr in region map */
+ /* XXX no error handling here */
+ l4rm_set_userptr(virtual, 0);
+
+ /* remove pgtab object from list */
+ l4lock_lock(&pa_list_lock);
+ p->next->prev= p->prev;
+ p->prev->next= p->next;
+ l4lock_unlock(&pa_list_lock);
+
+ /* free pgtab object */
+ ddekit_simple_free(p);
+}
+
+
+/**
+ * Set virtual->physical mapping for VM region
+ *
+ * \param virtual virtual start address for region
+ * \param physical physical start address for region
+ * \param pages number of pages in region
+ * \param type pgtab type for region
+ */
+void ddekit_pgtab_set_region(void *virtual, ddekit_addr_t physical, int pages, int type)
+{
+ /* allocate pgtab object */
+ struct pgtab_object *p = ddekit_simple_malloc(sizeof(*p));
+ if (!p) {
+ LOG_Error("ddekit heap exhausted");
+ return;
+ }
+
+ /* initialize pgtab object */
+ p->va = l4_trunc_page(virtual);
+ p->pa = l4_trunc_page(physical);
+ p->size = pages * L4_PAGESIZE;
+ p->type = type;
+
+ l4lock_lock(&pa_list_lock);
+ p->next=pa_list_head.next;
+ p->prev=&pa_list_head;
+ pa_list_head.next->prev=p;
+ pa_list_head.next=p;
+ l4lock_unlock(&pa_list_lock);
+
+ /* set userptr in region map to pgtab object */
+ int err = l4rm_set_userptr((void *)p->va, p);
+ if (err) {
+ LOG_Error("l4rm_set_userptr returned %d", err);
+ ddekit_panic("l4rm_set_userptr");
+ ddekit_simple_free(p);
+ }
+}
+
+void ddekit_pgtab_set_region_with_size(void *virt, ddekit_addr_t phys, int size, int type)
+{
+ int p = l4_round_page(size);
+ p >>= L4_PAGESHIFT;
+ ddekit_pgtab_set_region(virt, phys, p, type);
+}
+
diff --git a/libddekit/printf.c b/libddekit/printf.c
new file mode 100644
index 00000000..fb13a0b4
--- /dev/null
+++ b/libddekit/printf.c
@@ -0,0 +1,46 @@
+/*
+ * \brief Logging facility with printf()-like interface
+ * \author Thomas Friebel <yaron@yaron.de>
+ * \date 2006-03-01
+ */
+
+#include <l4/dde/ddekit/printf.h>
+
+#include <l4/log/l4log.h>
+
+/**
+ * Log constant string message w/o arguments
+ *
+ * \param msg message to be logged
+ */
+int ddekit_print(const char *msg)
+{
+ return LOG_printf("%s", msg);
+}
+
+/**
+ * Log message with print()-like arguments
+ *
+ * \param fmt format string followed by optional arguments
+ */
+int ddekit_printf(const char *fmt, ...)
+{
+ int res;
+ va_list va;
+
+ va_start(va, fmt);
+ res = ddekit_vprintf(fmt, va);
+ va_end(va);
+
+ return res;
+}
+
+/* Log message with vprintf()-like arguments
+ *
+ * \param fmt format string
+ * \param va variable argument list
+ */
+int ddekit_vprintf(const char *fmt, va_list va)
+{
+ return LOG_vprintf(fmt, va);
+}
diff --git a/libddekit/resources.c b/libddekit/resources.c
new file mode 100644
index 00000000..d4d421c4
--- /dev/null
+++ b/libddekit/resources.c
@@ -0,0 +1,57 @@
+#include <l4/dde/ddekit/resources.h>
+
+#include <l4/generic_io/libio.h>
+
+#include "config.h"
+
+int ddekit_request_dma(int nr) {
+ return l4io_request_dma(nr);
+}
+
+int ddekit_release_dma(int nr) {
+ return l4io_release_dma(nr);
+}
+
+/** Request an IO region
+ *
+ * \return 0 success
+ * \return -1 error
+ */
+int ddekit_request_io(ddekit_addr_t start, ddekit_addr_t count) {
+ return l4io_request_region(start, count);
+}
+
+/** Release an IO region.
+ *
+ * \return 0 success
+ * \return <0 error
+ */
+int ddekit_release_io(ddekit_addr_t start, ddekit_addr_t count) {
+ return l4io_release_region(start, count);
+}
+
+/** Request a memory region.
+ *
+ * \return vaddr virtual address of memory region
+ * \return 0 success
+ * \return -1 error
+ */
+int ddekit_request_mem(ddekit_addr_t start, ddekit_addr_t count, ddekit_addr_t *vaddr) {
+ ddekit_addr_t v;
+
+ v = l4io_request_mem_region(start, count, 0);
+ if (v) {
+ *vaddr = v;
+ return 0;
+ } else
+ return -1;
+}
+
+/** Release memory region.
+ *
+ * \return 0 success
+ * \return <0 error
+ */
+int ddekit_release_mem(ddekit_addr_t start, ddekit_addr_t count) {
+ return l4io_release_mem_region(start, count);
+}
diff --git a/libddekit/semaphore.c b/libddekit/semaphore.c
new file mode 100644
index 00000000..6fbb7f35
--- /dev/null
+++ b/libddekit/semaphore.c
@@ -0,0 +1,48 @@
+#include <l4/dde/ddekit/semaphore.h>
+#include <l4/dde/ddekit/memory.h>
+
+#include <l4/semaphore/semaphore.h>
+
+struct ddekit_sem {
+ l4semaphore_t sem;
+};
+
+ddekit_sem_t *ddekit_sem_init(int value) {
+ ddekit_sem_t *sem;
+
+ sem = (ddekit_sem_t *) ddekit_simple_malloc(sizeof(*sem));
+ sem->sem = L4SEMAPHORE_INIT(value);
+
+ return sem;
+}
+
+void ddekit_sem_deinit(ddekit_sem_t *sem) {
+ ddekit_simple_free(sem);
+}
+
+void ddekit_sem_down(ddekit_sem_t *sem) {
+#if 0
+ printf("%s:%d sem=%p l4sem=0x%08x\n", __FILE__, __LINE__, sem, sem->sem);
+ enter_kdebug("");
+#endif
+ l4semaphore_down(&sem->sem);
+/*printf("%s:%d\n", __FILE__, __LINE__); */
+}
+
+/* returns 0 on success, != 0 when it would block */
+int ddekit_sem_down_try(ddekit_sem_t *sem) {
+ return l4semaphore_try_down(&sem->sem) ? 0 : 1;
+}
+
+/* returns 0 on success, != 0 on timeout */
+int ddekit_sem_down_timed(ddekit_sem_t *sem, int timo) {
+ return l4semaphore_down_timed(&sem->sem, timo);
+}
+
+void ddekit_sem_up(ddekit_sem_t *sem) {
+#if 0
+ printf("%s:%d sem=%p l4sem=0x%08x\n", __FILE__, __LINE__, sem, sem->sem);
+#endif
+ l4semaphore_up(&sem->sem);
+}
+
diff --git a/libddekit/thread.c b/libddekit/thread.c
new file mode 100644
index 00000000..db767dc9
--- /dev/null
+++ b/libddekit/thread.c
@@ -0,0 +1,195 @@
+#include <l4/dde/ddekit/thread.h>
+#include <l4/dde/ddekit/condvar.h>
+#include <l4/dde/ddekit/panic.h>
+#include <l4/dde/ddekit/memory.h>
+#include <l4/dde/ddekit/printf.h>
+
+#include <l4/dde/dde.h>
+#include <l4/thread/thread.h>
+#include <l4/log/l4log.h>
+#include <l4/sys/syscalls.h>
+#include <l4/util/rdtsc.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#define DDEKIT_THREAD_STACK_SIZE 0x2000 /* 8 KB */
+
+static struct ddekit_slab *ddekit_stack_slab = NULL;
+
+struct ddekit_thread {
+ l4thread_t l4thread;
+ void *data;
+ void *stack;
+ ddekit_condvar_t *sleep_cv;
+ const char *name;
+};
+
+/**
+ * The thread-local-storage key for the BSD struct thread.
+ */
+static int tlskey_thread;
+
+struct startup_args {
+ void (*fun)(void *);
+ void *arg;
+ const char *name;
+};
+
+ddekit_thread_t *ddekit_thread_setup_myself(const char *name) {
+ ddekit_thread_t *td;
+ int namelen = strlen(name);
+ char *pname;
+
+ td = ddekit_simple_malloc(sizeof(*td) + (namelen+1));
+ pname = (char *) td + sizeof(*td);
+
+ td->data=NULL;
+ td->sleep_cv = ddekit_condvar_init();
+ td->l4thread = l4thread_myself();
+ td->name = pname;
+
+ strcpy(pname, name);
+
+ l4thread_data_set_current(tlskey_thread, td);
+
+ return td;
+}
+
+static void ddekit_thread_startup(void *arg) {
+ struct startup_args su;
+ ddekit_thread_t *td;
+
+ /* copy arg to su so that it can bee freed by caller */
+ su = *((struct startup_args*)arg);
+
+ /* init dde thread structure */
+ td = ddekit_thread_setup_myself(su.name);
+ /* inform caller of initialization */
+ l4thread_started(td);
+
+ /* call thread routine */
+ su.fun(su.arg);
+}
+
+ddekit_thread_t *ddekit_thread_create(void (*fun)(void *), void *arg, const char *name) {
+ struct startup_args su;
+ ddekit_thread_t *td;
+ l4thread_t l4td;
+ char l4name[20];
+ void *stack;
+
+ su.fun = fun;
+ su.arg = arg;
+ su.name = name;
+
+ snprintf(l4name, 20, ".%s", name);
+
+ stack = ddekit_slab_alloc(ddekit_stack_slab);
+
+
+ l4td = l4thread_create_long(L4THREAD_INVALID_ID, ddekit_thread_startup, l4name,
+ (l4_addr_t) stack + (DDEKIT_THREAD_STACK_SIZE-1 )* sizeof (void *),
+ DDEKIT_THREAD_STACK_SIZE,
+ L4THREAD_DEFAULT_PRIO, &su, L4THREAD_CREATE_SYNC);
+
+ if (l4td < 0)
+ ddekit_panic("error creating thread");
+
+ td = (ddekit_thread_t*) l4thread_startup_return(l4td);
+
+ td->stack = stack;
+
+ return td;
+}
+
+ddekit_thread_t *ddekit_thread_myself(void) {
+ return (ddekit_thread_t *) l4thread_data_get_current(tlskey_thread);
+}
+
+void ddekit_thread_set_data(ddekit_thread_t *thread, void *data) {
+ thread->data = data;
+}
+
+void ddekit_thread_set_my_data(void *data) {
+ ddekit_thread_set_data(ddekit_thread_myself(), data);
+}
+
+void *ddekit_thread_get_data(ddekit_thread_t *thread) {
+ return thread->data;
+}
+
+void *ddekit_thread_get_my_data() {
+ return ddekit_thread_get_data(ddekit_thread_myself());
+}
+
+void ddekit_thread_msleep(unsigned long msecs) {
+ l4thread_sleep(msecs);
+}
+
+void ddekit_thread_usleep(unsigned long usecs) {
+ l4_busy_wait_us(usecs);
+}
+
+
+void ddekit_thread_nsleep(unsigned long nsecs) {
+ l4_busy_wait_ns(nsecs);
+}
+
+void ddekit_thread_sleep(ddekit_lock_t *lock) {
+ ddekit_thread_t *td;
+
+ td = ddekit_thread_myself();
+
+ ddekit_condvar_wait(td->sleep_cv, lock);
+}
+
+void ddekit_thread_wakeup(ddekit_thread_t *td) {
+ ddekit_condvar_signal(td->sleep_cv);
+}
+
+void ddekit_thread_exit() {
+ ddekit_thread_t *td;
+
+ td = ddekit_thread_myself();
+
+ l4thread_exit();
+
+ ddekit_slab_free(ddekit_stack_slab ,td->stack);
+
+}
+
+void ddekit_thread_terminate(ddekit_thread_t *t)
+{
+ l4thread_shutdown(t->l4thread);
+}
+
+const char *ddekit_thread_get_name(ddekit_thread_t *thread) {
+ return thread->name;
+}
+
+int ddekit_thread_get_id(ddekit_thread_t *t)
+{
+ return t->l4thread;
+}
+
+void ddekit_thread_schedule(void)
+{
+ l4_yield();
+}
+
+void ddekit_yield(void)
+{
+ l4_yield();
+}
+
+void ddekit_init_threads() {
+ /* register TLS key for pointer to dde thread structure */
+ tlskey_thread = l4thread_data_allocate_key();
+
+ /* setup dde part of thread data */
+ ddekit_thread_setup_myself("main");
+
+ /* create slab for stacks */
+ ddekit_stack_slab = ddekit_slab_init(DDEKIT_THREAD_STACK_SIZE, 1);
+}
diff --git a/libddekit/timer.c b/libddekit/timer.c
new file mode 100644
index 00000000..b1af99bf
--- /dev/null
+++ b/libddekit/timer.c
@@ -0,0 +1,329 @@
+#include <l4/dde/ddekit/timer.h>
+#include <l4/dde/ddekit/thread.h>
+#include <l4/dde/ddekit/printf.h>
+#include <l4/dde/ddekit/panic.h>
+#include <l4/dde/ddekit/assert.h>
+#include <l4/dde/ddekit/memory.h>
+#include <l4/dde/ddekit/semaphore.h>
+
+#include <l4/thread/thread.h>
+#include <l4/lock/lock.h>
+#include <l4/env/errno.h>
+#include <l4/generic_io/libio.h>
+#include <l4/sys/ipc.h>
+#include <l4/util/rdtsc.h>
+
+#define __DEBUG 0
+
+/* Just to remind BjoernD of what this is:
+ * HZ = clock ticks per second
+ * jiffies = clock ticks counter.
+ *
+ * So, if someone schedules a timeout to expire in 2 seconds,
+ * this expires date will be in jiffies + 2 * HZ.
+ */
+extern volatile unsigned long jiffies;
+extern unsigned long HZ;
+
+typedef struct _timer
+{
+ struct _timer *next;
+ void (*fn)(void *);
+ void *args;
+ unsigned long expires;
+ int id;
+} ddekit_timer_t;
+
+
+static ddekit_timer_t *timer_list = NULL; ///< list of pending timers
+static l4lock_t timer_lock = L4LOCK_UNLOCKED; ///< lock to access timer_list
+static l4_threadid_t timer_thread = L4_NIL_ID; ///< the timer thread
+static ddekit_thread_t *timer_thread_ddekit = NULL; ///< ddekit ID of timer thread
+static ddekit_sem_t *notify_semaphore = NULL; ///< timer thread's wait semaphore
+
+static int timer_id_ctr = 0;
+
+static void dump_list(char *msg)
+{
+#if __DEBUG
+ ddekit_timer_t *l = timer_list;
+
+ ddekit_printf("-=-=-=-= %s =-=-=-\n", msg);
+ while (l) {
+ ddekit_printf("-> %d (%lld)\n", l->id, l->expires);
+ l = l->next;
+ }
+ ddekit_printf("-> NULL\n");
+ ddekit_printf("-=-=-=-=-=-=-=-\n");
+#endif
+}
+
+
+/** Notify the timer thread there is a new timer at the beginning of the
+ * timer list.
+ */
+static inline void __notify_timer_thread(void)
+{
+ int err;
+ l4_msgdope_t result;
+
+ /* Do not notify if there is no timer thread.
+ * XXX: Perhaps we should better assert that there is a timer
+ * thread before allowing users to add a timer.
+ */
+ if (l4_is_nil_id(timer_thread))
+ return;
+
+ ddekit_sem_up(notify_semaphore);
+}
+
+
+int ddekit_add_timer(void (*fn)(void *), void *args, unsigned long timeout)
+{
+ ddekit_timer_t *it;
+ ddekit_timer_t *t = ddekit_simple_malloc(sizeof(ddekit_timer_t));
+
+ Assert(t);
+
+ /* fill in values */
+ t->fn = fn;
+ t->args = args;
+ t->expires = timeout;
+ t->next = NULL;
+
+ l4lock_lock(&timer_lock);
+
+ t->id = timer_id_ctr++;
+
+ /* the easy case... */
+ if (timer_list == NULL || timer_list->expires >= t->expires) {
+ t->next = timer_list;
+ timer_list = t;
+ }
+ else { /* find where to insert */
+ it = timer_list;
+ while (it->next && it->next->expires < t->expires)
+ it = it->next;
+
+ if (it->next) { /* insert somewhere in the middle */
+ t->next = it->next;
+ it->next = t;
+ }
+ else /* we append */
+ it->next = t;
+ }
+
+ /*
+ * if we modified the first entry of the list, it is
+ * necessary to notify the timer thread.
+ */
+ if (t == timer_list) {
+ Assert(!l4_is_nil_id(timer_thread));
+ __notify_timer_thread();
+ }
+
+ l4lock_unlock(&timer_lock);
+
+ dump_list("after add");
+
+ return t->id;
+}
+
+
+int ddekit_del_timer(int timer)
+{
+ ddekit_timer_t *it, *it_next;
+ int ret = -1;
+
+ l4lock_lock(&timer_lock);
+
+ /* no timer? */
+ if (!timer_list) {
+ ret = -2;
+ goto out;
+ }
+
+ /* removee is first item, simply delete it */
+ if (timer_list->id == timer) {
+ it = timer_list->next;
+ ret = timer_list->id;
+ ddekit_simple_free(timer_list);
+ timer_list = it;
+
+ /*
+ * We do not notify the timer thread here to save IPCs. The
+ * thread will wakeup later and finally detect that there is
+ * no timer pending anymore.
+ */
+ goto out;
+ }
+
+ it = timer_list;
+ it_next = it->next;
+
+ /* more difficult if removee is somewhere in
+ * the middle of the list
+ */
+ while (it_next) {
+ if (it_next->id == timer) {
+ it->next = it->next->next;
+ ret = it_next->id;
+ ddekit_simple_free(it_next);
+ goto out;
+ }
+ it = it->next;
+ it_next = it->next;
+ }
+
+out:
+ l4lock_unlock(&timer_lock);
+
+ dump_list("after del");
+
+ return ret;
+}
+
+
+/** Check whether a timer with a given ID is still pending.
+ *
+ * \param timer Timer ID to check for.
+ * \return 0 if not pending
+ * 1 if timer is pending
+ */
+int ddekit_timer_pending(int timer)
+{
+ ddekit_timer_t *t = NULL;
+ int r = 0;
+
+ l4lock_lock(&timer_lock);
+
+ t = timer_list;
+ while (t) {
+ if (t->id == timer) {
+ r = 1;
+ break;
+ }
+ t = t->next;
+ }
+
+ l4lock_unlock(&timer_lock);
+
+ return r;
+}
+
+
+/** Get the next timer function to run.
+ *
+ * \return NULL if no timer is to be run now
+ * != NULL next timer to execute
+ */
+static ddekit_timer_t *get_next_timer(void)
+{
+ ddekit_timer_t *t = NULL;
+
+ /* This function must be called with the timer_lock held. */
+ Assert(l4_thread_equal(l4thread_l4_id(l4lock_owner(&timer_lock)),
+ timer_thread));
+
+ if (timer_list &&
+ (timer_list->expires <= jiffies)) {
+ t = timer_list;
+ timer_list = timer_list->next;
+ }
+
+ return t;
+}
+
+enum
+{
+ DDEKIT_TIMEOUT_NEVER = 0xFFFFFFFF,
+};
+
+/** Let the timer thread sleep for a while.
+ *
+ * \param to timeout in msec
+ *
+ * \return 1 if IPC timed out
+ * 0 if message received -> recalc timeout
+ */
+static inline int __timer_sleep(unsigned to)
+{
+ l4_umword_t dummy;
+ l4_msgdope_t res;
+
+ int err = 0;
+
+ l4lock_unlock(&timer_lock);
+
+ if (to == DDEKIT_TIMEOUT_NEVER) {
+ ddekit_sem_down(notify_semaphore);
+ }
+ else {
+#if 0
+ ddekit_printf("Going to sleep for %lu µs (%lu ms)\n", to * 1000, to);
+#endif
+ err = ddekit_sem_down_timed(notify_semaphore, to);
+#if 0
+ ddekit_printf("err: %x\n", err);
+#endif
+ }
+
+ l4lock_lock(&timer_lock);
+
+ return (err ? 1 : 0);
+}
+
+
+static void ddekit_timer_thread(void *arg)
+{
+ timer_thread_ddekit = ddekit_thread_setup_myself("ddekit_timer");
+
+ notify_semaphore = ddekit_sem_init(0);
+#if 0
+ l4thread_set_prio(l4thread_myself(), 0x11);
+#endif
+
+ l4thread_started(0);
+
+ l4lock_lock(&timer_lock);
+ while (1) {
+ ddekit_timer_t *timer = NULL;
+ unsigned long to = DDEKIT_TIMEOUT_NEVER;
+
+ if (timer_list) {
+#if 0
+ int jdiff = timer_list->expires - jiffies;
+ ddekit_printf("\033[31mscheduling new timeout.\033[0m\n");
+ ddekit_printf("\033[31mjiffies diff = %ld (%d s)\033[0m\n", jdiff, jdiff/HZ);
+#endif
+ to = (timer_list->expires - jiffies) * 1000000 / HZ;
+ to /= 1000;
+ }
+
+ __timer_sleep(to);
+
+ while ((timer = get_next_timer()) != NULL) {
+ l4lock_unlock(&timer_lock);
+ //ddekit_printf("doing timer fn @ %p\n", timer->fn);
+ timer->fn(timer->args);
+ ddekit_simple_free(timer);
+ l4lock_lock(&timer_lock);
+ }
+ }
+}
+
+ddekit_thread_t *ddekit_get_timer_thread()
+{
+ return timer_thread_ddekit;
+}
+
+
+void ddekit_init_timers(void)
+{
+ l4_tsc_init(L4_TSC_INIT_AUTO);
+
+ /* XXX this module needs HZ and jiffies to work - so l4io info page must be mapped */
+ timer_thread = l4thread_l4_id( l4thread_create_named(ddekit_timer_thread,
+ "ddekit_timer", 0,
+ L4THREAD_CREATE_SYNC));
+}