commit aee799648fdc6a1e3718ae1dad10c63dead114da Author: Bas Wijnen Date: Mon May 11 00:28:12 2009 +0200 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1375e3e --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +all +all.raw.gz +report +uimage +*.o diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..531d713 --- /dev/null +++ b/Makefile @@ -0,0 +1,43 @@ +load = 0xa0000000 + +CXXFLAGS = -Wno-unused-parameter -fno-strict-aliasing -fno-builtin -nostdinc +CPPFLAGS = -O5 -Wa,-mips32 +CROSS = mipsel-linux-gnu- +CC = $(CROSS)gcc +LD = $(CROSS)ld +OBJCOPY = $(CROSS)objcopy +OBJDUMP = $(CROSS)objdump + +BUILT_SOURCES = interrupts.cc panic.cc data.cc test.cc + +PYPP = /usr/bin/pypp +%.cc: %.ccp kernel.hh + $(PYPP) --name $< < $< > $@ +%.hh: %.hhp + $(PYPP) --name $< < $< > $@ + +# Transform ':' into ';' so vim doesn't think there are errors. +uimage: all.raw.gz Makefile + mkimage -A MIPS -O Linux -C gzip -a $(load) -e 0x$(shell /bin/sh -c '$(OBJDUMP) -t all | grep __start$$ | cut -b-8') -n "Shevek's kernel" -d $< $@ | sed -e 's/:/;/g' + +%.o:%.cc Makefile kernel.hh + $(CC) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@ + +%.o:%.S Makefile + $(CC) $(CPPFLAGS) -DKERNEL_STACK_SIZE=0x2000 -c $< -o $@ + +# entry.o must be the first file. For the rest, the order doesn't matter. +all: entry.o $(subst .cc,.o,$(BUILT_SOURCES)) + $(LD) --omagic -Ttext $(load) $^ -o $@ + +junk = mdebug.abi32 reginfo comment pdr +%.raw: % + $(OBJCOPY) -S $(addprefix --remove-section=.,$(junk)) -Obinary $< $@ + +%.gz: % + gzip < $< > $@ + +clean: + rm -f all uimage *.o all.raw.gz + +.PHONY: clean diff --git a/data.ccp b/data.ccp new file mode 100644 index 0000000..8f275c2 --- /dev/null +++ b/data.ccp @@ -0,0 +1,8 @@ +#pypp 0 +#define EXTERN +#include "kernel.hh" + +// This is needed to make gcc happy to compile c++ code without +// its standard library. +char __gxx_personality_v0[] = "hack"; + diff --git a/entry.S b/entry.S new file mode 100644 index 0000000..2ab3ca5 --- /dev/null +++ b/entry.S @@ -0,0 +1,230 @@ + // The kernel stack. + .lcomm kernel_stack, KERNEL_STACK_SIZE + + .globl __start + .globl run_idle + .set noat + .set noreorder + +#define EntryLo0 2 +#define EntryLo1 3 +#define EntryHi 10 +#define Random 1 +#define Index 0 +#define PageMask 5 +#define Wired 6 +#define Config 16 +#define Status 12 +#define Cause 13 +#define Compare 11 + +#define save_regs \ + sw $k0, -0x180($zero) ;\ + lw $k0, -0x184($zero) ;\ + sw $at, 12($k0) ;\ + sw $k1, 108($k0) ;\ + lw $k1, -0x180($zero) ;\ + sw $k1, 104($k0) ;\ + sw $v0, 16($k0) ;\ + sw $v1, 20($k0) ;\ + sw $t0, 40($k0) ;\ + sw $sp, 84($k0) ;\ + sw $fp, 88($k0) ;\ + sw $ra, 92($k0) ;\ + lw $gp, -12($zero) ;\ + la $sp, kernel_stack + KERNEL_STACK_SIZE ;\ + move $a0, $k0 ;\ + la $ra, kernel_exit ;\ + bal save_args + +#define restore_regs \ + lw $v0, 96($k0) ;\ + lw $v1, 100($k0) ;\ + mthi $v0 ;\ + mtlo $v1 ;\ + lw $v0, 16($k0) ;\ + lw $v1, 20($k0) ;\ + lw $a0, 24($k0) ;\ + lw $a1, 28($k0) ;\ + lw $a2, 32($k0) ;\ + lw $a3, 36($k0) ;\ + lw $t0, 40($k0) ;\ + lw $t1, 44($k0) ;\ + lw $t2, 48($k0) ;\ + lw $t3, 52($k0) ;\ + lw $t4, 56($k0) ;\ + lw $t5, 60($k0) ;\ + lw $t6, 64($k0) ;\ + lw $t7, 68($k0) ;\ + lw $t8, 72($k0) ;\ + lw $t9, 76($k0) ;\ + lw $gp, 80($k0) ;\ + lw $sp, 84($k0) ;\ + lw $fp, 88($k0) ;\ + lw $ra, 92($k0) ;\ + lw $at, 12($k0) ;\ + lw $k1, 104($k0) ;\ + sw $k1, -0x180($zero) ;\ + lw $k1, 108($k0) ;\ + sw $k0, -0x184($zero) ;\ + lw $k0, -0x180($zero) + +addr_000: + // TLB refill + // TODO: this should probably be assembly-only for speed reasons + + li $a0, 0xffff0000 + la $t9, panic + jr $t9 + + save_regs + mfc0 $a1, $EntryHi + la $t9, tlb_refill + jr $t9 + nop + .fill 0x100 - (. - addr_000) +addr_100: + // Cache error + // TODO + + li $a0, 0xaaaa0000 + la $t9, panic + jr $t9 + + save_regs + la $t9, cache_error + jr $t9 + nop + .fill 0x180 - (. - addr_000) +addr_180: + // General exception + // TODO + + li $a0, 0xaaff0000 + la $t9, panic + jr $t9 + + save_regs + la $t9, exception + jr $t9 + nop + .fill 0x200 - (. - addr_000) +addr_200: + // Interrupt + // TODO + + li $a0, 0x0a0f0000 + la $t9, panic + jr $t9 + + save_regs + la $t9, interrupt + jr $t9 + nop + .fill 0x280 - (. - addr_000) + + // space for save_regs. + .word 0 + .word 0 + +start_idle: + b . + nop + .word idle_page + .word 0x80000000 // A pointer to the current page. + +__start: + bal 1f + nop + .word _gp +// For some reason the disassembler considers everything +// after __start non-code until the next label. So I add a label. +start_hack: +1: lw $gp, 0($ra) + + la $sp, kernel_stack + KERNEL_STACK_SIZE + + // Disable interrupts during bootstrap. + mtc0 $zero, $Status + + // TODO: flush cache + + // Set kseg0 cachable. + li $k0, 0x3 + mtc0 $k0, $Config, 0 + + // Clear .bss + la $a0, _edata + la $a1, _end +1: sw $zero, 0($a0) + bne $a1, $a0, 1b + addu $a0, 4 + + // Set timer to a defined value + mtc0 $zero, $Compare, 0 + + // Use the interrupt vector for interrupts + li $k0, 1 << 23 + mtc0 $k0, $Cause + + li $a0, 0xff0a0000 + la $t9, panic + jr $t9 + + // clear the tlb, hardwire page 0 to 0xffffffff + // and soft-wire it to (0x294 << 20) + (0x290 << 10) + // (for the idle task). + + // this address doesn't reach the tlb, so it can't trigger exceptions. + mtc0 $zero, $PageMask + mtc0 $zero, $EntryLo0 + mtc0 $zero, $EntryLo1 + li $t0, 0x80000000 + // There are 32 tlb entries. + addi $a0, $zero, 31 +1: mtc0 $t0, $EntryHi + mtc0 $a0, $Index + tlbwi + addiu $t0, $t0, 1 << 13 + bgtz $a0, 1b + addiu $a0, $a0, -1 + + la $t9, init + jalr $t9 + nop + sw $gp, -12($zero) + +save_args: + sw $a0, 24($k0) + sw $a1, 28($k0) + sw $a2, 32($k0) + sw $a3, 36($k0) + sw $t1, 44($k0) + sw $t2, 48($k0) + sw $t3, 52($k0) + sw $t4, 56($k0) + sw $t5, 60($k0) + sw $t6, 64($k0) + sw $t7, 68($k0) + sw $t8, 72($k0) + sw $t9, 76($k0) + sw $gp, 80($k0) + j $ra + +run_idle: + move $k0, $a0 + move $k1, $gp + // Prepare enabling interrupts and switching to user mode. + // The command is executed in the delay slot of the jump. + li $t0, 0xff11 + // Now it's acceptable to receive an exception. Jump to kuseg, + // thereby generating a tlb_refill exception (which is handled). + // The address is the virtual address where the idle task is. + la $t9, ((0x294 << 20) + (0x290 << 10) + 0x288) + jr $t9 + mtc0 $t0, $Status + +kernel_exit: + move $k0, $v0 + restore_regs + eret diff --git a/interrupts.ccp b/interrupts.ccp new file mode 100644 index 0000000..9bbf651 --- /dev/null +++ b/interrupts.ccp @@ -0,0 +1,115 @@ +#pypp 0 +#include "kernel.hh" + +// hi and lo cannot be saved in assemply due to space restrictions. +#define save_hilo(x) do { __asm__ ("mfhi %0 ; mflo %1" : "=r"((x)->cpu.hi), "=r"((x)->cpu.lo)); } while (0) + +/// A TLB miss has occurred. This should eventually move to entry.S. +Thread *tlb_refill (Thread *current, unsigned EntryHi): + save_hilo (current) + led (false, false) + Page ***dir = current->address_space->cpu.directory + if !dir: + panic (0x00000000, "no page directory for thread") + EntryHi >>= 12 + Page **table = dir[EntryHi >> 10] + if !table: + panic (0x11111111, "no page table at requested address") + EntryHi &= (1 << 10) - 1 + Page *page0 = table[EntryHi & ~1] + Page *page1 = table[EntryHi | 1] + if (!(EntryHi & 1) && !page0) || ((EntryHi & 1) && !page1): + panic (0x22222222, "no page mapped at requested address") + unsigned low0, low1 + if page0: + low0 = page0->physical | 0x18 | 0x4 | 0x2 + else + low0 = 0 + if page1: + low1 = page1->physical | 0x18 | 0x4 | 0x2 + else + low1 = 0 + __asm__ ("mtc0 %0, $2; mtc0 %1, $3; tlbwr" :: "r"(low0), "r"(low1)) + return current + +/// An interrupt which is not an exception has occurred. +Thread *interrupt (Thread *current): + save_hilo (current) + led (false, false) + return current + +/// A general exception has occurred. +Thread *exception (Thread *current): + save_hilo (current) + panic (0xdeadbeaf, "exception") + return current + +/// There's a cache error. Big trouble. Probably not worth trying to recover. +Thread *cache_error (Thread *current): + save_hilo (current) + panic (0x33333333, "cache error") + return current + +static void init_idle (): + // initialize idle task as if it is currently running. + idle.size = sizeof (Thread) + idle.prev = NULL + idle.next = NULL + idle.thread_prev = NULL + idle.thread_next = NULL + idle.schedule_prev = NULL + idle.schedule_next = NULL + idle.address_space = &idle_memory + // initialize idle_memory. + idle_memory.size = sizeof (Memory) + idle_memory.prev = NULL + idle_memory.next = NULL + idle_memory.memory_prev = NULL + idle_memory.memory_next = NULL + idle_memory.pages = &idle_page + idle_memory.threads = &idle + idle_memory.memories = NULL + idle_memory.limit = 0 + idle_memory.used = 0 + idle_memory.cpu.directory = (Page ***)0x80000000 + idle_memory.cpu.asid = 0 + // initialize idle_page + idle_page.size = sizeof (Page) + idle_page.prev = NULL + idle_page.next = NULL + idle_page.page_prev = NULL + idle_page.page_next = NULL + // Not an error, this is really physical page 0. + idle_page.physical = 0 + +/// Initialize the kernel, finish by falling into the idle task. +extern unsigned _end +void init (): + // Initialize kernel variables to empty. + sleepers = NULL + runners = NULL + zero_pages = NULL + // Fill junk pages with all memory not currently used. + junk_pages = (FreePage *)(((unsigned)&_end + (1 << 12) - 1) & ~((1 << 12) - 1)) + FreePage *p, *next + unsigned count = 1 + for p = junk_pages; (unsigned)next - 0x80000000 < (1 << 27); p = next, next = (FreePage *)((unsigned)p + 1 << 12): + p->next = next + ++count + p->next = NULL + // initialize everything about the idle task. + init_idle () + // initialize top_memory. + top_memory.size = sizeof (Memory) + top_memory.prev = NULL + top_memory.next = NULL + top_memory.memory_prev = NULL + top_memory.memory_next = NULL + top_memory.pages = NULL + top_memory.threads = NULL + top_memory.memories = NULL + top_memory.limit = count + top_memory.used = 0 + top_memory.cpu.directory = NULL + top_memory.cpu.asid = 0 + // TOOO: set up initial threads. diff --git a/kernel.hhp b/kernel.hhp new file mode 100644 index 0000000..d350efb --- /dev/null +++ b/kernel.hhp @@ -0,0 +1,75 @@ +#pypp 0 +#ifndef _KERNEL_HH +#define _KERNEL_HH + +#define NULL 0 + +class Object +class Page +class Thread +class Memory + +struct Object: + unsigned size + Object *prev, *next + +struct Page : public Object: + Page *page_prev, *page_next + unsigned physical + +struct Thread : public Object: + struct Cpu: + unsigned at, v0, v1, a0, a1, a2, a3 + unsigned t0, t1, t2, t3, t4, t5, t6, t7, t8, t9 + unsigned gp, sp, fp, ra, hi, lo, k0, k1, pc + Cpu cpu + Thread *thread_prev, *thread_next + Thread *schedule_prev, *schedule_next + Memory *address_space + +struct Memory : public Object: + Memory *memory_prev, *memory_next + Page *pages + Thread *threads + Memory *memories + unsigned limit, used + struct Cpu: + unsigned asid + Page ***directory + Cpu cpu + +// Functions which can be called from assembly must not be mangled. +extern "C": + // Kernel entry points, called from entry.S. + void init () + Thread *interrupt (Thread *current) + Thread *cache_error (Thread *current) + Thread *exception (Thread *current) + + // tlb stuff. tlb_refill is also an entry point. + void tlb_setup () + Thread *tlb_refill (Thread *current, unsigned EntryHi) + + // Start running the idle task for the first time. + void run_idle (Thread *self) + + // Panic. n is sent over caps led. message is currently ignored. + void panic (unsigned n, char const *message) + // Debug: switch caps led + void led (bool on, bool tick) + +#ifndef EXTERN +#define EXTERN extern +#endif + +struct FreePage: + FreePage *next + +EXTERN FreePage *zero_pages, *junk_pages +EXTERN Memory top_memory +EXTERN Thread *sleepers, *runners +EXTERN Thread idle +EXTERN Memory idle_memory +EXTERN Page idle_page + +#endif diff --git a/panic.ccp b/panic.ccp new file mode 100644 index 0000000..bfba871 --- /dev/null +++ b/panic.ccp @@ -0,0 +1,10 @@ +#pypp 0 +#include "kernel.hh" + +void panic (unsigned n, char const *message): + while (1): + for unsigned bit = 0x80000000; bit; bit >>= 1: + for int i = 0; i < 60000; ++i: + led (n & bit, i > 20000 && i < 40000) + for int i = 0; i < 100000; ++i: + led (false, false) diff --git a/start.S b/start.S new file mode 100644 index 0000000..5d83a4d --- /dev/null +++ b/start.S @@ -0,0 +1,24 @@ + .set noreorder + .globl __start + .globl kernel_stack +__start: + bal 1f + nop + .word _gp +1: lw $gp, 0($ra) + nop + + la $a0, _edata + la $a1, _end +1: sw $zero, 0($a0) + bne $a1, $a0, 1b + addu $a0, 4 + + la $sp, kernel_stack + KERNEL_STACK_SIZE + + bal kernel_entry + nop + b . + nop + + .lcomm kernel_stack, KERNEL_STACK_SIZE diff --git a/test.ccp b/test.ccp new file mode 100644 index 0000000..114d1ba --- /dev/null +++ b/test.ccp @@ -0,0 +1,74 @@ +#pypp 0 +#include "kernel.hh" + +#define REG32(addr) *((volatile unsigned int *)(addr)) + +#define GPIO_BASE 0xB0010000 + +#define __gpio_port_data(p) ( REG_GPIO_GPDR(p) ) + +#define GPIO_GPSLR(n) (GPIO_BASE + (0x10 + (n)*0x30)) +#define GPIO_GPSUR(n) (GPIO_BASE + (0x14 + (n)*0x30)) +#define GPIO_GPFLR(n) (GPIO_BASE + (0x18 + (n)*0x30)) +#define GPIO_GPFUR(n) (GPIO_BASE + (0x1c + (n)*0x30)) + +#define GPIO_GPDR(n) (GPIO_BASE + (0x00 + (n)*0x30)) + +#define REG_GPIO_GPSLR(n) REG32(GPIO_GPSLR((n))) +#define REG_GPIO_GPSUR(n) REG32(GPIO_GPSUR((n))) +#define REG_GPIO_GPFLR(n) REG32(GPIO_GPFLR((n))) +#define REG_GPIO_GPFUR(n) REG32(GPIO_GPFUR((n))) + +#define REG_GPIO_GPDR(n) REG32(GPIO_GPDR((n))) + +static void __gpio_port_as_gpiofn (unsigned p, unsigned o, unsigned fn): + unsigned int tmp; + if o < 16: + tmp = REG_GPIO_GPSLR(p) + tmp &= ~(3 << ((o) << 1)) + REG_GPIO_GPSLR(p) = tmp + tmp = REG_GPIO_GPFLR(p) + tmp &= ~(3 << ((o) << 1)) + tmp |= fn << ((o) << 1) + REG_GPIO_GPFLR(p) = tmp + else: + tmp = REG_GPIO_GPSUR(p) + tmp &= ~(3 << (((o) - 16) << 1)) + REG_GPIO_GPSUR(p) = tmp + tmp = REG_GPIO_GPFUR(p) + tmp &= ~(3 << (((o) - 16) << 1)) + tmp |= fn << (((o) - 16) << 1) + REG_GPIO_GPFUR(p) = tmp + +static void __gpio_port_as_output (unsigned p, unsigned o): + __gpio_port_as_gpiofn (p, o, 1) +static void __gpio_port_as_input (unsigned p, unsigned o): + __gpio_port_as_gpiofn (p, o, 0) +static void __gpio_as_output (unsigned n): + __gpio_port_as_output(n / 32, n % 32) +static void __gpio_as_input (unsigned n): + __gpio_port_as_input(n / 32, n % 32) +static void __gpio_set_pin (unsigned n): + __gpio_port_data (n / 32) |= (1 << (n % 32)) +static void __gpio_clear_pin (unsigned n): + __gpio_port_data (n / 32) &= ~(1 << (n % 32)) + +#define CAPSLOCKLED_IO 27 +#define NUMLOCKLED_IO 86 +#define NETWORK_IO 9 +#define LIGHT 105 + +void led (bool on, bool tick): + __gpio_as_output (CAPSLOCKLED_IO) + __gpio_as_output (NUMLOCKLED_IO) + __gpio_as_output (NETWORK_IO) + if on: + __gpio_set_pin (NUMLOCKLED_IO) + __gpio_clear_pin (CAPSLOCKLED_IO) + else: + __gpio_set_pin (CAPSLOCKLED_IO) + __gpio_clear_pin (NUMLOCKLED_IO) + if tick: + __gpio_clear_pin (NETWORK_IO) + else: + __gpio_set_pin (NETWORK_IO)