voron

experimental ARM OS
git clone git://git.2f30.org/voron
Log | Files | Refs | README | LICENSE

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:
Minclude/alloc.h | 2+-
Ainclude/dmtimer.h | 11+++++++++++
Minclude/errno.h | 4+++-
Ainclude/gic.h | 11+++++++++++
Ainclude/irq.h | 14++++++++++++++
Minclude/kernel.h | 6++++++
Minclude/list.h | 37++++++++++++++++++++++++++++++++-----
Minclude/mmu.h | 2++
Ainclude/regs.h | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainclude/sched.h | 42++++++++++++++++++++++++++++++++++++++++++
Minclude/spinlock.h | 16++++++++++++----
Minclude/string.h | 45++++-----------------------------------------
Minclude/uart.h | 32++++++++++++++++++--------------
Akernel/abort.c | 19+++++++++++++++++++
Mkernel/alloc.c | 24+++++++++++++++++-------
Mkernel/debug.c | 40+++++++++++++++++++++++++++++++++++++---
Akernel/dmtimer.c | 252+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Akernel/gic.c | 110+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mkernel/interrupts.S | 89++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Akernel/irq.c | 21+++++++++++++++++++++
Mkernel/kmain.c | 19+++++++++++++++----
Mkernel/linker.ld | 3+++
Mkernel/mm.c | 19++++++++++++++-----
Mkernel/mmu.c | 57+++++++++++++++++++++++++++++++++++++++++++++++++--------
Mkernel/print.c | 32++++++++++++++++++++++++--------
Mkernel/rs232.c | 36++++++++++++++++++++++++++++++++++--
Akernel/sched.c | 134+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mkernel/start.S | 26++++++++++++++++++--------
Akernel/string.c | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Mkernel/syscalls.c | 8++++++--
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;