diff --git a/include/kernel/kthread.h b/include/kernel/kthread.h
index 112074aa675f2ff1295843b9330ff2f53baa5137..2f860ecc11909591d7ce3cca2dd3c0e8e3d3c35c 100644
--- a/include/kernel/kthread.h
+++ b/include/kernel/kthread.h
@@ -11,6 +11,7 @@
 #include <list.h>
 #include <asm-generic/thread.h>
 #include <kernel/time.h>
+#include <kernel/sched.h>
 
 
 
@@ -35,14 +36,9 @@ struct remove_this_declaration {
 #define TASK_DEAD	0x0004
 
 
-enum sched_policy {
-	SCHED_RR,
-	SCHED_EDF,
-	SCHED_FIFO,
-	SCHED_OTHER,
-};
 
-extern volatile int sched_edf;
+
+//extern volatile int sched_edf;
 struct task_struct {
 
 	struct thread_info thread_info;
@@ -68,11 +64,10 @@ struct task_struct {
 	 */
 	unsigned long stack_canary;
 
-	enum sched_policy		policy;
-	unsigned long			priority;
-	ktime				period; /* wakeup period */
-	ktime				wcet; /* max runtime per period*/
-	ktime				deadline_rel; /* time to deadline from begin of wakeup*/
+
+	struct scheduler		*sched;
+
+	struct sched_attr		attr;
 
 	ktime				runtime; /* remaining runtime in this period  */
 	ktime				wakeup; /* start of next period */
diff --git a/include/kernel/sched.h b/include/kernel/sched.h
new file mode 100644
index 0000000000000000000000000000000000000000..fbb3ba55289737dd97c62173f6840d4ca373e030
--- /dev/null
+++ b/include/kernel/sched.h
@@ -0,0 +1,120 @@
+/**
+ * @file include/kernel/sched.h
+ */
+
+
+#ifndef _KERNEL_SCHED_H_
+#define _KERNEL_SCHED_H_
+
+
+
+
+enum sched_policy {
+	SCHED_RR,
+	SCHED_EDF,
+	SCHED_FIFO,
+	SCHED_OTHER,
+};
+
+
+struct sched_attr {
+	enum sched_policy	policy;
+
+	/* static priority scheduling for RR, FIFO, ... */
+	unsigned long		priority;
+
+	/* period based scheduling for EDF, RMS, ... */
+	ktime			period;		/* wakeup period */
+	ktime			wcet;		/* max runtime per period*/
+	ktime			deadline_rel;	/* time to deadline from begin of wakeup */
+};
+
+
+
+
+
+#if 0
+struct rq {
+
+		/* runqueue lock: */
+	raw_spinlock_t		lock;
+
+	struct dl_rq		dl;
+};
+#endif
+
+
+struct task_queue {
+	struct list_head new;
+	struct list_head run;
+	struct list_head wake;
+	struct list_head dead;
+};
+
+
+
+
+
+
+
+#if 1
+struct scheduler {
+
+	struct task_queue	tq;
+
+	const enum sched_policy policy;
+
+	struct task_struct *(*pick_next_task)(struct task_queue *tq);
+
+	/* XXX: sucks */
+	void (*wake_next_task)(struct task_queue *tq);
+	void (*enqueue_task)  (struct task_queue *tq, struct task_struct *task);
+
+	ktime (*timeslice_ns) (struct task_struct *task);
+	ktime (*task_ready_ns) (struct task_queue *tq);
+
+	int (*check_sched_attr) (struct sched_attr *attr);
+
+	unsigned long sched_priority;		/* scheduler priority */
+	struct list_head	node;
+#if 0
+	const struct sched_class *next;
+
+	void (*enqueue_task) (struct rq *rq, struct task_struct *p, int flags);
+	void (*dequeue_task) (struct rq *rq, struct task_struct *p, int flags);
+	void (*yield_task)   (struct rq *rq);
+	bool (*yield_to_task)(struct rq *rq, struct task_struct *p, bool preempt);
+
+	void (*check_preempt_curr)(struct rq *rq, struct task_struct *p, int flags);
+	/*
+	 * It is the responsibility of the pick_next_task() method that will
+	 * return the next task to call put_prev_task() on the @prev task or
+	 * something equivalent.
+	 *
+	 * May return RETRY_TASK when it finds a higher prio class has runnable
+	 * tasks.
+	 */
+	struct task_struct * (*pick_next_task)(struct rq *rq,
+					       struct task_struct *prev,
+					       struct rq_flags *rf);
+#endif
+#if 0
+	void (*put_prev_task)(struct rq *rq, struct task_struct *p);
+#endif
+
+};
+#endif
+
+
+
+
+
+int sched_set_attr(struct task_struct *task, struct sched_attr *attr);
+int sched_get_attr(struct task_struct *task, struct sched_attr *attr);
+
+int sched_set_policy_default(struct task_struct *task);
+int sched_enqueue(struct task_struct *task);
+int sched_register(struct scheduler *sched);
+
+
+#endif /* _KERNEL_SCHED_H_ */
diff --git a/include/kernel/tick.h b/include/kernel/tick.h
index e80edc0494ba4fbb15053b7605ce00d9211526e4..7f1b3d16e9a944bde2607c8c785eadc15f347208 100644
--- a/include/kernel/tick.h
+++ b/include/kernel/tick.h
@@ -36,5 +36,6 @@ void tick_check_device(struct clock_event_device *dev);
 int tick_set_mode(enum tick_mode mode);
 int tick_set_next_ns(unsigned long nanoseconds);
 int tick_set_next_ktime(struct timespec expires);
+unsigned long tick_get_period_min_ns(void);
 
 #endif /* _KERNEL_TICK_H_ */
diff --git a/init/main.c b/init/main.c
index a79bcf74420eef6abde99d213d6d8d598bd33684..1ab9e60dedc259cce0339860281a4277549fe082 100644
--- a/init/main.c
+++ b/init/main.c
@@ -49,8 +49,8 @@
 int task1(void *data)
 {
 	while (1) {
-	//	printk(".");
-	//	sched_yield();
+		printk(".");
+		sched_yield();
 	}
 }
 
@@ -58,11 +58,20 @@ int task1(void *data)
 int task2(void *data)
 {
 	while (1) {
-	//	printk("-");
-	//	sched_yield();
+		printk("-");
+		sched_yield();
 	}
 }
 
+int task3(void *data)
+{
+	while (1) {
+		printk("x");
+		sched_yield();
+	}
+}
+
+
 
 
 extern struct task_struct *kernel;
@@ -150,7 +159,7 @@ int kernel_main(void)
 
 	/* elevate boot thread */
 	kernel = kthread_init_main();
-
+#if 0
 	/*
 	 *  T1: (P=50, D=20, R=10)
 	 *
@@ -177,19 +186,38 @@ int kernel_main(void)
 	t = kthread_create(task1, NULL, KTHREAD_CPU_AFFINITY_NONE, "T7");
 	kthread_set_sched_edf(t, 50 * MSEC_PER_SEC, 6 * MSEC_PER_SEC, 46 * MSEC_PER_SEC);
 
+#endif
 
 
 
 
+#if 1
+{
+	struct sched_attr attr;
 
-#if 0
 	t = kthread_create(task1, NULL, KTHREAD_CPU_AFFINITY_NONE, "print");
-	t->priority = 4;
+	sched_get_attr(t, &attr);
+	attr.priority = 4;
+	sched_set_attr(t, &attr);
 	kthread_wake_up(t);
 
 	t = kthread_create(task2, NULL, KTHREAD_CPU_AFFINITY_NONE, "print1");
-	t->priority = 8;
+	sched_get_attr(t, &attr);
+	attr.priority = 8;
+	sched_set_attr(t, &attr);
+	kthread_wake_up(t);
+
+
+	t = kthread_create(task3, NULL, KTHREAD_CPU_AFFINITY_NONE, "edfprint");
+	sched_get_attr(t, &attr);
+	attr.policy = SCHED_EDF;
+	attr.period       = 9000000;
+	attr.deadline_rel = 1100000;
+	attr.wcet         = 1000000;
+	sched_set_attr(t, &attr);
 	kthread_wake_up(t);
+
+}
 #endif
 
 
diff --git a/kernel/kthread.c b/kernel/kthread.c
index dfa624245b4795a10ceb3a80189978fe49ad0af1..eefbb11e1a75c3d35be76f808f9576a8f23d753f 100644
--- a/kernel/kthread.c
+++ b/kernel/kthread.c
@@ -92,7 +92,7 @@ void sched_yield(void)
 	struct task_struct *tsk;
 
 	tsk = current_set[0]->task;
-	if (tsk->policy == SCHED_EDF)
+	if (tsk->attr.policy == SCHED_EDF)
 		tsk->runtime = 0;
 
 	schedule();
@@ -109,19 +109,19 @@ void sched_wake(struct task_struct *next, ktime now, int64_t slot_ns)
 
 	task = list_entry(_kthreads.wake.next, struct task_struct, node);
 
-	if (task->policy == SCHED_EDF) {
-		if (next->policy == SCHED_EDF)
+	if (task->attr.policy == SCHED_EDF) {
+		if (next->attr.policy == SCHED_EDF)
 			return;
 		/* initially set current time as wakeup */
 		task->wakeup = ktime_add(now, slot_ns);
-		task->deadline = ktime_add(task->wakeup, task->deadline_rel);
+		task->deadline = ktime_add(task->wakeup, task->attr.deadline_rel);
 		task->first_wake = task->wakeup;
 		task->first_dead = task->deadline;
 
 		list_move(&task->node, &_kthreads.run);
 	}
 
-	if (task->policy == SCHED_RR) {
+	if (task->attr.policy == SCHED_RR) {
 		task->state = TASK_RUN;
 		list_move(&task->node, &_kthreads.run);
 	}
@@ -129,139 +129,7 @@ void sched_wake(struct task_struct *next, ktime now, int64_t slot_ns)
 }
 
 
-#define MIN_SLICE	1000000LL	/* 1 ms */
 
-#define OVERHEAD	0LL
-
-/* XXX */
-int64_t schedule_edf(ktime now);
-
-void schedule(void)
-{
-	struct task_struct *next;
-	struct task_struct *current;
-	int64_t slot_ns = MIN_SLICE;
-
-
-
-	ktime now;
-
-
-	if (list_empty(&_kthreads.run))
-		return;
-
-
-
-	/* XXX: dummy; need solution for sched_yield() so timer is
-	 * disabled either via disable_tick or need_resched variable
-	 * (in thread structure maybe?)
-	 * If we don't do this here, the timer may fire while interrupts
-	 * are disabled, which may land us here yet again
-	 */
-
-
-	arch_local_irq_disable();
-
-	kthread_lock();
-
-	now = ktime_add(ktime_get(), OVERHEAD);
-
-	tick_set_next_ns(1e9);
-
-
-
-	current = current_set[0]->task;
-
-
-
-	if (current->policy == SCHED_EDF) {
-		ktime d;
-		d = ktime_delta(now, current->exec_start);
-		BUG_ON(d < 0);
-		current->total = ktime_add(current->total, d);
-		current->slices++;
-		current->runtime = ktime_sub(current->runtime, d);
-		//printk("%lld %lld\n", d, current->runtime);
-	}
-
-
-	/** XXX not here, add cleanup thread */
-	kthread_cleanup_dead();
-
-
-#if 1
-	{
-		static int init;
-		if (!init) { /* dummy switch */
-			tick_set_mode(TICK_MODE_ONESHOT);
-			init = 1;
-		}
-	}
-#endif
-
-
-
-
-
-
-
-
-#if 1
-	slot_ns = schedule_edf(now);
-#endif
-
-
-	/* round robin as before */
-	do {
-
-		next = list_entry(_kthreads.run.next, struct task_struct, node);
-
-		BUG_ON(!next);
-
-		if (next->state == TASK_RUN) {
-			list_move_tail(&next->node, &_kthreads.run);
-			break;
-		}
-		if (next->state == TASK_IDLE) {
-			list_move_tail(&next->node, &_kthreads.run);
-			continue;
-		}
-
-		if (next->state == TASK_DEAD)
-			list_move_tail(&next->node, &_kthreads.dead);
-
-	} while (!list_empty(&_kthreads.run));
-
-
-	if (next->policy == SCHED_EDF) {
-		if (next->runtime <= slot_ns)
-			slot_ns = next->runtime; /* XXX must track actual time because of IRQs */
-	}
-
-	if (next->policy == SCHED_RR)
-		slot_ns = (ktime) next->priority * MIN_SLICE;
-
-
-
-	if (slot_ns > 0xffffffff)
-		slot_ns = 0xffffffff;
-
-	BUG_ON (slot_ns < 18000);
-
-
-	sched_wake(next, now, slot_ns);
-
-	next->exec_start = ktime_get();
-
-	kthread_unlock();
-
-	tick_set_next_ns(slot_ns);
-
-	prepare_arch_switch(1);
-	switch_to(next);
-
-	arch_local_irq_enable();
-}
 
 __attribute__((unused))
 /* static */ void kthread_set_sched_policy(struct task_struct *task,
@@ -269,7 +137,7 @@ __attribute__((unused))
 {
 	arch_local_irq_disable();
 	kthread_lock();
-	task->policy = policy;
+	task->attr.policy = policy;
 	kthread_unlock();
 	arch_local_irq_enable();
 }
@@ -284,8 +152,9 @@ void kthread_wake_up(struct task_struct *task)
 	BUG_ON(task->state != TASK_NEW);
 
 	task->state = TASK_IDLE;
-
-	list_move_tail(&task->node, &_kthreads.wake);
+	/** XXX **/
+	sched_enqueue(task);
+	//list_move_tail(&task->node, &_kthreads.wake);
 
 	kthread_unlock();
 	arch_local_irq_enable();
@@ -303,13 +172,13 @@ struct task_struct *kthread_init_main(void)
 		return ERR_PTR(-ENOMEM);
 
 	/* XXX accessors */
-	task->policy = SCHED_RR; /* default */
-	task->priority = 1;
+	task->attr.policy = SCHED_RR; /* default */
+	task->attr.priority = 1;
 
 	arch_promote_to_task(task);
 
 	task->name = "KERNEL";
-	task->policy = SCHED_RR;
+	BUG_ON(sched_set_policy_default(task));
 
 	arch_local_irq_disable();
 	kthread_lock();
@@ -320,7 +189,9 @@ struct task_struct *kthread_init_main(void)
 
 
 	task->state = TASK_RUN;
-	list_add_tail(&task->node, &_kthreads.run);
+
+	sched_enqueue(task);
+	/*list_add_tail(&task->node, &_kthreads.run);*/
 
 
 
@@ -380,9 +251,10 @@ static struct task_struct *kthread_create_internal(int (*thread_fn)(void *data),
 	BUG_ON(!task->name);
 	vsnprintf(task->name, 32, namefmt, args);
 
-	/* XXX accessors */
-	task->policy = SCHED_RR; /* default */
-	task->priority = 1;
+	if (sched_set_policy_default(task)) {
+		pr_crit("KTHREAD: must destroy task at this point\n");
+		BUG();
+	}
 
 	task->total = 0;
 	task->slices = 0;
@@ -393,7 +265,8 @@ static struct task_struct *kthread_create_internal(int (*thread_fn)(void *data),
 	arch_local_irq_disable();
 	kthread_lock();
 
-	list_add_tail(&task->node, &_kthreads.new);
+	/** XXX **/ /*sched_enqueue(task); */
+	//list_add_tail(&task->node, &_kthreads.new);
 
 	kthread_unlock();
 	arch_local_irq_enable();
diff --git a/kernel/sched/Makefile b/kernel/sched/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..0101ffdb2171727789140c4a81c9ea6a26404de2
--- /dev/null
+++ b/kernel/sched/Makefile
@@ -0,0 +1,3 @@
+obj-y += core.o
+obj-y += rr.o
+obj-y += edf.o
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
new file mode 100644
index 0000000000000000000000000000000000000000..8edbc890a0169386a84a3b2ca84d35527be92f94
--- /dev/null
+++ b/kernel/sched/core.c
@@ -0,0 +1,247 @@
+/**
+ * @file kernel/sched/core.c
+ *
+ * @brief the core scheduling code
+ */
+
+
+
+#include <kernel/err.h>
+#include <kernel/kthread.h>
+#include <kernel/sched.h>
+#include <kernel/init.h>
+#include <kernel/tick.h>
+#include <asm-generic/irqflags.h>
+#include <asm-generic/spinlock.h>
+#include <asm/switch_to.h>
+#include <string.h>
+
+
+
+#define MSG "SCHEDULER: "
+
+static LIST_HEAD(kernel_schedulers);
+
+
+/* XXX: per-cpu */
+extern struct thread_info *current_set[1];
+
+
+
+void schedule(void)
+{
+	struct scheduler *sched;
+
+	struct task_struct *next = NULL;
+
+	struct task_struct *current;
+	int64_t slot_ns;
+	int64_t wake_ns = 0;
+
+
+
+
+	arch_local_irq_disable();
+	/* kthread_lock(); */
+
+
+	/* XXX: for now, try to wake up any threads not running
+	 * this is a waste of cycles and instruction space; should be
+	 * done in the scheduler's code (somewhere) */
+	list_for_each_entry(sched, &kernel_schedulers, node) {
+		sched->wake_next_task(&sched->tq);
+	}
+
+	current = current_set[0]->task;
+
+	/* XXX need sorted list: highest->lowest scheduler priority, e.g.:
+	 * EDF -> RMS -> FIFO -> RR
+	 * TODO: scheduler priority value
+	 */
+	list_for_each_entry(sched, &kernel_schedulers, node) {
+
+		next = sched->pick_next_task(&sched->tq);
+
+		/* check if we need to limit the next tasks timeslice;
+		 * since our scheduler list is sorted by scheduler priority,
+		 * only update the value if wake_next is not set;
+		 * we assume that the timeslice is reasonable; if not fix it in
+		 * the corresponding scheduler
+		 */
+		if (!wake_ns)
+			wake_ns = sched->task_ready_ns(&sched->tq);
+
+		if (next)
+			break;
+	}
+
+
+	if (!next) {
+		/* nothing to do, check again later */
+		if (wake_ns)
+			tick_set_next_ns(wake_ns);
+		else
+			tick_set_next_ns(1e9);	/* XXX pause for a second */
+
+		goto exit;
+	}
+
+	/* see if we can use a full slice or if we have to wake earlier */
+	if (wake_ns)
+		slot_ns = wake_ns;
+	else
+		slot_ns = next->sched->timeslice_ns(next);
+
+	/* statistics */
+	next->exec_start = ktime_get();
+
+	/* kthread_unlock(); */
+
+	tick_set_next_ns(slot_ns);
+
+	prepare_arch_switch(1);
+	switch_to(next);
+
+exit:
+	arch_local_irq_enable();
+}
+
+
+
+
+/**
+ * @brief enqueue a task
+ */
+
+int sched_enqueue(struct task_struct *task)
+{
+	if (!task->sched) {
+		pr_err(MSG "no scheduler configured for task %s\n", task->name);
+		return -EINVAL;
+	}
+
+	/** XXX retval **/
+	if (task->sched->check_sched_attr(&task->attr))
+		return -EINVAL;
+
+	task->sched->enqueue_task(&task->sched->tq, task);
+
+	return 0;
+}
+
+
+/**
+ * @brief set a scheduling attribute for a task
+ *
+ * @returns 0 on success, < 0 on error
+ *
+ * XXX: should implement list of all threads, so we can use pid_t pid
+ *
+ * XXX: no error checking of attr params
+ */
+
+int sched_set_attr(struct task_struct *task, struct sched_attr *attr)
+{
+	struct scheduler *sched;
+
+
+	if (!task)
+		goto error;
+
+	if (!attr)
+		goto error;
+
+
+	list_for_each_entry(sched, &kernel_schedulers, node) {
+
+		if (sched->policy == attr->policy) {
+
+			memcpy(&task->attr, attr, sizeof(struct sched_attr));
+
+			if (sched->check_sched_attr(attr))
+				goto error;
+
+			task->sched  = sched;
+
+			/* XXX other stuff */
+
+			return 0;
+		}
+	}
+
+	pr_crit(MSG "specified policy %d not available\n", attr->policy);
+
+error:
+	task->sched = NULL;
+	return -EINVAL;
+}
+
+
+/**
+ * @brief get a scheduling attribute for a task
+ * XXX: should implement list of all threads, so we can use pid_t pid
+ */
+
+int sched_get_attr(struct task_struct *task, struct sched_attr *attr)
+{
+
+	if (!task)
+		return -EINVAL;
+
+	if (!attr)
+		return -EINVAL;
+
+
+	memcpy(attr, &task->attr, sizeof(struct sched_attr));
+
+
+	return 0;
+}
+
+
+/**
+ * @brief set a task to the default scheduling policy
+ */
+
+int sched_set_policy_default(struct task_struct *task)
+{
+	struct sched_attr attr = {.policy = SCHED_RR,
+				  .priority = 1};
+
+	return sched_set_attr(task, &attr);
+}
+
+
+/**
+ * @brief register a new scheduler
+ */
+
+int sched_register(struct scheduler *sched)
+{
+	/* XXX locks */
+
+
+	/* XXX stupid */
+	if (!sched->sched_priority)
+		list_add_tail(&sched->node, &kernel_schedulers);
+	else
+		list_add(&sched->node, &kernel_schedulers);
+
+	return 0;
+}
+
+
+/**
+ * @brief scheduler initcall
+ *
+ * @note sets tick mode to oneshot
+ */
+
+static int sched_init(void)
+{
+	tick_set_mode(TICK_MODE_ONESHOT);
+	tick_set_next_ns(1e9);	/* XXX default to 1s ticks initially */
+
+	return 0;
+}
+late_initcall(sched_init);
diff --git a/kernel/sched/edf.c b/kernel/sched/edf.c
index 8bc3c15588431d7b0d1c787f6b604806e6593589..ee5de0d1ce1aba4836829ecabf50c8d9423493f9 100644
--- a/kernel/sched/edf.c
+++ b/kernel/sched/edf.c
@@ -20,11 +20,7 @@
 #include <kernel/tick.h>
 
 
-extern struct remove_this_declaration _kthreads;
-void kthread_set_sched_policy(struct task_struct *task,
-			      enum sched_policy policy);
-
-
+#if 0
 
 void sched_print_edf_list_internal(ktime now)
 {
@@ -46,7 +42,7 @@ void sched_print_edf_list_internal(ktime now)
 	printk("----------------------------------------------\n");
 	list_for_each_entry_safe(tsk, tmp, &_kthreads.run, node) {
 
-		if (tsk->policy == SCHED_RR)
+		if (tsk->attr.policy == SCHED_RR)
 			continue;
 
 		rel_deadline = ktime_delta(tsk->deadline, now);
@@ -76,7 +72,7 @@ void sched_print_edf_list(void)
 {
 	printk("avg: %lld\n", ktime_to_us(total/times));
 }
-
+#endif
 /**
  * Our EDF task scheduling timeline:
  *
@@ -125,7 +121,7 @@ static inline bool schedule_edf_can_execute(struct task_struct *tsk, ktime now)
 		printk("%s violated, %lld %lld, %lld %lld\n", tsk->name,
 		       tsk->runtime, ktime_us_delta(tsk->deadline, now),
 		       tsk->deadline, now);
-		sched_print_edf_list_internal(now);
+	//	sched_print_edf_list_internal(now);
 		BUG();
 		return false;
 	}
@@ -137,7 +133,7 @@ static inline bool schedule_edf_can_execute(struct task_struct *tsk, ktime now)
 
 
 
-	if (tsk->wcet * to_deadline < tsk->period * tsk->runtime)
+	if (tsk->attr.wcet * to_deadline < tsk->attr.period * tsk->runtime)
 		return true;
 
 
@@ -152,18 +148,18 @@ static inline void schedule_edf_reinit_task(struct task_struct *tsk, ktime now)
 {
 	tsk->state = TASK_IDLE;
 
-	tsk->wakeup = ktime_add(tsk->wakeup, tsk->period);
+	tsk->wakeup = ktime_add(tsk->wakeup, tsk->attr.period);
 #if 0
 	if (ktime_after(now, tsk->wakeup))
 		printk("%s delta %lld\n",tsk->name, ktime_us_delta(tsk->wakeup, now));
 
 	BUG_ON(ktime_after(now, tsk->wakeup)); /* deadline missed earlier? */
 #endif
-	tsk->deadline = ktime_add(tsk->wakeup, tsk->deadline_rel);
+	tsk->deadline = ktime_add(tsk->wakeup, tsk->attr.deadline_rel);
 
-	tsk->runtime = tsk->wcet;
+	tsk->runtime = tsk->attr.wcet;
 }
-
+#if 0
 
 #define SOME_DEFAULT_TICK_PERIOD_FOR_SCHED_MODE 10000000LL
 /* stupidly sort EDFs */
@@ -183,7 +179,7 @@ static inline void schedule_edf_reinit_task(struct task_struct *tsk, ktime now)
 
 	list_for_each_entry_safe(tsk, tmp, &_kthreads.run, node) {
 
-		if (tsk->policy != SCHED_EDF)
+		if (tsk->attr.policy != SCHED_EDF)
 			continue;
 
 		/* time to wake up yet? */
@@ -226,7 +222,7 @@ static inline void schedule_edf_reinit_task(struct task_struct *tsk, ktime now)
 	wake = ktime_add(now, slot);
 	list_for_each_entry_safe(tsk, tmp, &_kthreads.run, node) {
 
-		if (tsk->policy != SCHED_EDF)
+		if (tsk->attr.policy != SCHED_EDF)
 			break;
 
 		/* all currently runnable task are at the top of the list */
@@ -267,6 +263,7 @@ static inline void schedule_edf_reinit_task(struct task_struct *tsk, ktime now)
 	return slot;
 }
 
+#endif
 
 /**
  *
@@ -511,7 +508,7 @@ static inline void schedule_edf_reinit_task(struct task_struct *tsk, ktime now)
  *
  */
 
-static ktime hyperperiod(void)
+static ktime edf_hyperperiod(struct task_queue *tq)
 {
 	ktime lcm = 0;
 
@@ -522,20 +519,20 @@ static ktime hyperperiod(void)
 	struct task_struct *tmp;
 
 
-	if (list_empty(&_kthreads.new))
+	if (list_empty(&tq->new))
 	    return 0;
 
-	t0 = list_entry(_kthreads.new.next, struct task_struct, node);
+	t0 = list_entry(tq->new.next, struct task_struct, node);
 
-	lcm = t0->period;
+	lcm = t0->attr.period;
 
-	list_for_each_entry_safe(tsk, tmp, &_kthreads.new, node) {
+	list_for_each_entry_safe(tsk, tmp, &tq->new, node) {
 
 		if (tsk == t0)
 			continue;
 
 		a = lcm;
-		b = tsk->period;
+		b = tsk->attr.period;
 
 		/* already a multiple? */
 		if (a % b == 0)
@@ -548,165 +545,386 @@ static ktime hyperperiod(void)
 				b -= a;
 		}
 
-		lcm = lcm * (tsk->period / a);
+		lcm = lcm * (tsk->attr.period / a);
 	}
 
 	return lcm;
 }
 
-#define MIN(A,B)	((A) < (B) ? (A) : (B))
 
-void kthread_set_sched_edf(struct task_struct *task, unsigned long period_us,
-			  unsigned long wcet_us, unsigned long deadline_rel_us)
-{
-	/* XXX schedulability tests */
+/**** NEW EDF ****/
 
-	if (wcet_us >= period_us) {
-		printk("Cannot schedule EDF task with WCET %u >= PERIOD %u !\n", wcet_us, period_us);
-		return;
-	}
+#include <kernel/init.h>
 
-	if (wcet_us >= deadline_rel_us) {
-		printk("Cannot schedule EDF task with WCET %u >= DEADLINE %u !\n", wcet_us, deadline_rel_us);
-		return;
-	}
+#define MSG "SCHED_EDF: "
 
-	if (deadline_rel_us >= period_us) {
-		printk("Cannot schedule EDF task with DEADLINE %u >= PERIOD %u !\n", deadline_rel_us, period_us);
-		return;
-	}
 
+/**
+ * @brief EDF schedulability test
+ *
+ * @returns 0 if schedulable, <0 otherwise
+ */
 
-	arch_local_irq_disable();
-	task->period   = us_to_ktime(period_us);
-	task->wcet     = us_to_ktime(wcet_us);
-	//task->runtime  = ktime_sub(task->wcet, 7000LL);
-	task->runtime  = task->wcet;
-	task->deadline_rel = us_to_ktime(deadline_rel_us);
-	arch_local_irq_enable();
+static int edf_schedulable(struct task_queue *tq, const struct task_struct *task)
+{
 
-	printk("\nvvvv EDF analysis vvvv\n\n");
-	{
-		ktime p = hyperperiod();
-		ktime h ;
-		ktime max = 0;
 
-		ktime uh, ut, f1;
+	ktime p = edf_hyperperiod(tq);
+	ktime h ;
+	ktime max = 0;
 
-		struct task_struct *t0 = NULL;
-		struct task_struct *tsk = NULL;
-		struct task_struct *tmp;
+	ktime uh, ut, f1;
 
-		if (p < 0)
-			printk("appears to be empty\n");
+	struct task_struct *t0 = NULL;
+	struct task_struct *tsk = NULL;
+	struct task_struct *tmp;
 
-		list_for_each_entry_safe(tsk, tmp, &_kthreads.new, node) {
+	double u = 0.0;	/* utilisation */
 
-			if (tsk->period > max) {
-				t0 = tsk;
-				max = tsk->period;
-			}
-		}
 
-		BUG_ON(!t0);
+	static int64_t dmin = 0x7fffffffffffffLL;
 
-		BUG_ON(p < t0->period);
-		h = p / t0->period;
+	printk("\nvvvv EDF analysis vvvv (%lld) \n\n", p);
 
-		printk("Period factor %lld, duration %lld actual min period: %lld\n", h, ktime_to_ms(p), ktime_to_ms(t0->period));
+	/* list_empty(....) */
 
+	if (p <= 0)
+		printk("appears to be empty\n");
 
+	list_for_each_entry_safe(tsk, tmp, &tq->new, node) {
+		printk("%s\n", tsk->name);
+		if (tsk->attr.period > max) {
+			t0 = tsk;
+			max = tsk->attr.period;
+		}
+	}
 
-		uh = h * (t0->deadline_rel - t0->wcet);
-		ut = h * (t0->period - t0->deadline_rel);
-		f1 = ut/h;
+	BUG_ON(!t0);
 
+	BUG_ON(p < t0->attr.period);
+	h = p / t0->attr.period;
 
-		printk("max UH: %lld, UT: %lld\n", ktime_to_ms(uh), ktime_to_ms(ut));
+	printk("Period factor %lld, duration %lld actual min period: %lld\n", h, ktime_to_ms(p), ktime_to_ms(t0->attr.period));
 
 
-		list_for_each_entry_safe(tsk, tmp, &_kthreads.new, node) {
-			ktime sh, st;
 
-			if (tsk == t0)
-				continue;
+	uh = h * (t0->attr.deadline_rel - t0->attr.wcet);
+	ut = h * (t0->attr.period - t0->attr.deadline_rel);
+	f1 = ut/h;
 
 
-			if (tsk->deadline_rel < t0->deadline_rel) {
+	printk("max UH: %lld, UT: %lld\n", ktime_to_ms(uh), ktime_to_ms(ut));
 
-				/* slots before deadline of  T0 */
-				sh = h * tsk->wcet * t0->deadline_rel / tsk->period;
 
-				if (sh > uh) {
-					printk("NOT SCHEDULABLE in head: %s\n", tsk->name);
-					BUG();
-				}
-				uh = uh - sh;
+	list_for_each_entry_safe(tsk, tmp, &tq->new, node) {
+		ktime sh, st;
 
-			}
+		if (tsk == t0)
+			continue;
 
-			/* slots after deadline of T0 */
-			st = h * tsk->wcet * f1 / tsk->period;
-			printk("%s tail usage: %lld\n", tsk->name, ktime_to_ms(st));
-			if (st > ut) {
-				printk("NOT SCHEDULABLE in tail: %s\n", tsk->name);
-				BUG();
-			}
-			ut = ut - st;
 
+		if (tsk->attr.deadline_rel < t0->attr.deadline_rel) {
 
-			printk("UH: %lld, UT: %lld\n", ktime_to_ms(uh), ktime_to_ms(ut));
+			/* slots before deadline of  T0 */
+			sh = h * tsk->attr.wcet * t0->attr.deadline_rel / tsk->attr.period;
 
+			if (sh > uh) {
+				printk("NOT SCHEDULABLE in head: %s\n", tsk->name);
+				BUG();
+			}
+			uh = uh - sh;
 
 		}
 
-
-	}
+		/* slots after deadline of T0 */
+		st = h * tsk->attr.wcet * f1 / tsk->attr.period;
+		printk("%s tail usage: %lld\n", tsk->name, ktime_to_ms(st));
+		if (st > ut) {
+			printk("NOT SCHEDULABLE in tail: %s\n", tsk->name);
+			BUG();
+		}
+		ut = ut - st;
 
 
+		printk("UH: %lld, UT: %lld\n", ktime_to_ms(uh), ktime_to_ms(ut));
 
 
-	{
-		double u = 0.0;	/* utilisation */
 
-		struct task_struct *tsk;
-		struct task_struct *tmp;
 
-		static int64_t dmin = 0x7fffffffffffffLL;
 
 
-		if (dmin > task->deadline_rel)
-			dmin = task->deadline_rel;
+		if (dmin > task->attr.deadline_rel)
+			dmin = task->attr.deadline_rel;
 
 
-		u += (double) (int32_t) task->wcet / (double) (int32_t) task->period;
+		u += (double) (int32_t) task->attr.wcet / (double) (int32_t) task->attr.period;
 
 
-		list_for_each_entry_safe(tsk, tmp, &_kthreads.new, node) {
+		list_for_each_entry_safe(tsk, tmp, &tq->new, node) {
 
-			if (tsk->policy != SCHED_EDF)
+			if (tsk->attr.policy != SCHED_EDF)
 				continue;
 
-			u += (double) (int32_t) tsk->wcet / (double) (int32_t) tsk->period;
+			u += (double) (int32_t) tsk->attr.wcet / (double) (int32_t) tsk->attr.period;
 		}
 
 		if (u > 1.0) {
 			printk("I am NOT schedul-ableh: %g ", u);
-			kthread_set_sched_policy(task, SCHED_RR);
+			return -EINVAL;
 			printk("changed task mode to RR\n", u);
 		} else {
 			printk("Utilisation %g\n", u);
-			kthread_set_sched_policy(task, SCHED_EDF);
+			return 0;
+		}
+	}
+
+
+	u = (double) (int32_t) task->attr.wcet / (double) (int32_t) task->attr.period;
+
+	printk("was the first task %g\n", u);
+
+	return 0;
+}
+
+/** XXX **/
+static int64_t slot;
+
+
+
+
+static struct task_struct *edf_pick_next(struct task_queue *tq)
+{
+#define SOME_DEFAULT_TICK_PERIOD_FOR_SCHED_MODE 10000000LL
+	int64_t delta;
+
+
+	struct task_struct *go = NULL;
+	struct task_struct *tsk;
+	struct task_struct *tmp;
+	ktime now = ktime_get();
+slot = SOME_DEFAULT_TICK_PERIOD_FOR_SCHED_MODE;
+
+
+	list_for_each_entry_safe(tsk, tmp, &tq->run, node) {
+
+		/* time to wake up yet? */
+		delta = ktime_delta(tsk->wakeup, now);
+		if (delta >= 0) {
+			/* nope, just update minimum runtime for this slot */
+			if (delta < slot)
+				slot = delta;
+
+			continue;
 		}
+
+		/* if it's already running, see if there is time remaining */
+		if (tsk->state == TASK_RUN) {
+			if (!schedule_edf_can_execute(tsk, now)) {
+				schedule_edf_reinit_task(tsk, now);
+				/* nope, update minimum runtime for this slot */
+				delta = ktime_delta(tsk->wakeup, now);
+				if (delta < slot)
+					slot = delta;
+				continue;
+			}
+
+			/* move to top */
+			list_move(&tsk->node, &tq->run);
+			continue;
+		}
+
+		/* time to wake up */
+		if (tsk->state == TASK_IDLE) {
+			tsk->state = TASK_RUN;
+			/* move to top */
+			list_move(&tsk->node, &tq->run);
+			go = tsk;
+		}
+
+	}
+
+	/** XXX **/
+	//tsk = list_entry(tq->run.next, struct task_struct, node);
+
+	if (tsk->state == TASK_RUN)
+		return tsk;
+	else
+		return go;
+}
+
+
+static void edf_wake_next(struct task_queue *tq)
+{
+
+	struct task_struct *task;
+
+	if (list_empty(&tq->wake))
+		return;
+
+	task = list_entry(tq->wake.next, struct task_struct, node);
+
+//		if (next->attr.policy == SCHED_EDF)
+//			return;
+		/* initially set current time as wakeup */
+		task->wakeup = ktime_add(ktime_get(), task->attr.period);
+		task->deadline = ktime_add(task->wakeup, task->attr.deadline_rel);
+		task->first_wake = task->wakeup;
+		task->first_dead = task->deadline;
+
+		list_move(&task->node, &tq->run);
+}
+
+
+
+
+static void edf_enqueue(struct task_queue *tq, struct task_struct *task)
+{
+	/* reset runtime to full */
+	task->runtime = task->attr.wcet;
+
+	/* XXX */
+	list_add_tail(&task->node, &tq->new);
+
+
+	if (task->sched->check_sched_attr(&task->attr))
+		return;
+
+	if (edf_schedulable(tq, task)) {
+		printk("---- NOT SCHEDUL-ABLE---\n");
+		return;
 	}
 
-	printk("\n^^^^ EDF analysis ^^^^\n");
 
+	/** XXX **/
+	if (task->state == TASK_RUN)
+		list_add_tail(&task->node, &tq->run);
+	else
+		list_add_tail(&task->node, &tq->wake);
+
+}
+
+
+static ktime edf_timeslice_ns(struct task_struct *task)
+{
+	return 0;
+}
+
+static int edf_check_sched_attr(struct sched_attr *attr)
+{
+	if (!attr)
+		goto error;
+
+	if (attr->policy != SCHED_EDF) {
+		pr_err(MSG "attribute policy is %d, expected SCHED_EDF (%d)\n",
+			attr->policy, SCHED_EDF);
+		return -EINVAL;
+	}
+
+	/* need only check WCET, all other times are longer */
+	if (attr->wcet < (ktime) tick_get_period_min_ns()) {
+		pr_err(MSG "Cannot schedule EDF task with WCET of %lld ns, "
+		           "minimum tick duration is %ld\n", attr->wcet,
+			   tick_get_period_min_ns());
+		goto error;
+	}
+
+	if (attr->wcet >= attr->period) {
+		pr_err(MSG "Cannot schedule EDF task with WCET %u >= "
+		           "PERIOD %u!\n", attr->wcet, attr->period);
+		goto error;
+	}
+
+	if (attr->wcet >= attr->deadline_rel) {
+		pr_err(MSG "Cannot schedule EDF task with WCET %u >= "
+		           "DEADLINE %u !\n", attr->wcet, attr->deadline_rel);
+		goto error;
+	}
+
+	if (attr->deadline_rel >= attr->period) {
+		pr_err(MSG "Cannot schedule EDF task with DEADLINE %u >= "
+		           "PERIOD %u !\n", attr->deadline_rel, attr->period);
+		goto error;
+	}
+
+
+	return 0;
+error:
+
+	return -EINVAL;
+}
+
+
+ktime edf_task_ready_ns(struct task_queue *tq)
+{
+	/* now find the closest relative deadline */
+	int64_t delta;
+
+
+	struct task_struct *tsk;
+	struct task_struct *tmp;
+	ktime now = ktime_get();
+	ktime wake;
 
-//	arch_local_irq_enable();
+	wake = ktime_add(now, slot);
+	list_for_each_entry_safe(tsk, tmp, &tq->run, node) {
 
+		/* all currently runnable task are at the top of the list */
+		if (tsk->state != TASK_RUN)
+			break;
+
+		if (ktime_before(wake, tsk->deadline))
+			continue;
+
+		delta = ktime_delta(wake, tsk->deadline);
+
+		if (delta < 0) {
+			delta = ktime_delta(now, tsk->deadline);
+			printk("\n [%lld] %s deadline violated by %lld us\n", ktime_to_ms(now), tsk->name, ktime_to_us(delta));
+		}
 
-	printk("\n");
 
+		if (delta < slot) {
+			if (delta)
+				slot = delta;
+			else
+				delta = tsk->runtime;
+			wake = ktime_add(now, slot); /* update next wakeup */
+			/* move to top */
+			list_move(&tsk->node, &tq->run);
+			BUG_ON(slot <= 0);
+		}
+	}
+
+	slot = 1000000;
+	BUG_ON(slot < 0);
+
+	return slot;
 }
 
+
+static struct scheduler sched_edf = {
+	.policy           = SCHED_EDF,
+	.pick_next_task   = edf_pick_next,
+	.wake_next_task   = edf_wake_next,
+	.enqueue_task     = edf_enqueue,
+	.timeslice_ns     = edf_timeslice_ns,
+	.task_ready_ns    = edf_task_ready_ns,
+	.check_sched_attr = edf_check_sched_attr,
+	.sched_priority   = 1,
+};
+
+
+
+static int sched_edf_init(void)
+{
+	/* XXX */
+	INIT_LIST_HEAD(&sched_edf.tq.new);
+	INIT_LIST_HEAD(&sched_edf.tq.run);
+	INIT_LIST_HEAD(&sched_edf.tq.wake);
+	INIT_LIST_HEAD(&sched_edf.tq.dead);
+
+	sched_register(&sched_edf);
+
+	return 0;
+}
+postcore_initcall(sched_edf_init);
diff --git a/kernel/sched/rr.c b/kernel/sched/rr.c
new file mode 100644
index 0000000000000000000000000000000000000000..232e19c5afe5faf6c1491d482bc3697efe01308e
--- /dev/null
+++ b/kernel/sched/rr.c
@@ -0,0 +1,160 @@
+/**
+ * @file kernel/sched/round_robin.c
+ *
+ * @brief round-robin scheduler
+ */
+
+
+#include <errno.h>
+#include <kernel/init.h>
+#include <kernel/tick.h>
+#include <kernel/kthread.h>
+
+#define MSG "SCHED_RR: "
+
+
+static struct task_struct *rr_pick_next(struct task_queue *tq)
+{
+	struct task_struct *next = NULL;
+
+
+	while (!list_empty(&tq->run)) {
+
+		next = list_entry(tq->run.next, struct task_struct, node);
+
+		BUG_ON(!next);
+
+		if (next->state == TASK_RUN) {
+			/* XXX: must pick head first, then move tail on put()
+			 * following a scheduling event. for now, just force
+			 * round robin
+			 */
+			list_move_tail(&next->node, &tq->run);
+			break;
+		}
+
+		if (next->state == TASK_IDLE)
+			list_move_tail(&next->node, &tq->run);
+
+		if (next->state == TASK_DEAD)
+			list_move_tail(&next->node, &tq->dead);
+
+	}
+
+
+	return next;
+}
+
+
+/* this sucks, wrong place. keep for now */
+static void rr_wake_next(struct task_queue *tq)
+{
+
+	struct task_struct *task;
+
+	if (list_empty(&tq->wake))
+		return;
+
+
+	task = list_entry(tq->wake.next, struct task_struct, node);
+
+	BUG_ON(task->attr.policy != SCHED_RR);
+	/** XXX NO LOCKS */
+	task->state = TASK_RUN;
+	list_move(&task->node, &tq->run);
+}
+
+
+static void rr_enqueue(struct task_queue *tq, struct task_struct *task)
+{
+
+	/** XXX **/
+	if (task->state == TASK_RUN)
+		list_add_tail(&task->node, &tq->run);
+	else
+		list_add_tail(&task->node, &tq->wake);
+}
+
+/**
+ * @brief get the requested timeslice of a task
+ *
+ * @note RR timeslices are determined from their configured priority
+ *	 XXX: must not be 0
+ *
+ * @note for now, just take the minimum tick period to fit as many RR slices
+ *	 as possible. This will jack up the IRQ rate, but RR tasks only run when
+ *	 the system is not otherwise busy;
+ *	 still, a larger (configurable) extra factor may be desirable
+ */
+
+static ktime rr_timeslice_ns(struct task_struct *task)
+{
+	return (ktime) task->attr.priority * tick_get_period_min_ns();
+}
+
+
+
+/**
+ * @brief sanity-check sched_attr configuration
+ *
+ * @return 0 on success -EINVAL on error
+ */
+
+static int rr_check_sched_attr(struct sched_attr *attr)
+{
+	if (attr->policy != SCHED_RR) {
+		pr_err(MSG "attribute policy is %d, expected SCHED_RR (%d)\n",
+			attr->policy, SCHED_RR);
+		return -EINVAL;
+	}
+
+	if (!attr->priority) {
+		pr_warn(MSG "minimum priority is 1, adjusted\n");
+	}
+
+	return 0;
+}
+
+
+
+/**
+ * @brief return the time until the the next task is ready
+ *
+ * @note RR tasks are always "ready" and they do not have deadlines,
+ *	 so this function always returns 0
+ */
+
+ktime rr_task_ready_ns(struct task_queue *tq)
+{
+	return (ktime) 0ULL;
+}
+
+
+
+
+static struct scheduler sched_rr = {
+	.policy           = SCHED_RR,
+	.pick_next_task   = rr_pick_next,
+	.wake_next_task   = rr_wake_next,
+	.enqueue_task     = rr_enqueue,
+	.timeslice_ns     = rr_timeslice_ns,
+	.task_ready_ns    = rr_task_ready_ns,
+	.check_sched_attr = rr_check_sched_attr,
+	.sched_priority   = 0,
+};
+
+
+
+static int sched_rr_init(void)
+{
+	/* XXX */
+	INIT_LIST_HEAD(&sched_rr.tq.new);
+	INIT_LIST_HEAD(&sched_rr.tq.run);
+	INIT_LIST_HEAD(&sched_rr.tq.wake);
+	INIT_LIST_HEAD(&sched_rr.tq.dead);
+
+	sched_register(&sched_rr);
+
+	return 0;
+}
+postcore_initcall(sched_rr_init);
diff --git a/kernel/tick.c b/kernel/tick.c
index cd0ba943e1a957d4ca02eaed1831b55a63260c66..5269e26892b0261606eeb0f71b71a31756367667 100644
--- a/kernel/tick.c
+++ b/kernel/tick.c
@@ -19,6 +19,9 @@
 
 #define MSG "TICK: "
 
+/* the minimum effective tick period; default to 1 ms */
+static unsigned long tick_period_min_ns = 1000000UL;
+
 static struct clock_event_device *tick_device;
 
 
@@ -100,6 +103,32 @@ static int tick_set_mode_oneshot(struct clock_event_device *dev)
 	return 0;
 }
 
+/**
+ * @brief calibrate the minimum processable tick length for this device
+ *
+ * XXX:
+ * what this will do:
+ *  - disable scheduling
+ *  - mask all interrupts except timer (maybe)
+ *  - in tick_event_handler, record time between calls
+ *  - keep decreasing tick length until time between calls does not decrement
+ *    (i.e. interrupt response limit has been hit)
+ *  - NOTE: check clockevents_timout_in_range() or somesuch to clamp to
+ *	    actual timer range (maybe add function to clockevents to
+ *	    return actual timer minimum
+ *  - multiply tick length by some factor (2...10)
+ *  - ???
+ *  - profit!
+ */
+
+static void tick_calibrate_min(struct clock_event_device *dev)
+{
+#define RANDOM_TICK_RATE_NS	18000UL
+	tick_period_min_ns = RANDOM_TICK_RATE_NS;
+#define MIN_SLICE		1000000UL
+	tick_period_min_ns = MIN_SLICE;
+}
+
 
 /**
  * @brief configure the tick device
@@ -107,13 +136,14 @@ static int tick_set_mode_oneshot(struct clock_event_device *dev)
 
 static void tick_setup_device(struct clock_event_device *dev)
 {
-#define RANDOM_TICK_RATE_NS	18000
-	clockevents_set_handler(dev, tick_event_handler);
+	tick_calibrate_min(dev);
 
-	/* FIXME: assume blindly for the moment */
+	/* FIXME: assume blindly for the moment, should apply mode
+	 * of previous clock device (if replaced) */
 	tick_set_mode_periodic(dev);
 
-	clockevents_program_timeout_ns(dev, RANDOM_TICK_RATE_NS);
+	clockevents_set_handler(dev, tick_event_handler);
+	clockevents_program_timeout_ns(dev, tick_period_min_ns);
 }
 
 
@@ -141,6 +171,9 @@ void tick_check_device(struct clock_event_device *dev)
 	tick_set_device(dev, 0);
 
 	tick_setup_device(dev);
+
+	/* XXX should inform scheduler to recalculate any deadline-related
+	 * timeouts of tasks */
 }
 
 
@@ -171,6 +204,20 @@ int tick_set_mode(enum tick_mode mode)
 }
 
 
+/**
+ * @brief get the (calibrated) minimum effective tick period
+ *
+ * @note This represents a safe-to-use value of the best-effort interrupt
+ *	 processing time of the system.
+ */
+
+unsigned long tick_get_period_min_ns(void)
+{
+	return tick_period_min_ns;
+}
+
+
+
 /**
  * @brief configure next tick period in nanoseconds
  *