voron

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

commit ddf168c71ef495d152dd68294a1f01328d86fef5
parent abcaf464d3b173bd226284a8c9b9776d9e744540
Author: oblique <psyberbits@gmail.com>
Date:   Sat Aug  4 18:18:48 +0300

added: MMU, physical page allocator, I/O helpers
changed: kernel entry point

Diffstat:
.gitignore | 1+
Makefile | 12++++++++----
include/errno.h | 39+++++++++++++++++++++++++++++++++++++++
include/inttypes.h | 1+
include/io.h | 36++++++++++++++++++++++++++++++++++++
include/mm.h | 9+++++++++
include/mmu.h | 42++++++++++++++++++++++++++++++++++++++++++
kernel/debug.c | 19+++++++++++--------
kernel/interrupts.S | 10+++++-----
kernel/linker.ld | 9+++++++--
kernel/mm.c | 142+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
kernel/mmu.c | 125+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
kernel/rs232.c | 11++++++-----
kernel/start.S | 10+++++++++-
usbboot/usbboot.ld | 2+-
15 files changed, 442 insertions(+), 26 deletions(-)
diff --git a/.gitignore b/.gitignore @@ -9,3 +9,4 @@ /kernel.syms /uImage /usbbootImage +/voron.tar.gz diff --git a/Makefile b/Makefile @@ -16,7 +16,7 @@ NM = $(CROSS_COMPILE)nm objs = kernel/start.o kernel/kmain.o kernel/rs232.o kernel/print.o \ kernel/debug.o kernel/interrupts.o kernel/syscall_table.o \ - kernel/syscalls.o + kernel/syscalls.o kernel/mmu.o kernel/mm.o all: uImage usbbootImage kernel.syms @@ -31,12 +31,12 @@ kernel.syms: kernel.elf @$(NM) $< > $@ uImage: kernel.bin - @mkimage -A arm -T kernel -C none -a 0x80008000 -e 0x80008000 -n Voron -d $< $@ + @mkimage -A arm -T kernel -C none -a 0x80000000 -e 0x80000000 -n Voron -d $< $@ usbbootImage: usbboot/usbboot.o usbboot/usbboot.ld usbboot/kernel_image.o @echo "Creating $@" @echo "Entry Point: 0x82000000" - @echo "Load Image: 0x80008000" + @echo "Load Image: 0x80000000" @$(CC) -T usbboot/usbboot.ld -nostdlib -nostdinc -nodefaultlibs -nostartfiles \ -fno-builtin -o __$@ $< @$(OBJCOPY) __$@ -O binary $@ @@ -56,4 +56,8 @@ usbboot/kernel_image.o: kernel.bin clean: @rm -f $(objs) kernel.elf kernel.bin kernel.syms uImage usbbootImage \ - usbboot/kernel_image.o usbboot/usbboot.o + usbboot/kernel_image.o usbboot/usbboot.o voron.tar.gz + +targz: + @git archive --format=tar.gz --prefix=voron/ -o voron.tar.gz HEAD + @echo voron.tar.gz created diff --git a/include/errno.h b/include/errno.h @@ -0,0 +1,39 @@ +#ifndef __ERRNO_H +#define __ERRNO_H + +#define EPERM 1 /* Operation not permitted */ +#define ENOENT 2 /* No such file or directory */ +#define ESRCH 3 /* No such process */ +#define EINTR 4 /* Interrupted system call */ +#define EIO 5 /* I/O error */ +#define ENXIO 6 /* No such device or address */ +#define E2BIG 7 /* Argument list too long */ +#define ENOEXEC 8 /* Exec format error */ +#define EBADF 9 /* Bad file number */ +#define ECHILD 10 /* No child processes */ +#define EAGAIN 11 /* Try again */ +#define ENOMEM 12 /* Out of memory */ +#define EACCES 13 /* Permission denied */ +#define EFAULT 14 /* Bad address */ +#define ENOTBLK 15 /* Block device required */ +#define EBUSY 16 /* Device or resource busy */ +#define EEXIST 17 /* File exists */ +#define EXDEV 18 /* Cross-device link */ +#define ENODEV 19 /* No such device */ +#define ENOTDIR 20 /* Not a directory */ +#define EISDIR 21 /* Is a directory */ +#define EINVAL 22 /* Invalid argument */ +#define ENFILE 23 /* File table overflow */ +#define EMFILE 24 /* Too many open files */ +#define ENOTTY 25 /* Not a typewriter */ +#define ETXTBSY 26 /* Text file busy */ +#define EFBIG 27 /* File too large */ +#define ENOSPC 28 /* No space left on device */ +#define ESPIPE 29 /* Illegal seek */ +#define EROFS 30 /* Read-only file system */ +#define EMLINK 31 /* Too many links */ +#define EPIPE 32 /* Broken pipe */ +#define EDOM 33 /* Math argument out of domain of func */ +#define ERANGE 34 /* Math result not representable */ + +#endif /* __ERRNO_H */ diff --git a/include/inttypes.h b/include/inttypes.h @@ -35,5 +35,6 @@ typedef long intptr_t; #define S64_C(x) x ## LL #define U64_C(x) x ## ULL +#define PTR_DIFF(a, b) ((uintptr_t)(a) - (uintptr_t)(b)) #endif /* __INTTYPES_H */ diff --git a/include/io.h b/include/io.h @@ -0,0 +1,36 @@ +#ifndef __IO_H +#define __IO_H + +#include <inttypes.h> + +/* data memory barrier */ +#define dmb() asm volatile("dmb" : : : "memory") +/* data synchronization barrier */ +#define dsb() asm volatile("dsb" : : : "memory") +/* instruction synchronization barrier */ +#define isb() asm volatile("isb" : : : "memory") + +/* memory barrier */ +#define mb() dsb() +/* read memory barrier */ +#define rmb() dsb() +/* write memory barrier */ +#define wmb() dsb() + +#define readl_relaxed(a) (*(volatile u32*)(a)) +#define readw_relaxed(a) (*(volatile u16*)(a)) +#define readb_relaxed(a) (*(volatile u8*)(a)) + +#define writel_relaxed(v, a) (*(volatile u32*)(a) = (u32)(v)) +#define writew_relaxed(v, a) (*(volatile u16*)(a) = (u16)(v)) +#define writeb_relaxed(v, a) (*(volatile u8*)(a) = (u8)(v)) + +#define readl(a) ({ u32 __r = readl_relaxed(a); rmb(); __r; }) +#define readw(a) ({ u16 __r = readw_relaxed(a); rmb(); __r; }) +#define readb(a) ({ u8 __r = readb_relaxed(a); rmb(); __r; }) + +#define writel(v, a) ({ wmb(); writel_relaxed(v, a); }) +#define writew(v, a) ({ wmb(); writew_relaxed(v, a); }) +#define writeb(v, a) ({ wmb(); writeb_relaxed(v, a); }) + +#endif /* __IO_H */ diff --git a/include/mm.h b/include/mm.h @@ -0,0 +1,9 @@ +#ifndef __MM_H +#define __MM_H + +#include <inttypes.h> + +void *palloc(uint_t npages); +int pfree(void *paddr, uint_t npages); + +#endif /* __MM_H */ diff --git a/include/mmu.h b/include/mmu.h @@ -0,0 +1,42 @@ +#ifndef __MMU_H +#define __MMU_H + +#define PAGE_SHIFT 12 +#define PAGE_SIZE (1UL << PAGE_SHIFT) +#define PAGE_MASK (~(PAGE_SIZE - 1)) +#define PAGE_ALIGN(x) (((x) + PAGE_SIZE - 1) & PAGE_MASK) + +#define L1_FAULT 0 +#define L1_PAGE_TABLE 1 +#define L1_SECTION 2 + +#define L2_PAGE_FAULT 0 +#define L2_LARGE_PAGE 1 +#define L2_SMALL_PAGE 2 +#define L2_TINY_PAGE 3 + +#define PT_AP0 (1 << 4) +#define PT_AP1 (1 << 5) +#define PT_AP2 (1 << 9) + + +/* MMU access permissions + * MMU_AP_X_Y + * X = access permissions for PL1 or higher + * Y = access permissions for PL0 (user mode) + */ +typedef enum { + MMU_AP_RW_RW, + MMU_AP_RW_RO, + MMU_AP_RO_RO, + MMU_AP_RW_NONE, + MMU_AP_RO_NONE, + MMU_AP_NONE_NONE +} mmu_ap_t; + +void mmu_init(); +void mmu_enable(); +void mmu_disable(); +int mmu_map_page(void *phys, void *virt, uint_t npages, mmu_ap_t perms); + +#endif /* __MMU_H */ diff --git a/kernel/debug.c b/kernel/debug.c @@ -1,23 +1,26 @@ #include <inttypes.h> #include <debug.h> +#include <io.h> -static volatile u32 *gpio_wk7 = (u32*)0x4A31E058; -static volatile u32 *gpio_wk8 = (u32*)0x4A31E05C; +static u32 *gpio_wk7 = (u32*)0x4A31E058; +static u32 *gpio_wk8 = (u32*)0x4A31E05C; void set_led_d1(int on) { - u32 cur = *gpio_wk7 & 0xffff; + u32 cur = readl(gpio_wk7) & 0xffff; if (on) - *gpio_wk7 = cur | 0x001B0000; + cur |= 0x001B0000; else - *gpio_wk7 = cur | 0x00030000; + cur |= 0x00030000; + writel(cur, gpio_wk7); } void set_led_d2(int on) { - u32 cur = *gpio_wk8 & 0xffff0000; + u32 cur = readl(gpio_wk8) & 0xffff0000; if (on) - *gpio_wk8 = cur | 0x001B; + cur |= 0x001B; else - *gpio_wk8 = cur | 0x0003; + cur |= 0x0003; + writel(cur, gpio_wk8); } void set_leds(int on) { diff --git a/kernel/interrupts.S b/kernel/interrupts.S @@ -2,7 +2,7 @@ .global init_vector_table init_vector_table: mrc p15, 0, r0, c1, c0, 0 @ read CP15 SCTRL register - bic r0, r0, #(1 << 13) @ set V flag to 0 + bic r0, r0, #(1 << 13) @ set V flag to 0 (disable high vectors) mcr p15, 0, r0, c1, c0, 0 @ write CP15 SCTRL register ldr r0, =vector_table @@ -41,7 +41,7 @@ undefined_insn: swi_ex: stmfd sp!, { r6 - r12, lr } mrs r6, spsr - stmfd sp!, { r6 } + str r6, [sp, #-4]! ldr r6, [lr, #-4] bic r6, r6, #0xff000000 @@ -53,7 +53,7 @@ swi_ex: .Lsyscall_ret: add sp, sp, #8 - ldmfd sp!, { r6 } + ldr r6, [sp], #4 msr spsr, r6 ldmfd sp!, { r6 - r12, pc }^ @@ -68,7 +68,7 @@ data_abort: irq_ex: sub lr, lr, #4 - stmfd sp!, { lr } + str lr, [sp, #-4]! mov r0, #1 bl set_led_d1 ldmfd sp!, { pc }^ @@ -76,7 +76,7 @@ irq_ex: fiq_ex: sub lr, lr, #4 - stmfd sp!, { lr } + str lr, [sp, #-4]! mov r0, #1 bl set_led_d2 ldmfd sp!, { pc }^ diff --git a/kernel/linker.ld b/kernel/linker.ld @@ -3,10 +3,12 @@ OUTPUT_ARCH(arm) ENTRY(_start) SECTIONS { - _usr_stack_top = 0x80000000; + _ram_start = 0x80000000; + _ram_end = 0xbfffffff; - .text 0x80008000 : + .text 0x80000000 : { + _kernel_bin_start = .; kernel/start.o (.text) *(.text) } @@ -27,10 +29,13 @@ SECTIONS } . = ALIGN(4096); + . = . + 0x2000; _svc_stack_top = .; . = . + 0x2000; _fiq_stack_top = .; . = . + 0x2000; _irq_stack_top = .; + + _kernel_bin_end = .; } diff --git a/kernel/mm.c b/kernel/mm.c @@ -0,0 +1,142 @@ +#include <inttypes.h> +#include <mmu.h> +#include <errno.h> + +#define ALIGN32(x) (((x) + 31) & ~31) + +extern void *_ram_start; +extern void *_ram_end; +extern void *_kernel_bin_start; +extern void *_kernel_bin_end; + +static u32 *bitmap; +static uint_t bitmapsz; +static uint_t kbound_s; +static uint_t kbound_e; + +/* set range of bits */ +static int set_bitmap_bits(uint_t start_bit, uint_t n, int flag) { + uint_t bound_s, bound_e, i; + u32 bits; + + if (n == 0) + return -EINVAL; + + bound_s = start_bit; + bound_e = start_bit + n; + + if (bound_e > bitmapsz * 32) + return -EINVAL; + + if (bound_s / 32 != bound_e / 32) { + bits = ~((1 << (bound_s % 32)) - 1); + if (flag) + bitmap[bound_s/32] |= bits; + else + bitmap[bound_s/32] &= ~bits; + + bits = (1 << (bound_e % 32)) - 1; + if (flag) + bitmap[bound_e/32] |= bits; + else + bitmap[bound_e/32] &= ~bits; + } else { + bits = ~((1 << (bound_s % 32)) - 1); + bits &= (1 << (bound_e % 32)) - 1; + if (flag) + bitmap[bound_s/32] |= bits; + else + bitmap[bound_s/32] &= ~bits; + } + + for (i = (bound_s / 32) + 1; i < bound_e / 32; i++) { + if (flag) + bitmap[i] = ~0U; + else + bitmap[i] = 0; + } + + return 0; +} + +void mm_init() { + uint_t i; + size_t ramsz; + + kbound_s = PAGE_ALIGN(PTR_DIFF(&_kernel_bin_start, &_ram_start)) >> PAGE_SHIFT; + kbound_e = PAGE_ALIGN(PTR_DIFF(&_kernel_bin_end, &_ram_start)) >> PAGE_SHIFT; + + ramsz = PTR_DIFF(&_ram_end, &_ram_start) + 1; + bitmapsz = ALIGN32(ramsz >> PAGE_SHIFT) / 32; + bitmap = (u32*)&_kernel_bin_end; + kbound_e += PAGE_ALIGN(bitmapsz * 4) >> PAGE_SHIFT; + + for (i = 0; i < bitmapsz; i++) + bitmap[i] = 0; + + set_bitmap_bits(kbound_s, kbound_e - kbound_s, 1); + + mmu_init(); + + /* map on-chip memory */ + mmu_map_page((void*)0x40000000, (void*)0x40000000, + 0x40000, MMU_AP_RW_NONE); + /* map kernel memory */ + mmu_map_page(&_kernel_bin_start, &_kernel_bin_start, + kbound_e - kbound_s, MMU_AP_RW_NONE); + + mmu_enable(); +} + +void *palloc(uint_t npages) { + uint_t i, j, n; + uint_t sbit; + int ret; + + if (npages == 0) + return (void*)-EINVAL; + + n = 0; + for (i = 0; i < bitmapsz; i++) { + /* if bitmap[i] has unallocated page */ + if (bitmap[i] != ~0U) { + for (j = 0; j < 32; j++) { + if (n > 0 && (bitmap[i] & (1 << j))) + n = 0; + else if (!(bitmap[i] & (1 << j))) + n++; + if (n == npages) { + sbit = i * 32 + j - n; + goto out; + } + } + } else + n = 0; + } + + return (void*)-EINVAL; + +out: + ret = set_bitmap_bits(sbit, npages, 1); + if (ret < 0) + return (void*)ret; + return (void*)((uintptr_t)sbit * PAGE_SIZE + (uintptr_t)&_ram_start); +} + +int pfree(void *paddr, uint_t npages) { + uintptr_t paddr_a = (uintptr_t)paddr; + uint_t sbit; + + if (npages == 0 || paddr_a & (PAGE_SIZE - 1) || + paddr_a < (uintptr_t)&_ram_start || + paddr_a >= (uintptr_t)&_ram_end) + return -EINVAL; + + sbit = (paddr_a - (uintptr_t)&_ram_start) >> PAGE_SHIFT; + + /* we cannot free the memory of ourself */ + if (sbit >= kbound_s && sbit < kbound_e) + return -EINVAL; + + return set_bitmap_bits(sbit, npages, 0); +} diff --git a/kernel/mmu.c b/kernel/mmu.c @@ -0,0 +1,125 @@ +#include <inttypes.h> +#include <mmu.h> +#include <errno.h> +#include <debug.h> +#include <print.h> + +static u32 mmu_ttb[4096] __attribute__((__aligned__ (16 * 1024))); +static u32 l2[4096][256] __attribute__((__aligned__ (1024))); + +void mmu_init() { + int i; + + for (i = 0; i < 4096; i++) + mmu_ttb[i] = L1_FAULT; + + asm volatile ( + /* invalidate TLB */ + "mov v1, #0 \n\t" + "mcr p15, 0, v1, c8, c7, 0 \n\t" + /* set TTBCR */ + "mov v1, #0 \n\t" + "mcr p15, 0, v1, c2, c0, 2 \n\t" + /* set TTBR0 */ + "ldr v1, =mmu_ttb \n\t" + "mcr p15, 0, v1, c2, c0, 0 \n\t" + /* set DACR */ + "ldr v1, =0x55555555 \n\t" + "mcr p15, 0, v1, c3, c0, 0 \n\t" + /* invalidate TLB */ + "mov v1, #0 \n\t" + "mcr p15, 0, v1, c8, c7, 0 \n\t" + /* enable AFE */ + "mrc p15, 0, v1, c1, c0, 0 \n\t" + "orr v1, v1, #(1 << 29) \n\t" + "mcr p15, 0, v1, c1, c0, 0 \n\t" + : : : "v1" + ); +} + +void mmu_enable() { + asm volatile ( + /* invalidate TLB */ + "mov v1, #0 \n\t" + "mcr p15, 0, v1, c8, c7, 0 \n\t" + /* enable MMU */ + "mrc p15, 0, v1, c1, c0, 0 \n\t" + "orr v1, v1, #1 \n\t" + "mcr p15, 0, v1, c1, c0, 0 \n\t" + : : : "v1" + ); +} + +void mmu_disable() { + asm volatile ( + /* disable MMU */ + "mrc p15, 0, v1, c1, c0, 0 \n\t" + "bic v1, v1, #1 \n\t" + "mcr p15, 0, v1, c1, c0, 0 \n\t" + : : : "v1" + ); +} + +/* map physical memory to virtual memory */ +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; + uint_t i, pte_idx, pde_idx; + + phys_a = (uintptr_t)phys; + virt_a = (uintptr_t)virt; + + if (npages == 0 || phys_a & (PAGE_SIZE - 1) || virt_a & (PAGE_SIZE - 1)) + return -EINVAL; + + switch (perms) { + case MMU_AP_RW_RW: + /* AP[2:0] = 011 */ + pte_perms = PT_AP0 | PT_AP1; + break; + case MMU_AP_RW_RO: + /* AP[2:0] = 010 */ + pte_perms = PT_AP1; + break; + case MMU_AP_RO_RO: + /* AP[2:0] = 111 */ + pte_perms = PT_AP2 | PT_AP1 | PT_AP0; + break; + case MMU_AP_RW_NONE: + /* AP[2:0] = 001 */ + pte_perms = PT_AP0; + break; + case MMU_AP_RO_NONE: + /* AP[2:0] = 101 */ + pte_perms = PT_AP2 | PT_AP0; + break; + case MMU_AP_NONE_NONE: + /* AP[2:0] = 000 */ + pte_perms = 0; + break; + default: + return -EINVAL; + } + + for (i = 0; i < npages; i++) { + pde_idx = virt_a >> 20; + + if (!mmu_ttb[pde_idx]) { + int j; + mmu_ttb[pde_idx] = (u32)(&l2[pde_idx]) | L1_PAGE_TABLE; + for (j = 0; j < 256; j++) + l2[pde_idx][j] = L2_PAGE_FAULT; + } + + pde = (u32*)(mmu_ttb[pde_idx] & ~0x3ff); + pte_idx = (virt_a & 0xff000) >> 12; + pte = (phys_a & 0xfffff000) | L2_SMALL_PAGE; + pde[pte_idx] = pte | pte_perms; + + phys_a += PAGE_SIZE; + virt_a += PAGE_SIZE; + } + + return 0; +} diff --git a/kernel/rs232.c b/kernel/rs232.c @@ -1,8 +1,9 @@ #include <inttypes.h> #include <rs232.h> #include <uart.h> +#include <io.h> -static volatile struct uart *uart = (struct uart*)0x48020000; +static struct uart *uart = (struct uart*)0x48020000; int rs232_puts(const char *s) { int ret = 0; @@ -17,14 +18,14 @@ int rs232_puts(const char *s) { int rs232_putchar(int c) { if (c == '\n') { - while ((uart->lsr & TX_FIFO_E) == 0) + while ((readl(&uart->lsr) & TX_FIFO_E) == 0) ; - uart->thr = (u32)'\r'; + writel('\r', &uart->thr); } /* while UART TX FIFO is not empty */ - while ((uart->lsr & TX_FIFO_E) == 0) + while ((readl(&uart->lsr) & TX_FIFO_E) == 0) ; /* write the character */ - uart->thr = (u32)c; + writel(c, &uart->thr); return c; } diff --git a/kernel/start.S b/kernel/start.S @@ -17,6 +17,12 @@ _start: /* initialize vector table */ bl init_vector_table + /* disable caches for now */ + mrc p15, 0, r0, c1, c0, 0 + bic r0, r0, #(1 << 12) @ set I flag to 0 (disable instruction cache) + bic r0, r0, #(1 << 2) @ set C flag to 0 (disable data cache) + mcr p15, 0, r0, c1, c0, 0 + cps #CPS_FIQ @ change to FIQ mode ldr sp, =_fiq_stack_top @ set FIQ stack @@ -26,6 +32,8 @@ _start: cps #CPS_SVC @ change to SVC mode cpsie if @ enable interrupts + bl mm_init + /* example of calling a system call with at least 6 arguments */ mov r0, #1 mov r1, #2 @@ -37,7 +45,7 @@ _start: /* example of calling a system call with more than 6 arguments */ mov r0, #7 - stmfd sp!, { r0 } + str r0, [sp, #-4]! mov r0, #1 mov r1, #2 mov r2, #3 diff --git a/usbboot/usbboot.ld b/usbboot/usbboot.ld @@ -3,7 +3,7 @@ OUTPUT_ARCH(arm) ENTRY(_start) SECTIONS { - _image_load_addr = 0x80008000; + _image_load_addr = 0x80000000; .text 0x82000000 : {