commit 03d41e2e229a4efb1587956b27a9675c13f34ff5
parent e6669b6ab2d027b1f21a416737d26d5788764888
Author: oblique <psyberbits@gmail.com>
Date:   Fri, 26 Oct 2012 21:00:39 +0300
added: timers, scheduler, kernel threads
changes:
* improve exception handling
* fix some bugs in the allocator
* fix GIC controller
* add support for Dual-Mode Timers
* add scheduler
* add kernel threads
* change coding style of function declaration
Diffstat:
30 files changed, 1095 insertions(+), 123 deletions(-)
diff --git a/include/alloc.h b/include/alloc.h
@@ -5,6 +5,6 @@
 
 void *kmalloc(size_t size);
 void kfree(void *addr);
-void kdump();
+void kdump(void);
 
 #endif	/* __ALLOC_H */
diff --git a/include/dmtimer.h b/include/dmtimer.h
@@ -0,0 +1,11 @@
+#ifndef __DMTIMER_H
+#define __DMTIMER_H
+
+#include <kernel.h>
+
+typedef void (*dmtimer_callback_func)(int timer_id, struct regs *regs);
+
+int dmtimer_register(int id, dmtimer_callback_func func, u32 ms);
+int dmtimer_trigger(int id);
+
+#endif	/* __DMTIMER_H */
diff --git a/include/errno.h b/include/errno.h
@@ -36,7 +36,9 @@
 #define	EDOM		33	/* Math argument out of domain of func */
 #define	ERANGE		34	/* Math result not representable */
 
-#define	MAX_ERRNO	34
+#define	ENOSYS		35	/* Function not implemented */
+
+#define	MAX_ERRNO	35
 #define	IS_ERR(x)	((unsigned long)(x) >= (unsigned long)-MAX_ERRNO)
 #define	PTR_ERR(x)	((unsigned long)(x))
 #define	ERR_PTR(x)	((void*)(x))
diff --git a/include/gic.h b/include/gic.h
@@ -0,0 +1,11 @@
+#ifndef __GIC_H
+#define __GIC_H
+
+#include <kernel.h>
+#include <irq.h>
+
+int gic_register(u32 irq_num, irq_callback_func func);
+void gic_handler(struct regs *regs);
+void gic_init(void);
+
+#endif	/* __GIC_H */
diff --git a/include/irq.h b/include/irq.h
@@ -0,0 +1,14 @@
+#ifndef __IRQ_H
+#define __IRQ_H
+
+#include <kernel.h>
+
+typedef void (*irq_callback_func)(u32 irq_num, struct regs *regs);
+
+int irq_register(u32 irq_num, irq_callback_func func);
+
+/* board specific */
+#define HW_IRQ(x)	(x + 32)
+#define NUM_OF_IRQ	160
+
+#endif	/* __IRQ_H */
diff --git a/include/kernel.h b/include/kernel.h
@@ -3,10 +3,16 @@
 
 #include <inttypes.h>
 #include <stddef.h>
+#include <string.h>
 #include <print.h>
 #include <io.h>
 #include <errno.h>
 #include <varg.h>
+#include <regs.h>
+#include <alloc.h>
+#include <debug.h>
+
+#define __unused	__attribute__((__unused__))
 
 #define container_of(ptr, type, member) ({		\
 	const typeof(((type*)0)->member) *__mptr = (ptr);	\
diff --git a/include/list.h b/include/list.h
@@ -12,6 +12,7 @@ struct list_head {
 #define LIST_HEAD(name)	struct list_head name = LIST_HEAD_INIT(name)
 
 #define list_entry(ptr, type, member) container_of(ptr, type, member)
+#define list_first_entry(head_ptr, type, member) container_of((head_ptr)->next, type, member)
 
 /* list_for_each - iterate over a list
  * pos: the &struct list_head to use as a loop cursor.
@@ -30,7 +31,9 @@ struct list_head {
 	for (pos = (head)->next, n = pos->next; pos != (head);	\
 	     pos = n, n = pos->next)
 
-static inline void INIT_LIST_HEAD(struct list_head *list) {
+static inline void
+INIT_LIST_HEAD(struct list_head *list)
+{
 	list->prev = list;
 	list->next = list;
 }
@@ -38,30 +41,54 @@ static inline void INIT_LIST_HEAD(struct list_head *list) {
 /* Note: list_empty() on entry does not return true after this, the entry is
  * in an undefined state.
  */
-static inline void list_del(struct list_head *entry) {
+static inline void
+list_del(struct list_head *entry)
+{
 	entry->prev->next = entry->next;
 	entry->next->prev = entry->prev;
 	entry->next = NULL;
 	entry->prev = NULL;
 }
 
-static inline void list_add(struct list_head *entry, struct list_head *head) {
+static inline void
+list_add(struct list_head *entry, struct list_head *head)
+{
 	head->next->prev = entry;
 	entry->next = head->next;
 	entry->prev = head;
 	head->next = entry;
 }
 
-static inline void list_add_tail(struct list_head *entry, struct list_head *head) {
+static inline void
+list_add_tail(struct list_head *entry, struct list_head *head)
+{
 	head->prev->next = entry;
 	entry->next = head;
 	entry->prev = head->prev;
 	head->prev = entry;
 }
 
-static inline int list_empty(struct list_head *head) {
+static inline int
+list_empty(const struct list_head *head)
+{
 	return head->next == head;
 }
 
+/* list_is_singular - tests whether a list has just one entry. */
+static inline int
+list_is_singular(const struct list_head *head)
+{
+	return !list_empty(head) && (head->next == head->prev);
+}
+
+/* list_is_last - tests whether @list is the last entry in list @head
+ * list: the entry to test
+ * head: the head of the list
+ */
+static inline int
+list_is_last(const struct list_head *list, const struct list_head *head)
+{
+        return list->next == head;
+}
 
 #endif	/* __LIST_H */
diff --git a/include/mmu.h b/include/mmu.h
@@ -41,5 +41,7 @@ void mmu_enable();
 void mmu_disable();
 int mmu_map_page(void *phys, void *virt, uint_t npages, mmu_ap_t perms);
 int kmmap(void *virt, uint_t npages, mmu_ap_t perms);
+uintptr_t virt_to_phys(void *virt);
+int virt_is_mapped(void *virt);
 
 #endif /* __MMU_H */
diff --git a/include/regs.h b/include/regs.h
@@ -0,0 +1,56 @@
+#ifndef __REGS_H
+#define __REGS_H
+
+struct regs {
+	u32 cpsr;
+	union {
+		u32 r[16];
+		struct {
+			u32 r0;
+			u32 r1;
+			u32 r2;
+			u32 r3;
+			u32 r4;
+			u32 r5;
+			u32 r6;
+			u32 r7;
+			u32 r8;
+			u32 r9;
+			u32 r10;
+			u32 r11;
+			u32 r12;
+			u32 r13;
+			u32 r14;
+			u32 r15;
+		};
+		struct {
+			u32 a1; /* r0 */
+			u32 a2; /* r1 */
+			u32 a3; /* r2 */
+			u32 a4; /* r3 */
+			u32 v1; /* r4 */
+			u32 v2; /* r5 */
+			u32 v3; /* r6 */
+			u32 v4; /* r7 */
+			u32 v5; /* r8 */
+			union { /* r9 */
+				u32 v6;
+				u32 sb;
+			};
+			union {	/* r10  */
+				u32 v7;
+				u32 sl;
+			};
+			union {	/* r11  */
+				u32 v8;
+				u32 fp;
+			};
+			u32 ip; /* r12 */
+			u32 sp; /* r13 */
+			u32 lr; /* r14 */
+			u32 pc; /* r15 */
+		};
+	};
+};
+
+#endif	/* __REGS_H */
diff --git a/include/sched.h b/include/sched.h
@@ -0,0 +1,42 @@
+#ifndef __SCHED_H
+#define __SCHED_H
+
+#include <kernel.h>
+#include <spinlock.h>
+#include <list.h>
+
+typedef u32 pid_t;
+
+
+typedef enum {
+	TASK_TERMINATE,
+	TASK_RUNNABLE,
+	TASK_RUNNING,
+	TASK_SLEEP
+} task_state_t;
+
+
+struct task_struct {
+	pid_t pid;
+	task_state_t state;
+	struct regs regs;
+	struct list_head list;
+	spinlock_t *lock;
+	void *stack_alloc;
+};
+
+
+extern struct task_struct *curr_task;
+
+static inline struct task_struct **
+current_task(void)
+{
+	return &curr_task;
+}
+
+#define current (*current_task())
+
+int kthread_create(void (*routine)(void *), void *arg);
+void schedule(void);
+
+#endif	/* __SCHED_H */
diff --git a/include/spinlock.h b/include/spinlock.h
@@ -5,7 +5,9 @@
 
 typedef unsigned int spinlock_t;
 
-static inline void spinlock_lock(spinlock_t *sl) {
+static inline void
+spinlock_lock(spinlock_t *sl)
+{
 	asm volatile (
 		"1:			\n\t"
 		"ldrex v1, [%0]		\n\t"
@@ -19,7 +21,9 @@ static inline void spinlock_lock(spinlock_t *sl) {
 	);
 }
 
-static inline void spinlock_unlock(spinlock_t *sl) {
+static inline void
+spinlock_unlock(spinlock_t *sl)
+{
 	asm volatile (
 		"str %1, [%0]"
 		:
@@ -29,7 +33,9 @@ static inline void spinlock_unlock(spinlock_t *sl) {
 }
 
 /* returns 1 if locked and 0 if not */
-static inline int spinlock_trylock(spinlock_t *sl) {
+static inline int
+spinlock_trylock(spinlock_t *sl)
+{
 	unsigned int tmp;
 
 	asm volatile (
@@ -47,7 +53,9 @@ static inline int spinlock_trylock(spinlock_t *sl) {
 		return 0;
 }
 
-static inline void INIT_SPINLOCK(spinlock_t *sl) {
+static inline void
+INIT_SPINLOCK(spinlock_t *sl)
+{
 	*sl = 0;
 }
 
diff --git a/include/string.h b/include/string.h
@@ -1,47 +1,10 @@
 #ifndef __STRING_H
 #define __STRING_H
 
-static inline void *memset(void *s, int c, size_t n) {
-	unsigned char *us = s;
+#include <inttypes.h>
 
-	while (n) {
-		*us = c;
-		us++;
-		n--;
-	}
-
-	return s;
-}
-
-static inline void *memcpy(void *s1, const void *s2, size_t n) {
-	unsigned char *us1 = s1;
-	const unsigned char *us2 = s2;
-
-	while (n) {
-		*us1 = *us2;
-		us1++;
-		us2++;
-		n--;
-	}
-
-	return s1;
-}
-
-static inline int memcmp(const void *s1, const void *s2, size_t n) {
-	const unsigned char *us1, *us2;
-
-	us1 = s1;
-	us2 = s2;
-
-	while (n) {
-		if (*us1 != *us2)
-			return (*us1 - *us2 < 0) ? -1 : 1;
-		us1++;
-		us2++;
-		n--;
-	}
-
-	return 0;
-}
+void *memset(void *s, int c, size_t n);
+void *memcpy(void *s1, const void *s2, size_t n);
+int memcmp(const void *s1, const void *s2, size_t n);
 
 #endif	/* __STRING_H */
diff --git a/include/uart.h b/include/uart.h
@@ -5,12 +5,16 @@
 
 /* TRM p.4459 */
 #define TX_FIFO_E	(1<<5)
+#define RX_FIFO_E	(1<<0)
+#define RHR_IT		(1<<0)
+#define IT_PENDING	(1<<0)
+#define IT_TYPE_RHR	(2<<1)
 
 /* TRM p.4444 */
 struct uart {
 	union {			/* 0x00 */
 		u32 thr;
-		const u32 rhr;
+		u32 rhr;
 		u32 dll;
 	};
 	union {			/* 0x04 */
@@ -18,7 +22,7 @@ struct uart {
 		u32 dlh;
 	};
 	union {			/* 0x08 */
-		const u32 iir;
+		u32 iir;
 		u32 fcr;
 		u32 efr;
 	};
@@ -28,12 +32,12 @@ struct uart {
 		u32 xon1_addr1;
 	};
 	union {			/* 0x14 */
-		const u32 lsr;
+		u32 lsr;
 		u32 xon2_addr2;
 	};
 	union {			/* 0x18 */
 		u32 tcr;
-		const u32 msr;
+		u32 msr;
 		u32 xoff1;
 	};
 	union {			/* 0x1c */
@@ -44,37 +48,37 @@ struct uart {
 	u32 mdr1;		/* 0x20 */
 	u32 mdr2;		/* 0x24 */
 	union {			/* 0x28 */
-		const u32 sflsr;
+		u32 sflsr;
 		u32 txfll;
 	};
 	union {			/* 0x2c */
-		const u32 resume;
+		u32 resume;
 		u32 txflh;
 	};
 	union {			/* 0x30 */
-		const u32 sfregl;
+		u32 sfregl;
 		u32 rxfll;
 	};
 	union {			/* 0x34 */
-		const u32 sfregh;
+		u32 sfregh;
 		u32 rxflh;
 	};
 	union {			/* 0x38 */
 		u32 blr;
-		const u32 uasr;
+		u32 uasr;
 	};
 	u32 acreg;		/* 0x3c */
 	u32 scr;		/* 0x40 */
-	const u32 ssr;		/* 0x44 */
+	u32 ssr;		/* 0x44 */
 	u32 eblr;		/* 0x48 */
 	u32 __pad;		/* 0x4c */
-	const u32 mvr;		/* 0x50 */
+	u32 mvr;		/* 0x50 */
 	u32 sysc;		/* 0x54 */
-	const u32 syss;		/* 0x58 */
+	u32 syss;		/* 0x58 */
 	u32 wer;		/* 0x5c */
 	u32 cfps;		/* 0x60 */
-	const u32 rxfifo_lvl;	/* 0x64 */
-	const u32 txfifo_lvl;	/* 0x68 */
+	u32 rxfifo_lvl;		/* 0x64 */
+	u32 txfifo_lvl;		/* 0x68 */
 	u32 ier2;		/* 0x6c */
 	u32 isr2;		/* 0x70 */
 	u32 freq_sel;		/* 0x74 */
diff --git a/kernel/abort.c b/kernel/abort.c
@@ -0,0 +1,19 @@
+#include <kernel.h>
+
+void
+abort_handler(struct regs *regs, int data)
+{
+	int i;
+
+	if (data)
+		kprintf("\n-DATA ABORT-\n");
+	else
+		kprintf("\n-PREFETCH ABORT-\n");
+
+	for (i = 0; i <= 12; i++)
+		kprintf("r%d: %p\n", i, regs->r[i]);
+	kprintf("sp: %p\n", regs->sp);
+	kprintf("lr: %p\n", regs->lr);
+	kprintf("pc: %p\n", regs->pc - 8);
+	kprintf("cpsr: %p\n", regs->cpsr);
+}
diff --git a/kernel/alloc.c b/kernel/alloc.c
@@ -24,7 +24,9 @@ static void *heap_last;
 extern void *_kernel_heap_start;
 extern void *_kernel_heap_end;
 
-static size_t roundup(size_t size) {
+static size_t
+roundup(size_t size)
+{
 	size_t ret;
 
 	if (size <= 16)
@@ -42,7 +44,9 @@ static size_t roundup(size_t size) {
 		return ret >> 1;
 }
 
-void *kmalloc(size_t size) {
+void *
+kmalloc(size_t size)
+{
 	int ret;
 	uint_t npages;
 	uintptr_t heap_last_a, tmp_addr;
@@ -100,7 +104,9 @@ void *kmalloc(size_t size) {
 	return memc->start;
 }
 
-void kfree(void *addr) {
+void
+kfree(void *addr)
+{
 	struct mem_chunk *memc, *tmp;
 	struct list_head *pos;
 
@@ -121,25 +127,29 @@ void kfree(void *addr) {
 }
 
 
-void kdump() {
+void
+kdump(void)
+{
 	struct list_head *pos, *tmp;
 	struct mem_chunk *memc;
 
 	kprintf("alloc list\n");
 	list_for_each_safe(pos, tmp, &alloclist) {
 		memc = list_entry(pos, struct mem_chunk, list);
-		kprintf("%p %p %d\n", memc, memc->start, memc->size);
+		kprintf("%p (phys: %p): %p %p %d\n", pos, virt_to_phys(pos), memc, memc->start, memc->size);
 	}
 
 	kprintf("\nfree list\n");
 	list_for_each_safe(pos, tmp, &freelist) {
 		memc = list_entry(pos, struct mem_chunk, list);
-		kprintf("%p %p %d\n", memc, memc->start, memc->size);
+		kprintf("%p (phys: %p): %p %p %d\n", pos, virt_to_phys(pos), memc, memc->start, memc->size);
 	}
 }
 
 __attribute__ ((__constructor__))
-static void alloc_init() {
+static void
+alloc_init(void)
+{
 	INIT_LIST_HEAD(&freelist);
 	INIT_LIST_HEAD(&alloclist);
 	heap_last = &_kernel_heap_start;
diff --git a/kernel/debug.c b/kernel/debug.c
@@ -4,7 +4,9 @@
 static u32 *gpio_wk7 = (u32*)0x4A31E058;
 static u32 *gpio_wk8 = (u32*)0x4A31E05C;
 
-void set_led_d1(int on) {
+void
+set_led_d1(int on)
+{
 	u32 cur = readl(gpio_wk7) & 0xffff;
 	if (on)
 		cur |= 0x001B0000;
@@ -13,7 +15,9 @@ void set_led_d1(int on) {
 	writel(cur, gpio_wk7);
 }
 
-void set_led_d2(int on) {
+void
+set_led_d2(int on)
+{
 	u32 cur = readl(gpio_wk8) & 0xffff0000;
 	if (on)
 		cur |= 0x001B;
@@ -22,7 +26,37 @@ void set_led_d2(int on) {
 	writel(cur, gpio_wk8);
 }
 
-void set_leds(int on) {
+void
+set_leds(int on)
+{
 	set_led_d1(on);
 	set_led_d2(on);
 }
+
+static void
+_kstackdump(void *sp)
+{
+	u32 *stack = sp;
+	int i;
+
+	for (i = 10; i >= 0; i--) {
+		kprintf("%p: %p\n", &stack[i], stack[i]);
+	}
+}
+
+__attribute__((__naked__))
+void
+kstackdump(void)
+{
+	asm volatile (
+		"mov r0, sp		\n\t"
+		"str lr, [sp, #-4]!	\n\t"
+		"ldr lr, =1f		\n\t"
+		"mov pc, %0		\n\t"
+		"1:			\n\t"
+		"ldr pc, [sp], #4	\n\t"
+		:
+		: "r" (_kstackdump)
+		: "r0"
+	);
+}
diff --git a/kernel/dmtimer.c b/kernel/dmtimer.c
@@ -0,0 +1,252 @@
+#include <kernel.h>
+#include <irq.h>
+#include <dmtimer.h>
+
+#define DMT_OVF_ENA_FLAG		(1<<1)
+#define DMT_TCLR_ST			(1<<0)
+#define DMT_TCLR_AR			(1<<1)
+
+#define DMT_1MS_TIOCP_SOFTRESET	(1<<1)
+#define DMT_1MS_TIOCP_SMARTIDLE	(2<<3)
+#define DMT_1MS_TISTAT_RESETDONE	(1<<0)
+
+/* gptimer1, gptimer2, gptimer10 */
+struct dm_gpt_1ms {
+	u32 tidr;
+	u32 _pad1[3];
+	u32 tiocp_1ms_cfg;
+	u32 tistat;
+	u32 tisr;
+	u32 tier;
+	u32 twer;
+	u32 tclr;
+	u32 tcrr;
+	u32 tldr;
+	u32 ttgr;
+	u32 twps;
+	u32 tmar;
+	u32 tcar1;
+	u32 tsicr;
+	u32 tcar2;
+	u32 tpir;
+	u32 tnir;
+	u32 tcvr;
+	u32 tocr;
+	u32 towr;
+};
+
+
+#define DMT_TIOCP_SOFTRESET	(1<<0)
+#define DMT_TIOCP_SMARTIDLE	(2<<2)
+
+/* gptimer3, gptimer4, gptimer5, gptimer6
+ * gptimer7, gptimer8, gptimer9, gptimer11
+ */
+struct dm_gpt {
+	u32 tidr;
+	u32 _pad1[3];
+	u32 tiocp_cfg;
+	u32 _pad2[4];
+	u32 irqstatus_raw;
+	u32 irqstatus;
+	u32 irqenable_set;
+	u32 irqenable_clr;
+	u32 irqwakeen;
+	u32 tclr;
+	u32 tcrr;
+	u32 tldr;
+	u32 ttgr;
+	u32 twps;
+	u32 tmar;
+	u32 tcar1;
+	u32 tsicr;
+	u32 tcar2;
+};
+
+
+#define DMT_1MS	(1<<0)
+#define CM2_CLKSEL	(1<<24)
+
+struct dmtimer {
+	u32 flags;
+	dmtimer_callback_func callback_func;
+	union {
+		u32 *mem;
+		struct dm_gpt_1ms *dmt_1ms;
+		struct dm_gpt *dmt;
+	};
+	/* L4PER CM2 register */
+	u32 *cm2r;
+} dmtimers[] = {
+	{ DMT_1MS, NULL, { .mem = (u32*)0x4a318000 }, NULL },		  /* GPTIMER1 */
+	{ DMT_1MS, NULL, { .mem = (u32*)0x48032000 }, (u32*)0x4a009438 }, /* GPTIMER2 */
+	{	0, NULL, { .mem = (u32*)0x48034000 }, (u32*)0x4a009440 }, /* GPTIMER3 */
+	{ 	0, NULL, { .mem = (u32*)0x48036000 }, (u32*)0x4a009448 }, /* GPTIMER4 */
+
+	/* GPTIMER 5 - 8 are not implemented yet */
+	{ 	0, NULL, { .mem = (u32*)NULL }, NULL }, /* GPTIMER5 */
+	{ 	0, NULL, { .mem = (u32*)NULL }, NULL }, /* GPTIMER6 */
+	{ 	0, NULL, { .mem = (u32*)NULL }, NULL }, /* GPTIMER7 */
+	{ 	0, NULL, { .mem = (u32*)NULL }, NULL }, /* GPTIMER8 */
+
+	{ 	0, NULL, { .mem = (u32*)0x4803e000 }, (u32*)0x4a009450 }, /* GPTIMER9 */
+	{ DMT_1MS, NULL, { .mem = (u32*)0x48086000 }, (u32*)0x4a009428 }, /* GPTIMER10 */
+	{ 	0, NULL, { .mem = (u32*)0x48088000 }, (u32*)0x4a009430 }  /* GPTIMER11 */
+};
+
+
+
+static void
+dmtimer_irq_callback(u32 irq_num, struct regs *regs)
+{
+	u32 val;
+	int id;
+
+	if (irq_num < HW_IRQ(37) || irq_num > HW_IRQ(47))
+		return;
+
+	id = irq_num - HW_IRQ(37);
+
+	if (dmtimers[id].mem == NULL)
+		return;
+
+	/* read type of event */
+	if (dmtimers[id].flags & DMT_1MS)
+		val = readl(&dmtimers[id].dmt_1ms->tisr);
+	else
+		val = readl(&dmtimers[id].dmt->irqstatus);
+
+	if (dmtimers[id].callback_func)
+		dmtimers[id].callback_func(id + 1, regs);
+
+	/* clear event by writing 1 to the bits */
+	if (dmtimers[id].flags & DMT_1MS)
+		writel(val, &dmtimers[id].dmt_1ms->tisr);
+	else
+		writel(val, &dmtimers[id].dmt->irqstatus);
+}
+
+static int
+reset_timer(int id)
+{
+	u32 val;
+
+	if (id <= 0 || id > 11)
+		return -EINVAL;
+
+	id--;
+
+	/* not implemented yet */
+	if (dmtimers[id].mem == NULL)
+		return -ENOSYS;
+
+	if (dmtimers[id].flags & DMT_1MS) {
+		/* request reset */
+		writel(DMT_1MS_TIOCP_SOFTRESET, &dmtimers[id].dmt_1ms->tiocp_1ms_cfg);
+		/* wait until reset is done */
+		while (!(readl(&dmtimers[id].dmt_1ms->tistat) & DMT_1MS_TISTAT_RESETDONE))
+			;
+	} else {
+		/* request reset */
+		writel(DMT_TIOCP_SOFTRESET, &dmtimers[id].dmt->tiocp_cfg);
+		/* wait until reset is done */
+		while (readl(&dmtimers[id].dmt->tiocp_cfg) & DMT_TIOCP_SOFTRESET)
+			;
+	}
+
+	/* enable 32KHz clock */
+	if (dmtimers[id].cm2r) {
+		val = readl(dmtimers[id].cm2r);
+		val |= CM2_CLKSEL;
+		writel(val, dmtimers[id].cm2r);
+	}
+
+	return 0;
+}
+
+int
+dmtimer_trigger(int id)
+{
+	if (id <= 0 || id > 11)
+		return -EINVAL;
+
+	id--;
+
+	/* not implemented yet */
+	if (dmtimers[id].mem == NULL)
+		return -ENOSYS;
+
+	/* manually overflow the timer */
+	if (dmtimers[id].flags & DMT_1MS)
+		writel(0xffffffff, &dmtimers[id].dmt_1ms->tcrr);
+	else
+		writel(0xffffffff, &dmtimers[id].dmt->tcrr);
+
+	return 0;
+}
+
+int
+dmtimer_register(int id, dmtimer_callback_func func, u32 ms)
+{
+	int ret;
+	u32 val;
+
+	if (id <= 0 || id > 11 || ms == 0 || func == NULL)
+		return -EINVAL;
+
+	/* not implemented yet */
+	if (dmtimers[id - 1].mem == NULL)
+		return -ENOSYS;
+
+	ret = reset_timer(id);
+	if (ret)
+		return ret;
+
+	id--;
+	ret = irq_register(HW_IRQ(37) + id, dmtimer_irq_callback);
+	if (ret)
+		return ret;
+	dmtimers[id].callback_func = func;
+
+	if (dmtimers[id].flags & DMT_1MS) {
+		/* enable start-idle */
+		writel(DMT_1MS_TIOCP_SMARTIDLE, &dmtimers[id].dmt_1ms->tiocp_1ms_cfg);
+
+		/* set microseconds */
+		val = 0xffffffff;
+		val -= ms * 32;
+		writel(val, &dmtimers[id].dmt_1ms->tldr);
+
+		/* writing to TTGR causes TCRR to be loaded from TLDR */
+		writel(1, &dmtimers[id].dmt_1ms->ttgr);
+		/* enable overflow interrupt */
+		writel(DMT_OVF_ENA_FLAG, &dmtimers[id].dmt_1ms->tier);
+		/* start timer and enable autoreload */
+		writel(DMT_TCLR_ST | DMT_TCLR_AR, &dmtimers[id].dmt_1ms->tclr);
+	} else {
+		/* enable start-idle */
+		writel(DMT_TIOCP_SMARTIDLE, &dmtimers[id].dmt->tiocp_cfg);
+
+		/* set microseconds */
+		val = 0xffffffff;
+		val -= ms * 32;
+		writel(val, &dmtimers[id].dmt->tldr);
+
+		/* writing to TTGR causes TCRR to be loaded from TLDR */
+		writel(1, &dmtimers[id].dmt->ttgr);
+		/* enable overflow interrupt */
+		writel(DMT_OVF_ENA_FLAG, &dmtimers[id].dmt->irqenable_set);
+		/* start timer and enable autoreload */
+		writel(DMT_TCLR_ST | DMT_TCLR_AR, &dmtimers[id].dmt->tclr);
+	}
+
+	return 0;
+}
+
+void
+dmtimer_init(void)
+{
+	int i;
+	for (i = 1; i <= 11; i++)
+		reset_timer(i);
+}
diff --git a/kernel/gic.c b/kernel/gic.c
@@ -0,0 +1,110 @@
+#include <kernel.h>
+#include <irq.h>
+#include <gic.h>
+
+#define IAR_ID(x)	(x & 0x3ff)
+
+/* GICv1 CPU interface registers */
+struct gicc {
+	u32 ctlr;
+	u32 pmr;
+	u32 bpr;
+	u32 iar;
+	u32 eoir;
+	u32 rpr;
+	u32 hppir;
+	u32 abpr;
+	u32 _pad[55];
+	u32 iidr;
+};
+
+#define MAX_IT_LINES_NUMBER 32
+
+/* GICv1 Distributor registers */
+struct gicd {
+	u32 ctlr;
+	u32 typer;
+	u32 iidr;
+	u32 _pad1[29];
+	u32 igrour[MAX_IT_LINES_NUMBER];
+	u32 isenabler[MAX_IT_LINES_NUMBER];
+	u32 icenabler[MAX_IT_LINES_NUMBER];
+	u32 ispendr[MAX_IT_LINES_NUMBER];
+	u32 icpendr[MAX_IT_LINES_NUMBER];
+	u32 isactiver[MAX_IT_LINES_NUMBER];
+	u32 _pad2[32];
+	u32 ipriorityr[8 * MAX_IT_LINES_NUMBER];
+	u32 itargetsr[8 * MAX_IT_LINES_NUMBER];
+	u32 icfgr[2 * MAX_IT_LINES_NUMBER];
+	u32 _pad3[128];
+	u32 sgir;
+};
+
+static struct gicc *gicc = (struct gicc*)0x48240100;
+static struct gicd *gicd = (struct gicd*)0x48241000;
+static irq_callback_func irq_handlers[NUM_OF_IRQ];
+
+
+int
+gic_register(u32 irq_num, irq_callback_func func)
+{
+	int i;
+	u32 val;
+
+	if (irq_num >= NUM_OF_IRQ)
+		return -EINVAL;
+
+	/* set callback function */
+	irq_handlers[irq_num] = func;
+
+	/* enable irq number */
+	i = irq_num / 32;
+	val = readl(&gicd->isenabler[i]);
+	val |= 1 << (irq_num % 32);
+	writel(val, &gicd->isenabler[i]);
+
+	/* send the interrupt to CPU0 */
+	i = irq_num / 4;
+	val = readl(&gicd->itargetsr[i]);
+	val &= ~(0xff << (8 * (irq_num % 4)));
+	val |= 1 << (8 * (irq_num % 4));
+	writel(val, &gicd->itargetsr[i]);
+
+	return 0;
+}
+
+void
+gic_handler(struct regs *regs)
+{
+	u32 iar, id;
+
+	/* read interrupt id */
+	iar = readl(&gicc->iar);
+	id = IAR_ID(iar);
+
+	/* call handler */
+	if (id < NUM_OF_IRQ && irq_handlers[id] != NULL)
+		irq_handlers[id](id, regs);
+
+	/* end of interrupt */
+	writel(iar, &gicc->eoir);
+}
+
+void
+gic_init(void)
+{
+	/* disable gic cpu interface */
+	writel(0, &gicc->ctlr);
+	/* disable gic distributor */
+	writel(0, &gicd->ctlr);
+
+	/* make sure that software interrupts are enabled */
+	writel(0xffff, &gicd->isenabler[0]);
+
+	/* set lower priority level */
+	writel(0xf0, &gicc->pmr);
+	/* enable gic cpu interface */
+	writel(1, &gicc->ctlr);
+	/* enable gic cpu distributor */
+	writel(1, &gicd->ctlr);
+}
diff --git a/kernel/interrupts.S b/kernel/interrupts.S
@@ -1,3 +1,5 @@
+#include <p_modes.h>
+
 .section .text
 .global vector_table_init
 vector_table_init:
@@ -24,6 +26,48 @@ vector_table:
 .ltorg
 
 
+@@ save all the registers
+.macro SAVE_ALL_EX
+	stmfd sp!, { lr }     @ save lr (pc (r15) of the previous mode)
+	sub sp, sp, #8	      @ make space for sp (r13) and lr (r14) registers
+	stmfd sp!, { r0 - r12 } @ save r0 until r12
+	mrs r0, spsr	      @ save spsr (cpsr of the previus mode)
+	str r0, [sp, #-4]!    @ save cpsr
+	bic r0, r0, #0x60     @ clear F, T flags
+	orr r0, r0, #(1 << 7) @ set I flag
+	mrs r2, cpsr	      @ backup current cpsr
+	msr cpsr_c, r0	      @ switch to previous mode
+	mov r0, sp
+	mov r1, lr
+	msr cpsr, r2	      @ swith back to the current mode
+	str r0, [sp, #(4 * 14)] @ save sp (r13)
+	str r1, [sp, #(4 * 15)] @ save lr (r14)
+
+	ldr r0, [sp, #4]      @ restore destroyed registers
+	ldr r1, [sp, #8]
+	ldr r2, [sp, #12]
+.endm
+
+@@ restore registers
+.macro RESTORE_ALL_EX
+	ldr r1, [sp, #(4 * 14)] @ load sp (r13)
+	ldr r2, [sp, #(4 * 15)] @ load lr (r14)
+	ldr r0, [sp]
+	bic r0, r0, #0x60
+	orr r0, r0, #(1 << 7) @ get the previous mode number
+	mrs r3, cpsr
+	msr cpsr_c, r0	      @ switch to previous mode
+	mov sp, r1
+	mov lr, r2
+	msr cpsr, r3	      @ swith back to the current mode
+	ldr r0, [sp], #4      @ load spsr (cpsr of the previus mode)
+	msr spsr, r0
+	ldmfd sp!, { r0 - r12 }
+	add sp, sp, #8	      @ release the space we had for sp (r13) and lr (r14)
+	ldr lr, [sp], #4      @ load lr (pc (r15) of the previous mode)
+.endm
+
+
 reset:
 	b .
 
@@ -47,11 +91,10 @@ swi_ex:
 	bic r6, r6, #0xff000000
 
 	stmfd sp!, { r4, r5 }
-	ldr lr, =.Lsyscall_ret
+	ldr lr, =1f
 	ldr r7, =syscall_table
 	ldr pc, [r7, r6, lsl #2]
-
-.Lsyscall_ret:
+1:
 	add sp, sp, #8
 	ldr r6, [sp], #4
 	msr spsr, r6
@@ -59,19 +102,45 @@ swi_ex:
 
 
 prefetch_abort:
-	subs pc, lr, #4
+	@@ save registers
+	SAVE_ALL_EX
+	@@ set led d1 on
+	mov r0, #1
+	bl set_led_d1
+	@@ call handler
+	mov r0, sp		@ pass a pointer to the registers
+	mov r1, #0		@ 0 = prefetch
+	bl abort_handler	@ call handler
+
+	b .		      @ freeze
+	@subs pc, lr, #4
 
 
 data_abort:
-	subs pc, lr, #8
+	@@ save registers
+	SAVE_ALL_EX
+	@@ set led d2 on
+	mov r0, #1
+	bl set_led_d2
+	@@ call handler
+	mov r0, sp		@ pass a pointer to the registers
+	mov r1, #1		@ 1 = data
+	bl abort_handler	@ call handler
+
+	b .		      @ freeze
+	@subs pc, lr, #8
 
 
 irq_ex:
-	sub lr, lr, #4
-	str lr, [sp, #-4]!
-	mov r0, #1
-	bl set_led_d1
-	ldmfd sp!, { pc }^
+	@@ save registers
+	SAVE_ALL_EX
+	@@ call handler
+	mov r0, sp	      @ pass a pointer to the registers
+	bl irq_handler	      @ call irq_handler
+	@@ restore registers
+	RESTORE_ALL_EX
+	@@ exit exception
+	subs pc, lr, #4
 
 
 fiq_ex:
diff --git a/kernel/irq.c b/kernel/irq.c
@@ -0,0 +1,21 @@
+#include <kernel.h>
+#include <gic.h>
+#include <irq.h>
+
+int
+irq_register(u32 irq_num, irq_callback_func func)
+{
+	return gic_register(irq_num, func);
+}
+
+void
+irq_handler(struct regs *regs)
+{
+	gic_handler(regs);
+}
+
+void
+irq_init(void)
+{
+	gic_init();
+}
diff --git a/kernel/kmain.c b/kernel/kmain.c
@@ -1,10 +1,21 @@
 #include <kernel.h>
 #include <alloc.h>
 #include <string.h>
+#include <gic.h>
+#include <sched.h>
 
-void kmain(void) {
-	kprintf("voron initial stage\n\n");
-
+static void
+thread_func(void *arg)
+{
+	u32 n = (u32)arg;
 	while (1)
-		asm volatile("wfi");
+		kprintf("thread %d\n", n);
+}
+
+void
+kmain(void)
+{
+	kprintf("voron initial stage\n\n");
+	kthread_create(thread_func, (void*)1);
+	kthread_create(thread_func, (void*)2);
 }
diff --git a/kernel/linker.ld b/kernel/linker.ld
@@ -49,6 +49,9 @@ SECTIONS
 	_fiq_stack_top = .;
 	. = . + 0x2000;
 	_irq_stack_top = .;
+	. = . + 0x2000;
+	_abort_stack_top = .;
+
 
 	_kernel_bin_end = .;
 }
diff --git a/kernel/mm.c b/kernel/mm.c
@@ -18,7 +18,9 @@ static uint_t kheapbound_s;
 static uint_t kheapbound_e;
 
 /* set range of bits */
-static int set_bitmap_bits(uint_t start_bit, uint_t n, int flag) {
+static int
+set_bitmap_bits(uint_t start_bit, uint_t n, int flag)
+{
 	uint_t bound_s, bound_e, i;
 	u32 bits;
 
@@ -62,7 +64,10 @@ static int set_bitmap_bits(uint_t start_bit, uint_t n, int flag) {
 	return 0;
 }
 
-void mm_init() {
+void *palloc(uint_t n);
+void
+mm_init()
+{
 	uint_t i;
 	size_t ramsz;
 
@@ -96,7 +101,9 @@ void mm_init() {
 	mmu_enable();
 }
 
-void *palloc(uint_t npages) {
+void *
+palloc(uint_t npages)
+{
 	uint_t i, j, n;
 	uint_t sbit;
 	int ret;
@@ -114,7 +121,7 @@ void *palloc(uint_t npages) {
 				else if (!(bitmap[i] & (1 << j)))
 					n++;
 				if (n == npages) {
-					sbit = i * 32 + j - n;
+					sbit = i * 32 + j - n + 1;
 					goto out;
 				}
 			}
@@ -131,7 +138,9 @@ out:
 	return (void*)((uintptr_t)sbit * PAGE_SIZE + (uintptr_t)&_ram_start);
 }
 
-int pfree(void *paddr, uint_t npages) {
+int
+pfree(void *paddr, uint_t npages)
+{
 	uintptr_t paddr_a = (uintptr_t)paddr;
 	uint_t sbit;
 
diff --git a/kernel/mmu.c b/kernel/mmu.c
@@ -5,7 +5,9 @@
 static u32 mmu_ttb[4096] __attribute__((__aligned__ (16 * 1024)));
 static u32 l2[4096][256] __attribute__((__aligned__ (1024)));
 
-void mmu_init() {
+void
+mmu_init(void)
+{
 	int i;
 
 	for (i = 0; i < 4096; i++)
@@ -35,7 +37,9 @@ void mmu_init() {
 	);
 }
 
-void mmu_enable() {
+void
+mmu_enable(void)
+{
 	asm volatile (
 		/* invalidate TLB */
 		"mcr p15, 0, v1, c8, c7, 0	\n\t"
@@ -47,7 +51,9 @@ void mmu_enable() {
 	);
 }
 
-void mmu_disable() {
+void
+mmu_disable(void)
+{
 	asm volatile (
 		/* disable MMU */
 		"mrc p15, 0, v1, c1, c0, 0	\n\t"
@@ -57,7 +63,38 @@ void mmu_disable() {
 	);
 }
 
-static int is_mapped_virt(void *virt) {
+uintptr_t
+virt_to_phys(void *virt)
+{
+	uint_t pte_idx, pde_idx;
+	u32 *pde;
+	uintptr_t virt_a;
+
+	virt_a = (uintptr_t)virt;
+	pde_idx = virt_a >> 20;
+
+	switch (mmu_ttb[pde_idx] & L1_TYPE_MASK) {
+	case 1: /* page table */
+		pde = (u32*)(mmu_ttb[pde_idx] & ~0x3ff);
+		pte_idx = (virt_a & 0xff000) >> 12;
+		if (pde[pte_idx] & L2_TYPE_MASK)
+			return ((pde[pte_idx] & ~0xfff) | (virt_a & 0xfff));
+		else
+			return 0; /* not mapped */
+	case 2: /* section */
+		return ((mmu_ttb[pde_idx] & ~0xfffff) | (virt_a & 0xfffff));
+	case 0: /* not mapped */
+	default:
+		return 0;
+	}
+
+	/* not mapped */
+	return 0;
+}
+
+int
+virt_is_mapped(void *virt)
+{
 	uint_t pte_idx, pde_idx;
 	u32 *pde;
 	uintptr_t virt_a;
@@ -84,7 +121,9 @@ static int is_mapped_virt(void *virt) {
 }
 
 /* map physical memory to virtual memory */
-int mmu_map_page(void *phys, void *virt, uint_t npages, mmu_ap_t perms) {
+int
+mmu_map_page(void *phys, void *virt, uint_t npages, mmu_ap_t perms)
+{
 	u32 pte, pte_perms;
 	u32 *pde;
 	uintptr_t phys_a, virt_a;
@@ -151,7 +190,9 @@ int mmu_map_page(void *phys, void *virt, uint_t npages, mmu_ap_t perms) {
 	return 0;
 }
 
-int kmmap(void *virt, uint_t npages, mmu_ap_t perms) {
+int
+kmmap(void *virt, uint_t npages, mmu_ap_t perms)
+{
 	uint_t i;
 	uintptr_t virt_a;
 	void *pa;
@@ -166,13 +207,13 @@ int kmmap(void *virt, uint_t npages, mmu_ap_t perms) {
 		return -EFAULT;
 
 	for (i = 0; i < npages; i++) {
-		if (is_mapped_virt((void*)virt_a)) {
+		if (virt_is_mapped((void*)virt_a)) {
 			kprintf("WARNING: %p virtual address is already maped\n", virt);
 			virt_a += PAGE_SIZE;
 			continue;
 		}
 		pa = palloc(1);
-		mmu_map_page(pa, virt, 1, perms);
+		mmu_map_page(pa, (void*)virt_a, 1, perms);
 		virt_a += PAGE_SIZE;
 	}
 
diff --git a/kernel/print.c b/kernel/print.c
@@ -1,21 +1,29 @@
 #include <kernel.h>
 #include <rs232.h>
 
-int kputs(const char *s) {
+int
+kputs(const char *s)
+{
 	return rs232_puts(s);
 }
 
-int kputchar(int c) {
+int
+kputchar(int c)
+{
 	return rs232_putchar(c);
 }
 
-static inline int abs(int n) {
+static inline int
+abs(int n)
+{
 	if (n < 0)
 		return -n;
 	return n;
 }
 
-static int print_int(int n) {
+static int
+print_int(int n)
+{
 	int ret = 0, i = 1;
 
 	if (n > 0) {
@@ -38,7 +46,9 @@ static int print_int(int n) {
 	return ret;
 }
 
-static int print_hexint(uint_t n) {
+static int
+print_hexint(uint_t n)
+{
 	uint_t x, mask;
 	int i, bits, b, ret = 0;
 
@@ -72,7 +82,9 @@ static int print_hexint(uint_t n) {
 	return ret;
 }
 
-static int print_pointer(uintptr_t p) {
+static int
+print_pointer(uintptr_t p)
+{
 	uintptr_t x, mask;
 	int i, bits, b, ret = 0;
 
@@ -102,7 +114,9 @@ static int print_pointer(uintptr_t p) {
 	return ret;
 }
 
-int kprintf(const char *fmt, ...) {
+int
+kprintf(const char *fmt, ...)
+{
 	va_list ap;
 	int ret;
 
@@ -113,7 +127,9 @@ int kprintf(const char *fmt, ...) {
 	return ret;
 }
 
-int kvprintf(const char *fmt, va_list ap) {
+int
+kvprintf(const char *fmt, va_list ap)
+{
 	int d, ret = 0;
 	uint_t x;
 	uintptr_t p;
diff --git a/kernel/rs232.c b/kernel/rs232.c
@@ -1,10 +1,15 @@
 #include <kernel.h>
+#include <irq.h>
 #include <rs232.h>
 #include <uart.h>
 
+/* UART3 */
+#define UART_IRQ_NUM	(HW_IRQ(74))
 static struct uart *uart = (struct uart*)0x48020000;
 
-int rs232_puts(const char *s) {
+int
+rs232_puts(const char *s)
+{
 	int ret = 0;
 
 	while (s[ret]) {
@@ -15,7 +20,9 @@ int rs232_puts(const char *s) {
 	return ret;
 }
 
-int rs232_putchar(int c) {
+int
+rs232_putchar(int c)
+{
 	if (c == '\n') {
 		while ((readl(&uart->lsr) & TX_FIFO_E) == 0)
 			;
@@ -28,3 +35,28 @@ int rs232_putchar(int c) {
 	writel(c, &uart->thr);
 	return c;
 }
+
+int
+rs232_getchar(void)
+{
+	while ((readl(&uart->lsr) & RX_FIFO_E) == 0)
+		;
+	return readl(&uart->rhr);
+}
+
+static void
+rs232_irq_handler(__unused u32 irq_num, __unused struct regs *regs)
+{
+	while(!(readl(&uart->iir) & IT_PENDING)) {
+		if (readl(&uart->iir) & IT_TYPE_RHR)
+			rs232_putchar(rs232_getchar());
+	}
+}
+
+__attribute__((constructor))
+void
+rs232_init(void)
+{
+	irq_register(UART_IRQ_NUM, rs232_irq_handler);
+	writel(RHR_IT, &uart->ier);
+}
diff --git a/kernel/sched.c b/kernel/sched.c
@@ -0,0 +1,134 @@
+#include <kernel.h>
+#include <list.h>
+#include <spinlock.h>
+#include <dmtimer.h>
+#include <sched.h>
+#include <mmu.h>
+#include <p_modes.h>
+
+struct task_struct *curr_task = NULL;
+static struct list_head task_list_head;
+static spinlock_t task_struct_lock = SPINLOCK_INIT;
+
+static pid_t
+get_new_pid(void)
+{
+	static pid_t currpid = 0;
+	static spinlock_t lock = SPINLOCK_INIT;
+	pid_t new_pid;
+
+	spinlock_lock(&lock);
+	new_pid = ++currpid;
+	spinlock_unlock(&lock);
+
+	return new_pid;
+}
+
+static void
+task_remove(void)
+{
+	current->state = TASK_TERMINATE;
+	/* force schedule */
+	schedule();
+}
+
+int
+kthread_create(void (*routine)(void *), void *arg)
+{
+	struct task_struct *task;
+
+	task = kmalloc(sizeof(*task));
+	if (!task)
+		return -ENOMEM;
+
+	/* allocate stack */
+	task->stack_alloc = kmalloc(PAGE_SIZE);
+	if (!task->stack_alloc) {
+		kfree(task);
+		return -ENOMEM;
+	}
+
+	task->state = TASK_RUNNABLE;
+	task->pid = get_new_pid();
+	task->lock = &task_struct_lock;
+	memset(&task->regs, 0, sizeof(task->regs));
+	/* set thread stack */
+	task->regs.sp = (u32)task->stack_alloc;
+	task->regs.sp += PAGE_SIZE;
+	/* set argument */
+	task->regs.r0 = (u32)arg;
+	/* set the function that new thread will execute
+	 * we must add 4 because irq_ex will subtract 4 */
+	task->regs.pc = (u32)routine;
+	task->regs.pc += 4;
+	/* set return address */
+	task->regs.lr = (u32)&task_remove;
+	/* thread will run in System mode */
+	task->regs.cpsr = CPS_SYS;
+
+	/* add it to task list of the scheduler */
+	spinlock_lock(task->lock);
+	list_add(&task->list, &task_list_head);
+	spinlock_unlock(task->lock);
+
+	return 0;
+}
+
+void
+schedule(void)
+{
+	/* trigger scheduler timer */
+	dmtimer_trigger(1);
+	while(1)
+		asm volatile("wfi");
+}
+
+static void
+sched(struct regs *regs)
+{
+	struct task_struct *prev = current;
+
+	if (list_empty(&task_list_head))
+		return;
+
+	if (prev) {
+		if (prev->state != TASK_TERMINATE)
+			prev->regs = *regs;
+
+		if (list_is_last(&prev->list, &task_list_head))
+			current = list_first_entry(&task_list_head, struct task_struct, list);
+		else
+			current = list_entry(prev->list.next, struct task_struct, list);
+
+		if (prev->state == TASK_TERMINATE) {
+			spinlock_lock(prev->lock);
+			list_del(&prev->list);
+			spinlock_unlock(prev->lock);
+			kfree(prev->stack_alloc);
+			kfree(prev);
+			if (list_empty(&task_list_head))
+				current = NULL;
+		} else
+			current->state = TASK_RUNNABLE;
+	} else
+		current = list_first_entry(&task_list_head, struct task_struct, list);
+
+	if (current) {
+		current->state = TASK_RUNNING;
+		*regs = current->regs;
+	}
+}
+
+static void
+sched_handler(__unused int timer_id, struct regs *regs)
+{
+	sched(regs);
+}
+
+__attribute__((__constructor__))
+void
+sched_init(void)
+{
+	INIT_LIST_HEAD(&task_list_head);
+	dmtimer_register(1, sched_handler, 10);
+}
diff --git a/kernel/start.S b/kernel/start.S
@@ -33,31 +33,39 @@ _start:
 	bic r0, r0, #(1 << 2)		@ set C flag to 0 (disable data cache)
 	mcr p15, 0, r0, c1, c0, 0
 
+	/* initialize memory management */
+	bl mm_init
+
+	/* initialize interrupts */
 	cps #CPS_FIQ		@ change to FIQ mode
 	ldr sp, =_fiq_stack_top @ set FIQ stack
 
 	cps #CPS_IRQ		@ change to IRQ mode
 	ldr sp, =_irq_stack_top @ set IRQ stack
 
-	cps #CPS_SVC		@ change to SVC mode
+	cps #CPS_ABT	      @ change to Abort mode
+	ldr sp, =_abort_stack_top @ set stack
+
+	cps #CPS_SVC		@ change back to SVC mode
+	bl irq_init		@ initialize controler
 	cpsie if		@ enable interrupts
 
-	bl mm_init
+	bl dmtimer_init
 
 	/* run constructors */
 	ldr r0, =_kernel_ctors_start
 	ldr r1, =_kernel_ctors_end
 1:
 	teq r0, r1
-	beq 2f
+	beq 3f
 	ldr r2, [r0], #4
 	stmfd sp!, { r0, r1 }
-	ldr lr, =.Lctor_ret
+	ldr lr, =2f
 	mov pc, r2
-.Lctor_ret:
+2:
 	ldmfd sp!, { r0, r1 }
 	b 1b
-2:
+3:
 
 	/* example of calling a system call with at least 6 arguments */
 	mov r0, #1
@@ -82,8 +90,10 @@ _start:
 	swi #1
 	add sp, sp, #(7 * 4)
 
-	bl kmain
-	b .
+	ldr r0, =kmain
+	mov r1, #0
+	bl kthread_create
+	b schedule
 
 .section .rodata
 boot_msg:
diff --git a/kernel/string.c b/kernel/string.c
@@ -0,0 +1,51 @@
+#include <inttypes.h>
+#include <string.h>
+
+void *
+memset(void *s, int c, size_t n)
+{
+	unsigned char *us = s;
+
+	while (n) {
+		*us = c;
+		us++;
+		n--;
+	}
+
+	return s;
+}
+
+void *
+memcpy(void *s1, const void *s2, size_t n)
+{
+	unsigned char *us1 = s1;
+	const unsigned char *us2 = s2;
+
+	while (n) {
+		*us1 = *us2;
+		us1++;
+		us2++;
+		n--;
+	}
+
+	return s1;
+}
+
+int
+memcmp(const void *s1, const void *s2, size_t n)
+{
+	const unsigned char *us1, *us2;
+
+	us1 = s1;
+	us2 = s2;
+
+	while (n) {
+		if (*us1 != *us2)
+			return (*us1 - *us2 < 0) ? -1 : 1;
+		us1++;
+		us2++;
+		n--;
+	}
+
+	return 0;
+}
diff --git a/kernel/syscalls.c b/kernel/syscalls.c
@@ -1,6 +1,8 @@
 #include <kernel.h>
 
-long sys_test_6_args(u32 a1, u32 a2, u32 a3, u32 a4, u32 a5, u32 a6) {
+long
+sys_test_6_args(u32 a1, u32 a2, u32 a3, u32 a4, u32 a5, u32 a6)
+{
 	kprintf("sys_test_6_args(%d, %d, %d, %d, %d, %d)\n", a1, a2, a3, a4, a5, a6);
 	return 0;
 }
@@ -16,7 +18,9 @@ struct __test_7_args {
 	u32 a7;
 };
 
-long sys_test_7_args(struct __test_7_args *a) {
+long
+sys_test_7_args(struct __test_7_args *a)
+{
 	kprintf("sys_test_7_args(%d, %d, %d, %d, %d, %d, %d)\n",
 		a->a1, a->a2, a->a3, a->a4, a->a5, a->a6, a->a7);
 	return 0;