├── LICENSE ├── build ├── Makefile └── linker.ld ├── include ├── common │ └── stdlib.h └── kernel │ ├── atag.h │ ├── chars_pixels.h │ ├── emmc.h │ ├── framebuffer.h │ ├── gpu.h │ ├── interrupts.h │ ├── kerio.h │ ├── list.h │ ├── mailbox.h │ ├── mem.h │ ├── mutex.h │ ├── peripheral.h │ ├── process.h │ ├── spinlock.h │ ├── timer.h │ └── uart.h ├── src ├── common │ └── stdlib.c └── kernel │ ├── atag.c │ ├── boot.S │ ├── gpu.c │ ├── interrupt_vector.S │ ├── interrupts.c │ ├── kerio.c │ ├── kernel.c │ ├── lock_asm.S │ ├── mailbox.c │ ├── mem.c │ ├── model1 │ └── framebuffer.c │ ├── model2 │ └── framebuffer.c │ ├── process.c │ ├── process_asm.S │ ├── timer.c │ └── uart.c └── steps.txt /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 jsandler18 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /build/Makefile: -------------------------------------------------------------------------------- 1 | # Don't use normal gcc, use the arm cross compiler 2 | TOOLCHAIN = ./gcc-arm-none-eabi-6-2017-q2-update/bin/arm-none-eabi 3 | CC = $(TOOLCHAIN)-gcc 4 | OBJCOPY = $(TOOLCHAIN)-objcopy 5 | GDB = $(TOOLCHAIN)-gdb 6 | 7 | 8 | # Set any constants based on the raspberry pi model. Version 1 has some differences to 2 and 3 9 | ifeq ($(RASPI_MODEL),1) 10 | CPU = arm1176jzf-s 11 | DIRECTIVES = -D MODEL_1 12 | ARCHDIR = model1 13 | else 14 | CPU = cortex-a7 15 | ARCHDIR = model2 16 | endif 17 | 18 | CFLAGS= -mcpu=$(CPU) -fpic -ffreestanding $(DIRECTIVES) -g 19 | CSRCFLAGS= -O2 -Wall -Wextra 20 | LFLAGS= -ffreestanding -O2 -nostdlib 21 | 22 | # Location of the files 23 | KER_SRC = ../src/kernel 24 | KER_HEAD = ../include 25 | COMMON_SRC = ../src/common 26 | OBJ_DIR = objects 27 | KERSOURCES = $(wildcard $(KER_SRC)/*.c) 28 | KERSOURCES += $(wildcard $(KER_SRC)/$(ARCHDIR)/*.c) 29 | COMMONSOURCES = $(wildcard $(COMMON_SRC)/*.c) 30 | ASMSOURCES = $(wildcard $(KER_SRC)/*.S) 31 | OBJECTS = $(patsubst $(KER_SRC)/%.c, $(OBJ_DIR)/%.o, $(KERSOURCES)) 32 | OBJECTS += $(patsubst $(COMMON_SRC)/%.c, $(OBJ_DIR)/%.o, $(COMMONSOURCES)) 33 | OBJECTS += $(patsubst $(KER_SRC)/%.S, $(OBJ_DIR)/%.o, $(ASMSOURCES)) 34 | HEADERS = $(wildcard $(KER_HEAD)/*.h) 35 | 36 | IMG_NAME=kernel 37 | 38 | build: $(OBJECTS) $(HEADERS) 39 | $(CC) -T linker.ld -o $(IMG_NAME).elf $(LFLAGS) $(OBJECTS) 40 | $(OBJCOPY) $(IMG_NAME).elf -O binary $(IMG_NAME).img 41 | 42 | $(OBJ_DIR)/%.o: $(KER_SRC)/%.c 43 | mkdir -p $(@D) 44 | $(CC) $(CFLAGS) -I$(KER_SRC) -I$(KER_HEAD) -c $< -o $@ $(CSRCFLAGS) 45 | 46 | $(OBJ_DIR)/%.o: $(KER_SRC)/$(ARCHDIR)/%.c 47 | mkdir -p $(@D) 48 | $(CC) $(CFLAGS) -I$(KER_SRC) -I$(KER_HEAD) -c $< -o $@ $(CSRCFLAGS) 49 | 50 | $(OBJ_DIR)/%.o: $(KER_SRC)/%.S 51 | mkdir -p $(@D) 52 | $(CC) $(CFLAGS) -I$(KER_SRC) -c $< -o $@ 53 | 54 | $(OBJ_DIR)/%.o: $(COMMON_SRC)/%.c 55 | mkdir -p $(@D) 56 | $(CC) $(CFLAGS) -I$(KER_SRC) -I$(KER_HEAD) -c $< -o $@ $(CSRCFLAGS) 57 | 58 | clean: 59 | rm -rf $(OBJ_DIR) 60 | rm $(IMG_NAME).elf 61 | rm $(IMG_NAME).img 62 | 63 | run: build 64 | qemu-system-arm -m 128 -no-reboot -M raspi2 -serial stdio -kernel kernel.elf 65 | 66 | dbg: 67 | $(GDB) kernel.elf 68 | dbgrun: build gdbinit 69 | qemu-system-arm -m 128 -no-reboot -M raspi2 -serial stdio -kernel kernel.elf -S -s 70 | 71 | .PHONY: gdbinit 72 | gdbinit: 73 | echo "target remote localhost:1234" > .gdbinit 74 | echo "break kernel_main" >> .gdbinit 75 | -------------------------------------------------------------------------------- /build/linker.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_start) 2 | 3 | SECTIONS 4 | { 5 | /* Starts at LOADER_ADDR. */ 6 | . = 0x8000; 7 | __start = .; 8 | __text_start = .; 9 | .text : 10 | { 11 | KEEP(*(.text.boot)) 12 | *(.text) 13 | } 14 | . = ALIGN(4096); /* align to page size */ 15 | __text_end = .; 16 | 17 | __rodata_start = .; 18 | .rodata : 19 | { 20 | *(.rodata) 21 | } 22 | . = ALIGN(4096); /* align to page size */ 23 | __rodata_end = .; 24 | 25 | __data_start = .; 26 | .data : 27 | { 28 | *(.data) 29 | } 30 | . = ALIGN(4096); /* align to page size */ 31 | __data_end = .; 32 | 33 | __bss_start = .; 34 | .bss : 35 | { 36 | bss = .; 37 | *(.bss) 38 | } 39 | . = ALIGN(4096); /* align to page size */ 40 | __bss_end = .; 41 | __end = .; 42 | } 43 | -------------------------------------------------------------------------------- /include/common/stdlib.h: -------------------------------------------------------------------------------- 1 | #include 2 | #ifndef STDLIB_H 3 | #define STDLIB_H 4 | 5 | #define MIN(x,y) ((x < y ? x : y)) 6 | #define MAX(x,y) ((x < y ? y : x)) 7 | 8 | typedef struct divmod_result { 9 | uint32_t div; 10 | uint32_t mod; 11 | } divmod_t; 12 | 13 | divmod_t divmod(uint32_t dividend, uint32_t divisor); 14 | uint32_t div(uint32_t dividend, uint32_t divisor); 15 | void memcpy(void * dest, const void * src, int bytes); 16 | 17 | void bzero(void * dest, int bytes); 18 | void memset(void * dest, uint8_t c, int bytes); 19 | 20 | char * itoa(int i, int base); 21 | int atoi(char * num); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /include/kernel/atag.h: -------------------------------------------------------------------------------- 1 | #include 2 | #ifndef ATAG_H 3 | #define ATAG_H 4 | typedef enum { 5 | NONE = 0x00000000, 6 | CORE = 0x54410001, 7 | MEM = 0x54410002, 8 | INITRD2 = 0x54420005, 9 | CMDLINE = 0x54410009, 10 | } atag_tag_t; 11 | 12 | typedef struct { 13 | uint32_t size; 14 | uint32_t start; 15 | } mem_t; 16 | 17 | typedef struct { 18 | uint32_t start; 19 | uint32_t size; 20 | } initrd2_t; 21 | 22 | typedef struct { 23 | char line[1]; 24 | } cmdline_t; 25 | 26 | typedef struct atag { 27 | uint32_t tag_size; 28 | atag_tag_t tag; 29 | union { 30 | mem_t mem; 31 | initrd2_t initrd2; 32 | cmdline_t cmdline; 33 | }; 34 | } atag_t; 35 | 36 | uint32_t get_mem_size(atag_t * atags); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /include/kernel/chars_pixels.h: -------------------------------------------------------------------------------- 1 | #include 2 | #ifndef CHAR_BMPS_H 3 | #define CHAR_BMPS_H 4 | /* From https://github.com/dhepper/font8x8/blob/master/font8x8_block.h */ 5 | const uint8_t * font(int c) { 6 | static const char f[128][8] = { 7 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0000 (nul) 8 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0001 9 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0002 10 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0003 11 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0004 12 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0005 13 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0006 14 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0007 15 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0008 16 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0009 17 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000A 18 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000B 19 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000C 20 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000D 21 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000E 22 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000F 23 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0010 24 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0011 25 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0012 26 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0013 27 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0014 28 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0015 29 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0016 30 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0017 31 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0018 32 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0019 33 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001A 34 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001B 35 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001C 36 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001D 37 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001E 38 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001F 39 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0020 (space) 40 | { 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00}, // U+0021 (!) 41 | { 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0022 (") 42 | { 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00}, // U+0023 (#) 43 | { 0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00}, // U+0024 ($) 44 | { 0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00}, // U+0025 (%) 45 | { 0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00}, // U+0026 (&) 46 | { 0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0027 (') 47 | { 0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00}, // U+0028 (() 48 | { 0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00}, // U+0029 ()) 49 | { 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00}, // U+002A (*) 50 | { 0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00}, // U+002B (+) 51 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+002C (,) 52 | { 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00}, // U+002D (-) 53 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+002E (.) 54 | { 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00}, // U+002F (/) 55 | { 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00}, // U+0030 (0) 56 | { 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00}, // U+0031 (1) 57 | { 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00}, // U+0032 (2) 58 | { 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00}, // U+0033 (3) 59 | { 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00}, // U+0034 (4) 60 | { 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00}, // U+0035 (5) 61 | { 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00}, // U+0036 (6) 62 | { 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00}, // U+0037 (7) 63 | { 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+0038 (8) 64 | { 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00}, // U+0039 (9) 65 | { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+003A (:) 66 | { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+003B (//) 67 | { 0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00}, // U+003C (<) 68 | { 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00}, // U+003D (=) 69 | { 0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00}, // U+003E (>) 70 | { 0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00}, // U+003F (?) 71 | { 0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00}, // U+0040 (@) 72 | { 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00}, // U+0041 (A) 73 | { 0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00}, // U+0042 (B) 74 | { 0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00}, // U+0043 (C) 75 | { 0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00}, // U+0044 (D) 76 | { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00}, // U+0045 (E) 77 | { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00}, // U+0046 (F) 78 | { 0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00}, // U+0047 (G) 79 | { 0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00}, // U+0048 (H) 80 | { 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0049 (I) 81 | { 0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00}, // U+004A (J) 82 | { 0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00}, // U+004B (K) 83 | { 0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00}, // U+004C (L) 84 | { 0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00}, // U+004D (M) 85 | { 0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00}, // U+004E (N) 86 | { 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00}, // U+004F (O) 87 | { 0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00}, // U+0050 (P) 88 | { 0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00}, // U+0051 (Q) 89 | { 0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00}, // U+0052 (R) 90 | { 0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00}, // U+0053 (S) 91 | { 0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0054 (T) 92 | { 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00}, // U+0055 (U) 93 | { 0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0056 (V) 94 | { 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00}, // U+0057 (W) 95 | { 0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00}, // U+0058 (X) 96 | { 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00}, // U+0059 (Y) 97 | { 0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00}, // U+005A (Z) 98 | { 0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x00}, // U+005B ([) 99 | { 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00}, // U+005C (\) 100 | { 0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00}, // U+005D (]) 101 | { 0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00}, // U+005E (^) 102 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}, // U+005F (_) 103 | { 0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0060 (`) 104 | { 0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00}, // U+0061 (a) 105 | { 0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00}, // U+0062 (b) 106 | { 0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00}, // U+0063 (c) 107 | { 0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6E, 0x00}, // U+0064 (d) 108 | { 0x00, 0x00, 0x1E, 0x33, 0x3f, 0x03, 0x1E, 0x00}, // U+0065 (e) 109 | { 0x1C, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0F, 0x00}, // U+0066 (f) 110 | { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0067 (g) 111 | { 0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00}, // U+0068 (h) 112 | { 0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0069 (i) 113 | { 0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E}, // U+006A (j) 114 | { 0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00}, // U+006B (k) 115 | { 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+006C (l) 116 | { 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00}, // U+006D (m) 117 | { 0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00}, // U+006E (n) 118 | { 0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00}, // U+006F (o) 119 | { 0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F}, // U+0070 (p) 120 | { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78}, // U+0071 (q) 121 | { 0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00}, // U+0072 (r) 122 | { 0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00}, // U+0073 (s) 123 | { 0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00}, // U+0074 (t) 124 | { 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00}, // U+0075 (u) 125 | { 0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0076 (v) 126 | { 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00}, // U+0077 (w) 127 | { 0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00}, // U+0078 (x) 128 | { 0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0079 (y) 129 | { 0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00}, // U+007A (z) 130 | { 0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00}, // U+007B ({) 131 | { 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00}, // U+007C (|) 132 | { 0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00}, // U+007D (}) 133 | { 0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+007E (~) 134 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // U+007F 135 | }; 136 | return (uint8_t *)f[c]; 137 | } 138 | #endif 139 | -------------------------------------------------------------------------------- /include/kernel/emmc.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #ifndef EMMC_H 4 | #define EMMC_H 5 | 6 | #define EMMC_BASE (PERIPHERAL_BASE + EMMC_OFFSET) 7 | 8 | #define AUTO_NO_COMMAND 0 9 | #define AUTO_COMMAND_CMD12 1 10 | #define AUTO_COMMAND_CMD23 2 11 | #define AUTO_RESERVED 3 12 | #define DIRECTION_HOST_TO_CARD 0 13 | #define DIRECTION_CARD_TO_HOST 1 14 | #define RESPONSE_NO_RESPONSE 0 15 | #define RESPONSE_136_BIT_RESPONSE 1 16 | #define RESPONSE_48_BIT_RESPONSE 2 17 | #define RESPONSE_48_BIT_BUSY_RESPONSE 3 18 | #define COMMAND_NORMAL 0 19 | #define COMMAND_SUSPEND_CURRENT 1 20 | #define COMMAND_RESUME_LAST 2 21 | #define COMMAND_ABORT_CURRENT 3 22 | #define SD_CLOCK_GEN_DIVIDED 0 23 | #define SD_CLOCK_GEN_PROGRAMMABLE 1 24 | #define DATA_TIMEOUT_EXPONENT_DISABLED 15 25 | #define SD_SPEED_SDR12 0 26 | #define SD_SPEED_SDR25 1 27 | #define SD_SPEED_SDR50 2 28 | #define SD_SPEED_SDR104 3 29 | #define SD_SPEED_DDR50 4 30 | 31 | 32 | typedef struct { 33 | uint32_t block_size: 10; 34 | uint32_t reserved: 6; 35 | uint32_t block_count: 16; 36 | } block_size_count_t; 37 | 38 | 39 | typedef struct { 40 | uint32_t reserved0: 1; 41 | uint32_t enable_block_counter: 1; 42 | uint32_t auto_command: 2; 43 | uint32_t transfer_direction: 1; 44 | uint32_t transfer_multi_block: 1; 45 | uint32_t reserved1: 10; 46 | uint32_t response_type: 2; 47 | uint32_t reserved2: 1; 48 | uint32_t check_response_crc: 1; 49 | uint32_t check_response_index: 1; 50 | uint32_t command_has_data: 1; 51 | uint32_t command_type: 2; 52 | uint32_t command_index: 6; 53 | uint32_t reserved3: 2; 54 | } command_transfer_mode_t; 55 | 56 | typedef struct { 57 | uint32_t reserved0: 1; 58 | uint32_t use_4_data_lines: 1; 59 | uint32_t high_speed_mode: 1; 60 | uint32_t reserved1: 2; 61 | uint32_t use_8_data_lines: 1; 62 | uint32_t reserved2: 10; 63 | uint32_t stop_next_gap: 1; 64 | uint32_t restart_from_gap: 1; 65 | uint32_t use_dat2_readwait: 1; 66 | uint32_t enable_sdio_interrupt: 1; 67 | uint32_t enable_spi_mode: 1; 68 | uint32_t start_boot_access_mode: 1; 69 | uint32_t enable_alt_boot_access_mode: 1; 70 | uint32_t reserved3: 9; 71 | } control_0_t; 72 | 73 | typedef struct { 74 | uint32_t emmc_clock_enable: 1; 75 | uint32_t sd_clock_stable: 1; 76 | uint32_t sd_clock_enable: 1; 77 | uint32_t reserved0: 2; 78 | uint32_t sd_clock_generation_mode: 1; 79 | uint32_t sd_clock_base_divider_most_sig_bits: 2; 80 | uint32_t sd_clock_base_divider_least_sig_bits: 8; 81 | uint32_t data_timeout_exponent: 4; 82 | uint32_t reserved1: 4; 83 | uint32_t reset_complete_host_circuit: 1; 84 | uint32_t reset_command_handle_circuit: 1; 85 | uint32_t reset_data_handle_circuit: 1; 86 | uint32_t reserved2: 5; 87 | } control_1_t; 88 | 89 | typedef struct { 90 | uint32_t auto_command_not_executed: 1; 91 | uint32_t auto_command_timeout: 1; 92 | uint32_t auto_command_crc_error: 1; 93 | uint32_t auto_command_line_end_error: 1; 94 | uint32_t auto_command_index_error: 1; 95 | uint32_t reserved0: 2; 96 | uint32_t auto_command_cmd12_error: 1; 97 | uint32_t reserved1: 8; 98 | uint32_t sd_speed_mode: 3; 99 | uint32_t reserved2: 3; 100 | uint32_t start_sd_clock_tuning: 1; 101 | uint32_t tuned_clock_used_for_sampling: 1; 102 | uint32_t reserved3: 8; 103 | } control_2_t; 104 | 105 | typedef struct { 106 | uint32_t command_done: 1; 107 | uint32_t data_transfer_done: 1; 108 | uint32_t transfer_stopped_at_block_gap: 1; 109 | uint32_t reserved0: 1; 110 | uint32_t can_write: 1; 111 | uint32_t can_read: 1; 112 | uint32_t reserved1: 2; 113 | uint32_t card_made_irq: 1; 114 | uint32_t reserved2: 3; 115 | uint32_t clock_retune_request: 1; 116 | uint32_t boot_ack_received: 1; 117 | uint32_t boot_ended: 1; 118 | uint32_t error: 1; 119 | uint32_t command_line_timeout: 1; 120 | uint32_t command_crc_error: 1; 121 | uint32_t command_line_end_error: 1; 122 | uint32_t incorrect_response_index: 1; 123 | uint32_t data_line_timeout: 1; 124 | uint32_t data_crc_error: 1; 125 | uint32_t data_line_end_error: 1; 126 | uint32_t reserved3: 1; 127 | uint32_t auto_command_error: 1; 128 | uint32_t reserved4: 7; 129 | } emmc_interrupts_t; 130 | 131 | typedef struct { 132 | uint32_t arg2; 133 | block_size_count_t block_size_count; 134 | uint32_t arg1; 135 | command_transfer_mode_t command_transfer_mode; 136 | uint32_t response_0; 137 | uint32_t response_1; 138 | uint32_t response_2; 139 | uint32_t response_3; 140 | uint32_t data; 141 | uint32_t debug; 142 | control_0_t control_0; 143 | control_1_t control_1; 144 | emmc_interrupts_t interrupts_occured_flags; 145 | emmc_interrupts_t enable_set_flags; 146 | emmc_interrupts_t enable_irq; 147 | control_2_t control_2; 148 | emmc_interrupts_t fake_interrupt; 149 | uint32_t boot_mode_timeout; 150 | uint32_t debug_select; 151 | uint32_t extended_read_fifo_config; 152 | uint32_t extended_read_fifo_enable; 153 | uint32_t sd_clock_tune_step; 154 | uint32_t sd_clock_tune_sdr_delay; 155 | uint32_t sd_clock_tune_ddr_delay; 156 | uint32_t spi_interrupt_enable; 157 | uint32_t version_info; 158 | } emmc_registers_t; 159 | 160 | #endif 161 | -------------------------------------------------------------------------------- /include/kernel/framebuffer.h: -------------------------------------------------------------------------------- 1 | #include 2 | #ifndef FRAMEBUFFER_H 3 | #define FRAMEBUFFER_H 4 | 5 | #define COLORDEPTH 24 6 | #define BYTES_PER_PIXEL COLORDEPTH/8 7 | 8 | typedef struct framebuffer_info { 9 | uint32_t width; 10 | uint32_t height; 11 | uint32_t pitch; 12 | void * buf; 13 | uint32_t buf_size; 14 | uint32_t chars_width; 15 | uint32_t chars_height; 16 | uint32_t chars_x; 17 | uint32_t chars_y; 18 | } framebuffer_info_t; 19 | 20 | framebuffer_info_t fbinfo; 21 | 22 | int framebuffer_init(void); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /include/kernel/gpu.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #ifndef GPU_H 4 | #define GPU_H 5 | 6 | #define CHAR_WIDTH 8 7 | #define CHAR_HEIGHT 8 8 | 9 | typedef struct pixel { 10 | uint8_t red; 11 | uint8_t green; 12 | uint8_t blue; 13 | } pixel_t; 14 | 15 | void gpu_init(void); 16 | 17 | void write_pixel(uint32_t x, uint32_t y, const pixel_t * pixel); 18 | 19 | void gpu_putc(char c); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /include/kernel/interrupts.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #ifndef INTERRUPTS_H 4 | #define INTERRUPTS_H 5 | 6 | #define INTERRUPTS_BASE (PERIPHERAL_BASE + INTERRUPTS_OFFSET) 7 | #define INTERRUPTS_PENDING (INTERRUPTS_BASE + 0x200) 8 | 9 | #define IRQ_IS_BASIC(x) ((x >= 64 )) 10 | #define IRQ_IS_GPU2(x) ((x >= 32 && x < 64 )) 11 | #define IRQ_IS_GPU1(x) ((x < 32 )) 12 | #define IRQ_IS_PENDING(regs, num) ((IRQ_IS_BASIC(num) && ((1 << (num-64)) & regs->irq_basic_pending)) || (IRQ_IS_GPU2(num) && ((1 << (num-32)) & regs->irq_gpu_pending2)) || (IRQ_IS_GPU1(num) && ((1 << (num)) & regs->irq_gpu_pending1))) 13 | #define NUM_IRQS 72 14 | 15 | __inline__ int INTERRUPTS_ENABLED(void) { 16 | int res; 17 | __asm__ __volatile__("mrs %[res], CPSR": [res] "=r" (res)::); 18 | return ((res >> 7) & 1) == 0; 19 | } 20 | 21 | __inline__ void ENABLE_INTERRUPTS(void) { 22 | if (!INTERRUPTS_ENABLED()) { 23 | __asm__ __volatile__("cpsie i"); 24 | } 25 | } 26 | 27 | __inline__ void DISABLE_INTERRUPTS(void) { 28 | if (INTERRUPTS_ENABLED()) { 29 | __asm__ __volatile__("cpsid i"); 30 | } 31 | } 32 | 33 | typedef void (*interrupt_handler_f)(void); 34 | typedef void (*interrupt_clearer_f)(void); 35 | 36 | 37 | typedef enum { 38 | SYSTEM_TIMER_1 = 1, 39 | USB_CONTROLER = 9, 40 | ARM_TIMER = 64 41 | } irq_number_t; 42 | 43 | typedef struct { 44 | uint32_t irq_basic_pending; 45 | uint32_t irq_gpu_pending1; 46 | uint32_t irq_gpu_pending2; 47 | uint32_t fiq_control; 48 | uint32_t irq_gpu_enable1; 49 | uint32_t irq_gpu_enable2; 50 | uint32_t irq_basic_enable; 51 | uint32_t irq_gpu_disable1; 52 | uint32_t irq_gpu_disable2; 53 | uint32_t irq_basic_disable; 54 | } interrupt_registers_t; 55 | 56 | void interrupts_init(void); 57 | 58 | void register_irq_handler(irq_number_t irq_num, interrupt_handler_f handler, interrupt_clearer_f clearer); 59 | void unregister_irq_handler(irq_number_t irq_num); 60 | 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /include/kernel/kerio.h: -------------------------------------------------------------------------------- 1 | #ifndef STDIO_H 2 | #define STDIO_H 3 | 4 | char getc(void); 5 | void putc(char c); 6 | 7 | void puts(const char * s); 8 | 9 | // This version of gets copies until newline, replacing newline with null char, or until buflen. 10 | // whichever comes first 11 | void gets(char * buf, int buflen); 12 | 13 | void printf(const char * fmt, ...); 14 | 15 | #endif 16 | 17 | -------------------------------------------------------------------------------- /include/kernel/list.h: -------------------------------------------------------------------------------- 1 | /** 2 | * This implimentation of a list is done in macros so it may be used generically for any struct. 3 | * 4 | * To use, call DEFINE_LIST(typename) in the header file where you would want to define the struct for the list. typename should be the NON-TYPEDEFED name of the struct 5 | * without the keyword "struct". If I wand to define a list of type "struct point", I would need to do "DEFINE_LIST(point)". This will create the list type called 6 | * "typename_list_t", which may be reused as many times as you want. 7 | * 8 | * Next, you must call DEFINE_LINK(typename) inside the body of the struct. 9 | * 10 | * Finally, call IMPLEMENT_LIST(typename) where you would want the list to be implimented 11 | * 12 | * Before using an instance of the list, you should call INITIALIZE_LIST(list) to correctly it up 13 | * 14 | * 15 | * IMPLEMENT_LIST defines the following functions: 16 | * void append_nodeType_list(nodeType_list_t * list, struct nodeType *) 17 | * appends to the back of the list 18 | * 19 | * void push_nodeType_list(nodeType_list_t * list, struct nodeType *) 20 | * adds to the front of the list 21 | * 22 | * struct nodeType * peek_nodeType_list(nodeType_list_t * list) 23 | * gets the first element from the list without removing it 24 | * 25 | * struct nodeType * pop_nodeType_list(nodeType_list_t * list) 26 | * gets the first element from the list and removes it 27 | * 28 | * uint32_t size_nodeType_list(nodeType_list_t * list) 29 | * returns the number of elements in the list 30 | * 31 | * struct nodeType * next_nodeType_list(struct nodeType * node) 32 | * gets the next node in the list, null if none left 33 | * 34 | * void remove_nodeType(nodeType_list_t * list, struct nodeType * node) 35 | * removes the given element from the list 36 | */ 37 | #include 38 | #include 39 | #include 40 | #ifndef LIST_H 41 | #define LIST_H 42 | 43 | #define DEFINE_LIST(nodeType) \ 44 | struct nodeType; \ 45 | typedef struct nodeType##list { \ 46 | struct nodeType * head; \ 47 | struct nodeType * tail; \ 48 | spin_lock_t lock;\ 49 | uint32_t size;\ 50 | } nodeType##_list_t; 51 | 52 | #define DEFINE_LINK(nodeType) \ 53 | struct nodeType * next##nodeType;\ 54 | struct nodeType * prev##nodeType;\ 55 | nodeType##_list_t * container; 56 | 57 | #define INITIALIZE_LIST(list) \ 58 | list.head = list.tail = (void *)0;\ 59 | list.size = 0;\ 60 | spin_init(&list.lock); 61 | 62 | 63 | #define IMPLEMENT_LIST(nodeType) \ 64 | void append_##nodeType##_list(nodeType##_list_t * list, struct nodeType * node) { \ 65 | spin_lock(&list->lock); \ 66 | list->tail->next##nodeType = node; \ 67 | node->prev##nodeType = list->tail; \ 68 | list->tail = node; \ 69 | node->next##nodeType = NULL; \ 70 | list->size += 1; \ 71 | if (list->head == NULL) { \ 72 | list->head = node; \ 73 | } \ 74 | node->container = list; \ 75 | spin_unlock(&list->lock); \ 76 | } \ 77 | \ 78 | void push_##nodeType##_list(nodeType##_list_t * list, struct nodeType * node) { \ 79 | spin_lock(&list->lock); \ 80 | node->next##nodeType = list->head; \ 81 | node->prev##nodeType = NULL; \ 82 | list->head = node; \ 83 | list->size += 1; \ 84 | if (list->tail == NULL) { \ 85 | list->tail = node; \ 86 | } \ 87 | node->container = list; \ 88 | spin_unlock(&list->lock); \ 89 | } \ 90 | \ 91 | struct nodeType * peek_##nodeType##_list(nodeType##_list_t * list) { \ 92 | return list->head; \ 93 | } \ 94 | \ 95 | struct nodeType * pop_##nodeType##_list(nodeType##_list_t * list) { \ 96 | spin_lock(&list->lock); \ 97 | struct nodeType * res = list->head; \ 98 | list->head = list->head->next##nodeType; \ 99 | list->head->prev##nodeType = NULL; \ 100 | list->size -= 1; \ 101 | if (list->head == NULL) { \ 102 | list->tail = NULL; \ 103 | } \ 104 | res->container = NULL; \ 105 | spin_unlock(&list->lock); \ 106 | return res; \ 107 | } \ 108 | \ 109 | uint32_t size_##nodeType##_list(nodeType##_list_t * list) { \ 110 | return list->size; \ 111 | } \ 112 | \ 113 | struct nodeType * next_##nodeType##_list(struct nodeType * node) { \ 114 | return node->next##nodeType; \ 115 | } \ 116 | \ 117 | void remove_##nodeType (nodeType##_list_t * list, struct nodeType * node) { \ 118 | spin_lock(&list->lock); \ 119 | if (node->container == list) { \ 120 | if (node->prev##nodeType == NULL) { \ 121 | list->head = node->next##nodeType; \ 122 | } else { \ 123 | node->prev##nodeType = node->next##nodeType; \ 124 | } \ 125 | if (node->next##nodeType == NULL) { \ 126 | list->tail = node->prev##nodeType; \ 127 | } else { \ 128 | node->next##nodeType = node->prev##nodeType; \ 129 | } \ 130 | } \ 131 | node->container = NULL; \ 132 | spin_unlock(&list->lock); \ 133 | } \ 134 | 135 | 136 | #endif 137 | -------------------------------------------------------------------------------- /include/kernel/mailbox.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #ifndef MAILBOX_H 5 | #define MAILBOX_H 6 | 7 | #define MAILBOX_BASE PERIPHERAL_BASE + MAILBOX_OFFSET 8 | #define MAIL0_READ (((mail_message_t *)(0x00 + MAILBOX_BASE))) 9 | #define MAIL0_STATUS (((mail_status_t *)(0x18 + MAILBOX_BASE))) 10 | #define MAIL0_WRITE (((mail_message_t *)(0x20 + MAILBOX_BASE))) 11 | #define PROPERTY_CHANNEL 8 12 | #define FRAMEBUFFER_CHANNEL 1 13 | 14 | typedef struct { 15 | uint8_t channel: 4; 16 | uint32_t data: 28; 17 | } mail_message_t; 18 | 19 | typedef struct { 20 | uint32_t reserved: 30; 21 | uint8_t empty: 1; 22 | uint8_t full:1; 23 | } mail_status_t; 24 | 25 | mail_message_t mailbox_read(int channel); 26 | void mailbox_send(mail_message_t msg, int channel); 27 | 28 | /** 29 | * A property message can either be a request, or a response, and a response can be successfull or an error 30 | */ 31 | typedef enum { 32 | REQUEST = 0x00000000, 33 | RESPONSE_SUCCESS = 0x80000000, 34 | RESPONSE_ERROR = 0x80000001 35 | } buffer_req_res_code_t; 36 | 37 | 38 | /* 39 | * A buffer that holds many property messages. 40 | * The last tag must be a 4 byte zero, and then padding to make the whole thing 4 byte aligned 41 | */ 42 | typedef struct { 43 | uint32_t size; // Size includes the size itself 44 | buffer_req_res_code_t req_res_code; 45 | uint32_t tags[1]; // A concatenated sequence of tags. will use overrun to make large enough 46 | } property_message_buffer_t; 47 | 48 | 49 | /** 50 | * A message is identified by a tag. These are some of the possible tags 51 | */ 52 | typedef enum { 53 | NULL_TAG = 0, 54 | FB_ALLOCATE_BUFFER = 0x00040001, 55 | FB_RELESE_BUFFER = 0x00048001, 56 | FB_GET_PHYSICAL_DIMENSIONS = 0x00040003, 57 | FB_SET_PHYSICAL_DIMENSIONS = 0x00048003, 58 | FB_GET_VIRTUAL_DIMENSIONS = 0x00040004, 59 | FB_SET_VIRTUAL_DIMENSIONS = 0x00048004, 60 | FB_GET_BITS_PER_PIXEL = 0x00040005, 61 | FB_SET_BITS_PER_PIXEL = 0x00048005, 62 | FB_GET_BYTES_PER_ROW = 0x00040008 63 | } property_tag_t; 64 | 65 | /** 66 | * For each possible tag, we create a struct corresponding to the request value buffer, and the response value buffer 67 | */ 68 | 69 | typedef struct { 70 | void * fb_addr; 71 | uint32_t fb_size; 72 | } fb_allocate_res_t; 73 | 74 | typedef struct { 75 | uint32_t width; 76 | uint32_t height; 77 | } fb_screen_size_t; 78 | 79 | 80 | /* 81 | * The value buffer can be any one of these types 82 | */ 83 | typedef union { 84 | uint32_t fb_allocate_align; 85 | fb_allocate_res_t fb_allocate_res; 86 | fb_screen_size_t fb_screen_size; 87 | uint32_t fb_bits_per_pixel; 88 | uint32_t fb_bytes_per_row; 89 | } value_buffer_t; 90 | 91 | /* 92 | * A message_buffer can contain any number of these 93 | */ 94 | typedef struct { 95 | property_tag_t proptag; 96 | value_buffer_t value_buffer; 97 | } property_message_tag_t; 98 | 99 | 100 | /** 101 | * given an array of tags, will send all of the tags given, and will populate that array with the responses. 102 | * the given array should end with a "null tag" with the proptag field set to 0. 103 | * returns 0 on success 104 | */ 105 | int send_messages(property_message_tag_t * tags); 106 | 107 | #endif 108 | -------------------------------------------------------------------------------- /include/kernel/mem.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #ifndef MEM_H 5 | #define MEM_H 6 | 7 | #define PAGE_SIZE 4096 8 | #define KERNEL_HEAP_SIZE (1024*1024) 9 | #define KERNEL_STACK_SIZE PAGE_SIZE 10 | 11 | typedef struct { 12 | uint8_t allocated: 1; // This page is allocated to something 13 | uint8_t kernel_page: 1; // This page is a part of the kernel 14 | uint8_t kernel_heap_page: 1; // This page is a part of the kernel 15 | uint32_t reserved: 29; 16 | } page_flags_t; 17 | 18 | DEFINE_LIST(page); 19 | 20 | typedef struct page { 21 | uint32_t vaddr_mapped; // The virtual address that maps to this page 22 | page_flags_t flags; 23 | DEFINE_LINK(page); 24 | } page_t; 25 | 26 | 27 | void mem_init(atag_t * atags); 28 | 29 | void * alloc_page(void); 30 | void free_page(void * ptr); 31 | 32 | void * kmalloc(uint32_t bytes); 33 | void kfree(void *ptr); 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /include/kernel/mutex.h: -------------------------------------------------------------------------------- 1 | #include 2 | #ifndef MUTEX_H 3 | #define MUTEX_H 4 | 5 | typedef struct { 6 | int lock; 7 | process_control_block_t * locker; 8 | pcb_list_t wait_queue; 9 | } mutex_t; 10 | 11 | void mutex_init(mutex_t * lock); 12 | void mutex_lock(mutex_t * lock); 13 | void mutex_unlock(mutex_t * lock); 14 | #endif 15 | -------------------------------------------------------------------------------- /include/kernel/peripheral.h: -------------------------------------------------------------------------------- 1 | #ifndef PERIPHERAL_H 2 | #define PERIPHERAL_H 3 | 4 | 5 | #ifdef MODEL_1 6 | #define PERIPHERAL_BASE 0x20000000 7 | #else 8 | #define PERIPHERAL_BASE 0x3F000000 9 | #endif 10 | 11 | #define PERIPHERAL_LENGTH 0x01000000 12 | 13 | 14 | #define SYSTEM_TIMER_OFFSET 0x3000 15 | #define INTERRUPTS_OFFSET 0xB000 16 | #define MAILBOX_OFFSET 0xB880 17 | #define UART0_OFFSET 0x201000 18 | #define GPIO_OFFSET 0x200000 19 | #define EMMC_OFFSET 0x300000 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /include/kernel/process.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #ifndef PROCESS_H 5 | #define PROCESS_H 6 | 7 | 8 | typedef void (*kthread_function_f)(void); 9 | 10 | typedef struct { 11 | uint32_t r0; 12 | uint32_t r1; 13 | uint32_t r2; 14 | uint32_t r3; 15 | uint32_t r4; 16 | uint32_t r5; 17 | uint32_t r6; 18 | uint32_t r7; 19 | uint32_t r8; 20 | uint32_t r9; 21 | uint32_t r10; 22 | uint32_t r11; 23 | uint32_t cpsr; 24 | uint32_t sp; 25 | uint32_t lr; 26 | } proc_saved_state_t; 27 | 28 | DEFINE_LIST(pcb); 29 | 30 | typedef struct pcb { 31 | proc_saved_state_t * saved_state; // Pointer to where on the stack this process's state is saved. Becomes invalid once the process is running 32 | void * stack_page; // The stack for this proces. The stack starts at the end of this page 33 | uint32_t pid; // The process ID number 34 | DEFINE_LINK(pcb); 35 | char proc_name[20]; // The process's name 36 | } process_control_block_t; 37 | 38 | 39 | 40 | void process_init(void); 41 | 42 | void create_kernel_thread(kthread_function_f thread_func, char * name, int name_len); 43 | void schedule(void); 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /include/kernel/spinlock.h: -------------------------------------------------------------------------------- 1 | #ifndef SPIN_LOCK_H 2 | #define SPIN_LOCK_H 3 | typedef int spin_lock_t; 4 | 5 | void spin_init(spin_lock_t * lock); 6 | void spin_lock(spin_lock_t * lock); 7 | void spin_unlock(spin_lock_t * lock); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /include/kernel/timer.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #ifndef TIMER_H 4 | #define TIMER_H 5 | 6 | #define SYSTEM_TIMER_BASE (SYSTEM_TIMER_OFFSET + PERIPHERAL_BASE) 7 | 8 | void timer_init(void); 9 | 10 | void timer_set(uint32_t usecs); 11 | 12 | void udelay(uint32_t usecs); 13 | 14 | typedef struct { 15 | uint8_t timer0_matched: 1; 16 | uint8_t timer1_matched: 1; 17 | uint8_t timer2_matched: 1; 18 | uint8_t timer3_matched: 1; 19 | uint32_t reserved: 28; 20 | } timer_control_reg_t; 21 | 22 | typedef struct { 23 | timer_control_reg_t control; 24 | uint32_t counter_low; 25 | uint32_t counter_high; 26 | uint32_t timer0; 27 | uint32_t timer1; 28 | uint32_t timer2; 29 | uint32_t timer3; 30 | } timer_registers_t; 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /include/kernel/uart.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #ifndef UART_H 6 | #define UART_H 7 | 8 | 9 | typedef union uart_flags { 10 | struct { 11 | uint8_t clear_to_send: 1; 12 | uint8_t data_set_ready: 1; 13 | uint8_t data_carrier_detected: 1; 14 | uint8_t busy: 1; 15 | uint8_t recieve_queue_empty: 1; 16 | uint8_t transmit_queue_full: 1; 17 | uint8_t recieve_queue_full: 1; 18 | uint8_t transmit_queue_empty: 1; 19 | uint8_t ring_indicator: 1; 20 | uint32_t padding: 23; 21 | }; 22 | uint32_t as_int; 23 | } uart_flags_t; 24 | 25 | typedef union uart_control { 26 | struct { 27 | uint8_t uart_enabled: 1; 28 | uint8_t sir_enabled: 1; 29 | uint8_t sir_low_power_mode: 1; 30 | uint8_t reserved: 4; 31 | uint8_t loop_back_enabled: 1; 32 | uint8_t transmit_enabled: 1; 33 | uint8_t receive_enabled: 1; 34 | uint8_t data_transmit_ready: 1; 35 | uint8_t request_to_send: 1; 36 | uint8_t out1: 1; 37 | uint8_t out2: 1; 38 | uint8_t rts_hardware_flow_control_enabled: 1; 39 | uint8_t cts_hardware_flow_control_enabled: 1; 40 | uint16_t padding; 41 | }; 42 | uint32_t as_int; 43 | } uart_control_t; 44 | 45 | void mmio_write(uint32_t reg, uint32_t data); 46 | 47 | uint32_t mmio_read(uint32_t reg); 48 | 49 | // Loop times in a way that the compiler won't optimize away 50 | void delay(int32_t count); 51 | 52 | enum 53 | { 54 | // The GPIO registers base address. 55 | GPIO_BASE = PERIPHERAL_BASE + GPIO_OFFSET, 56 | // The offsets for reach register. 57 | 58 | // Controls actuation of pull up/down to ALL GPIO pins. 59 | GPPUD = (GPIO_BASE + 0x94), 60 | 61 | // Controls actuation of pull up/down for specific GPIO pin. 62 | GPPUDCLK0 = (GPIO_BASE + 0x98), 63 | 64 | // The base address for UART. 65 | UART0_BASE = PERIPHERAL_BASE + UART0_OFFSET, 66 | 67 | // The offsets for reach register for the UART. 68 | UART0_DR = (UART0_BASE + 0x00), 69 | UART0_RSRECR = (UART0_BASE + 0x04), 70 | UART0_FR = (UART0_BASE + 0x18), 71 | UART0_ILPR = (UART0_BASE + 0x20), 72 | UART0_IBRD = (UART0_BASE + 0x24), 73 | UART0_FBRD = (UART0_BASE + 0x28), 74 | UART0_LCRH = (UART0_BASE + 0x2C), 75 | UART0_CR = (UART0_BASE + 0x30), 76 | UART0_IFLS = (UART0_BASE + 0x34), 77 | UART0_IMSC = (UART0_BASE + 0x38), 78 | UART0_RIS = (UART0_BASE + 0x3C), 79 | UART0_MIS = (UART0_BASE + 0x40), 80 | UART0_ICR = (UART0_BASE + 0x44), 81 | UART0_DMACR = (UART0_BASE + 0x48), 82 | UART0_ITCR = (UART0_BASE + 0x80), 83 | UART0_ITIP = (UART0_BASE + 0x84), 84 | UART0_ITOP = (UART0_BASE + 0x88), 85 | UART0_TDR = (UART0_BASE + 0x8C), 86 | }; 87 | 88 | void uart_init(); 89 | 90 | uart_flags_t read_flags(void); 91 | 92 | void uart_putc(unsigned char c); 93 | 94 | unsigned char uart_getc(); 95 | 96 | void uart_puts(const char* str); 97 | 98 | #endif 99 | -------------------------------------------------------------------------------- /src/common/stdlib.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // raspi model 1 does not have division instruction, so we need to define our own 5 | __inline__ uint32_t div(uint32_t dividend, uint32_t divisor) { 6 | #ifdef MODEL_1 7 | // Use long division, but in binary. Copied from Stack overflow... 8 | uint32_t denom=divisor; 9 | uint32_t current = 1; 10 | uint32_t answer=0; 11 | 12 | if ( denom > dividend) 13 | return 0; 14 | 15 | if ( denom == dividend) 16 | return 1; 17 | 18 | while (denom <= dividend) { 19 | denom <<= 1; 20 | current <<= 1; 21 | } 22 | 23 | denom >>= 1; 24 | current >>= 1; 25 | 26 | while (current!=0) { 27 | if ( dividend >= denom) { 28 | dividend -= denom; 29 | answer |= current; 30 | } 31 | current >>= 1; 32 | denom >>= 1; 33 | } 34 | return answer; 35 | #else 36 | return dividend / divisor; 37 | #endif 38 | } 39 | 40 | __inline__ divmod_t divmod(uint32_t dividend, uint32_t divisor) { 41 | divmod_t res; 42 | #ifdef MODEL_1 43 | res.div = div(dividend, divisor); 44 | res.mod = dividend - res.div*divisor; 45 | #else 46 | res.div = dividend / divisor; 47 | res.mod = dividend % divisor; 48 | #endif 49 | return res; 50 | } 51 | 52 | void memcpy(void * dest, const void * src, int bytes) { 53 | char * d = dest; 54 | const char * s = src; 55 | while (bytes--) { 56 | *d++ = *s++; 57 | } 58 | } 59 | 60 | void bzero(void * dest, int bytes) { 61 | memset(dest, 0, bytes); 62 | } 63 | 64 | void memset(void * dest, uint8_t c, int bytes) { 65 | uint8_t * d = dest; 66 | while (bytes--) { 67 | *d++ = c; 68 | } 69 | } 70 | 71 | char * itoa(int num, int base) { 72 | static char intbuf[33]; 73 | uint32_t j = 0, isneg = 0, i; 74 | divmod_t divmod_res; 75 | 76 | if (num == 0) { 77 | intbuf[0] = '0'; 78 | intbuf[1] = '\0'; 79 | return intbuf; 80 | } 81 | 82 | if (base == 10 && num < 0) { 83 | isneg = 1; 84 | num = -num; 85 | } 86 | 87 | i = (uint32_t) num; 88 | 89 | while (i != 0) { 90 | divmod_res = divmod(i,base); 91 | intbuf[j++] = (divmod_res.mod) < 10 ? '0' + (divmod_res.mod) : 'a' + (divmod_res.mod) - 10; 92 | i = divmod_res.div; 93 | } 94 | 95 | if (isneg) 96 | intbuf[j++] = '-'; 97 | 98 | if (base == 16) { 99 | intbuf[j++] = 'x'; 100 | intbuf[j++] = '0'; 101 | } else if(base == 8) { 102 | intbuf[j++] = '0'; 103 | } else if(base == 2) { 104 | intbuf[j++] = 'b'; 105 | intbuf[j++] = '0'; 106 | } 107 | 108 | intbuf[j] = '\0'; 109 | j--; 110 | i = 0; 111 | while (i < j) { 112 | isneg = intbuf[i]; 113 | intbuf[i] = intbuf[j]; 114 | intbuf[j] = isneg; 115 | i++; 116 | j--; 117 | } 118 | 119 | return intbuf; 120 | } 121 | 122 | int atoi(char * num) { 123 | int res = 0, power = 0, digit, i; 124 | char * start = num; 125 | 126 | // Find the end 127 | while (*num >= '0' && *num <= '9') { 128 | num++; 129 | } 130 | 131 | num--; 132 | 133 | while (num != start) { 134 | digit = *num - '0'; 135 | for (i = 0; i < power; i++) { 136 | digit *= 10; 137 | } 138 | res += digit; 139 | power++; 140 | num--; 141 | } 142 | 143 | return res; 144 | } 145 | -------------------------------------------------------------------------------- /src/kernel/atag.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | uint32_t get_mem_size(atag_t * tag) { 4 | while (tag->tag != NONE) { 5 | if (tag->tag == MEM) { 6 | return tag->mem.size; 7 | } 8 | tag = (atag_t *)(((uint32_t *)tag) + tag->tag_size); 9 | } 10 | return 0; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/kernel/boot.S: -------------------------------------------------------------------------------- 1 | .equ KERNEL_STACK_SIZE, 4096 2 | // To keep this in the first portion of the binary. 3 | .section ".text.boot" 4 | 5 | // Make _start global. 6 | .globl _start 7 | 8 | // Entry point for the kernel. 9 | // r15 -> should begin execution at 0x8000. 10 | // r0 -> 0x00000000 11 | // r1 -> 0x00000C42 12 | // r2 -> 0x00000100 - start of ATAGS 13 | // preserve these registers as argument for kernel_main 14 | _start: 15 | // This set of instructions sets 3 of the 4 cores to go to halt. 16 | // Model 1 only has 1 cpu and does not have this instruction, so don't include it if building for model 1 17 | #ifndef MODEL_1 18 | mrc p15, #0, r1, c0, c0, #5 19 | and r1, r1, #3 20 | cmp r1, #0 21 | bne halt 22 | #endif 23 | // Setup the stack. Stack will be 32 KB above kernel image 24 | ldr sp, =__end 25 | mov r4, #(KERNEL_STACK_SIZE) 26 | add sp, sp, r4 27 | 28 | // Clear out bss. 29 | ldr r4, =__bss_start 30 | ldr r9, =__bss_end 31 | mov r5, #0 32 | mov r6, #0 33 | mov r7, #0 34 | mov r8, #0 35 | b 2f 36 | 37 | 1: 38 | // store multiple at r4. 39 | stmia r4!, {r5-r8} 40 | 41 | // If we are still below bss_end, loop. 42 | 2: 43 | cmp r4, r9 44 | blo 1b 45 | 46 | // Call kernel_main 47 | mov r2, #0x100 48 | bl kernel_main 49 | b halt 50 | 51 | // halt 52 | halt: 53 | wfe 54 | b halt 55 | -------------------------------------------------------------------------------- /src/kernel/gpu.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | 11 | void write_pixel(uint32_t x, uint32_t y, const pixel_t * pix) { 12 | uint8_t * location = fbinfo.buf + y*fbinfo.pitch + x*BYTES_PER_PIXEL; 13 | memcpy(location, pix, BYTES_PER_PIXEL); 14 | } 15 | 16 | void gpu_putc(char c) { 17 | static const pixel_t WHITE = {0xff, 0xff, 0xff}; 18 | static const pixel_t BLACK = {0x00, 0x00, 0x00}; 19 | uint8_t w,h; 20 | uint8_t mask; 21 | const uint8_t * bmp = font(c); 22 | uint32_t i, num_rows = fbinfo.height/CHAR_HEIGHT; 23 | 24 | // shift everything up one row 25 | if (fbinfo.chars_y >= num_rows) { 26 | // Copy a whole character row into the one above it 27 | for (i = 0; i < num_rows-1; i++) 28 | memcpy(fbinfo.buf + fbinfo.pitch*i*CHAR_HEIGHT, fbinfo.buf + fbinfo.pitch*(i+1)*CHAR_HEIGHT, fbinfo.pitch * CHAR_HEIGHT); 29 | // zero out the last row 30 | bzero(fbinfo.buf + fbinfo.pitch*i*CHAR_HEIGHT,fbinfo.pitch * CHAR_HEIGHT); 31 | fbinfo.chars_y--; 32 | } 33 | 34 | if (c == '\n') { 35 | fbinfo.chars_x = 0; 36 | fbinfo.chars_y++; 37 | return; 38 | } 39 | 40 | for(w = 0; w < CHAR_WIDTH; w++) { 41 | for(h = 0; h < CHAR_HEIGHT; h++) { 42 | mask = 1 << (w); 43 | if (bmp[h] & mask) 44 | write_pixel(fbinfo.chars_x*CHAR_WIDTH + w, fbinfo.chars_y*CHAR_HEIGHT + h, &WHITE); 45 | else 46 | write_pixel(fbinfo.chars_x*CHAR_WIDTH + w, fbinfo.chars_y*CHAR_HEIGHT + h, &BLACK); 47 | } 48 | } 49 | 50 | fbinfo.chars_x++; 51 | if (fbinfo.chars_x > fbinfo.chars_width) { 52 | fbinfo.chars_x = 0; 53 | fbinfo.chars_y++; 54 | } 55 | } 56 | 57 | void gpu_init(void) { 58 | static const pixel_t BLACK = {0x00, 0x00, 0x00}; 59 | // Aparantly, this sometimes does not work, so try in a loop 60 | while(framebuffer_init()); 61 | 62 | // clear screen 63 | for (uint32_t j = 0; j < fbinfo.height; j++) { 64 | for (uint32_t i = 0; i < fbinfo.width; i++) { 65 | write_pixel(i,j,&BLACK); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/kernel/interrupt_vector.S: -------------------------------------------------------------------------------- 1 | .section ".text" 2 | 3 | .global move_exception_vector 4 | 5 | exception_vector: 6 | ldr pc, reset_handler_abs_addr 7 | ldr pc, undefined_instruction_handler_abs_addr 8 | ldr pc, software_interrupt_handler_abs_addr 9 | ldr pc, prefetch_abort_handler_abs_addr 10 | ldr pc, data_abort_handler_abs_addr 11 | nop // This one is reserved 12 | ldr pc, irq_handler_abs_addr 13 | ldr pc, fast_irq_handler_abs_addr 14 | 15 | reset_handler_abs_addr: .word reset_handler 16 | undefined_instruction_handler_abs_addr: .word undefined_instruction_handler 17 | software_interrupt_handler_abs_addr: .word software_interrupt_handler 18 | prefetch_abort_handler_abs_addr: .word prefetch_abort_handler 19 | data_abort_handler_abs_addr: .word data_abort_handler 20 | irq_handler_abs_addr: .word irq_handler_asm_wrapper 21 | fast_irq_handler_abs_addr: .word fast_irq_handler 22 | 23 | move_exception_vector: 24 | push {r4, r5, r6, r7, r8, r9} 25 | ldr r0, =exception_vector 26 | mov r1, #0x0000 27 | ldmia r0!,{r2, r3, r4, r5, r6, r7, r8, r9} 28 | stmia r1!,{r2, r3, r4, r5, r6, r7, r8, r9} 29 | ldmia r0!,{r2, r3, r4, r5, r6, r7, r8} 30 | stmia r1!,{r2, r3, r4, r5, r6, r7, r8} 31 | pop {r4, r5, r6, r7, r8, r9} 32 | blx lr 33 | 34 | irq_handler_asm_wrapper: 35 | sub lr, lr, #4 // Adjsut return address 36 | srsdb sp!, #0x13 // Save irq lr and irq spsp to supervisor stack, and save the resulting stack pointer as the current stack pointer 37 | cpsid if, #0x13 // Switch to supervisor mode with interrupts disabled 38 | push {r0-r3, r12, lr}// Save the caller save registers 39 | and r1, sp, #4 // Make sure stack is 8 byte aligned 40 | sub sp, sp, r1 41 | push {r1} // Save the stack adjustment 42 | bl irq_handler 43 | pop {r1} // Get the stack adjustment 44 | add sp, sp, r1 45 | pop {r0-r3, r12, lr}// Revert the caller save registers 46 | rfeia sp! // Load the saved return address and program state register from before the interrupt from the stack and return 47 | -------------------------------------------------------------------------------- /src/kernel/interrupts.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static interrupt_registers_t * interrupt_regs; 6 | 7 | static interrupt_handler_f handlers[NUM_IRQS]; 8 | static interrupt_clearer_f clearers[NUM_IRQS]; 9 | 10 | extern void move_exception_vector(void); 11 | extern uint32_t exception_vector; 12 | 13 | void interrupts_init(void) { 14 | interrupt_regs = (interrupt_registers_t *)INTERRUPTS_PENDING; 15 | bzero(handlers, sizeof(interrupt_handler_f) * NUM_IRQS); 16 | bzero(clearers, sizeof(interrupt_clearer_f) * NUM_IRQS); 17 | interrupt_regs->irq_basic_disable = 0xffffffff; // disable all interrupts 18 | interrupt_regs->irq_gpu_disable1 = 0xffffffff; 19 | interrupt_regs->irq_gpu_disable2 = 0xffffffff; 20 | move_exception_vector(); 21 | ENABLE_INTERRUPTS(); 22 | } 23 | 24 | /** 25 | * this function is going to be called by the processor. Needs to check pending interrupts and execute handlers if one is registered 26 | */ 27 | void irq_handler(void) { 28 | int j; 29 | for (j = 0; j < NUM_IRQS; j++) { 30 | // If the interrupt is pending and there is a handler, run the handler 31 | if (IRQ_IS_PENDING(interrupt_regs, j) && (handlers[j] != 0)) { 32 | clearers[j](); 33 | ENABLE_INTERRUPTS(); 34 | handlers[j](); 35 | DISABLE_INTERRUPTS(); 36 | return; 37 | } 38 | } 39 | } 40 | 41 | void __attribute__ ((interrupt ("ABORT"))) reset_handler(void) { 42 | printf("RESET HANDLER\n"); 43 | while(1); 44 | } 45 | void __attribute__ ((interrupt ("ABORT"))) prefetch_abort_handler(void) { 46 | printf("PREFETCH ABORT HANDLER\n"); 47 | while(1); 48 | } 49 | void __attribute__ ((interrupt ("ABORT"))) data_abort_handler(void) { 50 | printf("DATA ABORT HANDLER\n"); 51 | while(1); 52 | } 53 | void __attribute__ ((interrupt ("UNDEF"))) undefined_instruction_handler(void) { 54 | printf("UNDEFINED INSTRUCTION HANDLER\n"); 55 | while(1); 56 | } 57 | void __attribute__ ((interrupt ("SWI"))) software_interrupt_handler(void) { 58 | printf("SWI HANDLER\n"); 59 | while(1); 60 | } 61 | void __attribute__ ((interrupt ("FIQ"))) fast_irq_handler(void) { 62 | printf("FIQ HANDLER\n"); 63 | while(1); 64 | } 65 | 66 | 67 | 68 | 69 | void register_irq_handler(irq_number_t irq_num, interrupt_handler_f handler, interrupt_clearer_f clearer) { 70 | uint32_t irq_pos; 71 | if (IRQ_IS_BASIC(irq_num)) { 72 | irq_pos = irq_num - 64; 73 | handlers[irq_num] = handler; 74 | clearers[irq_num] = clearer; 75 | interrupt_regs->irq_basic_enable |= (1 << irq_pos); 76 | } 77 | else if (IRQ_IS_GPU2(irq_num)) { 78 | irq_pos = irq_num - 32; 79 | handlers[irq_num] = handler; 80 | clearers[irq_num] = clearer; 81 | interrupt_regs->irq_gpu_enable2 |= (1 << irq_pos); 82 | } 83 | else if (IRQ_IS_GPU1(irq_num)) { 84 | irq_pos = irq_num; 85 | handlers[irq_num] = handler; 86 | clearers[irq_num] = clearer; 87 | interrupt_regs->irq_gpu_enable1 |= (1 << irq_pos); 88 | } 89 | else { 90 | printf("ERROR: CANNOT REGISTER IRQ HANDLER: INVALID IRQ NUMBER: %d\n", irq_num); 91 | } 92 | } 93 | void unregister_irq_handler(irq_number_t irq_num) { 94 | uint32_t irq_pos; 95 | if (IRQ_IS_BASIC(irq_num)) { 96 | irq_pos = irq_num - 64; 97 | handlers[irq_num] = 0; 98 | clearers[irq_num] = 0; 99 | // Setting the disable bit clears the enabled bit 100 | interrupt_regs->irq_basic_disable |= (1 << irq_pos); 101 | } 102 | else if (IRQ_IS_GPU2(irq_num)) { 103 | irq_pos = irq_num - 32; 104 | handlers[irq_num] = 0; 105 | clearers[irq_num] = 0; 106 | interrupt_regs->irq_gpu_disable2 |= (1 << irq_pos); 107 | } 108 | else if (IRQ_IS_GPU1(irq_num)) { 109 | irq_pos = irq_num; 110 | handlers[irq_num] = 0; 111 | clearers[irq_num] = 0; 112 | interrupt_regs->irq_gpu_disable1 |= (1 << irq_pos); 113 | } 114 | else { 115 | printf("ERROR: CANNOT UNREGISTER IRQ HANDLER: INVALID IRQ NUMBER: %d\n", irq_num); 116 | } 117 | } 118 | 119 | -------------------------------------------------------------------------------- /src/kernel/kerio.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | char getc(void) { 8 | return uart_getc(); 9 | } 10 | 11 | void putc(char c) { 12 | gpu_putc(c); 13 | } 14 | 15 | void puts(const char * str) { 16 | int i; 17 | for (i = 0; str[i] != '\0'; i ++) 18 | putc(str[i]); 19 | } 20 | 21 | void gets(char * buf, int buflen) { 22 | int i; 23 | char c; 24 | // Leave a spot for null char in buffer 25 | for (i = 0; (c = getc()) != '\r' && buflen > 1; i++, buflen--) { 26 | putc(c); 27 | buf[i] = c; 28 | } 29 | 30 | putc('\n'); 31 | if (c == '\n') { 32 | buf[i] = '\0'; 33 | } 34 | else 35 | buf[buflen-1] = '\0'; 36 | } 37 | 38 | void printf(const char * fmt, ...) { 39 | va_list args; 40 | va_start(args, fmt); 41 | 42 | for (; *fmt != '\0'; fmt++) { 43 | if (*fmt == '%') { 44 | switch (*(++fmt)) { 45 | case '%': 46 | putc('%'); 47 | break; 48 | case 'd': 49 | puts(itoa(va_arg(args, int), 10)); 50 | break; 51 | case 'x': 52 | puts(itoa(va_arg(args, int), 16)); 53 | break; 54 | case 's': 55 | puts(va_arg(args, char *)); 56 | break; 57 | } 58 | } else putc(*fmt); 59 | } 60 | 61 | va_end(args); 62 | } 63 | -------------------------------------------------------------------------------- /src/kernel/kernel.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | mutex_t test_mut; 15 | 16 | void test(void) { 17 | int i = 0; 18 | while (1) { 19 | if (i % 10 == 0) 20 | mutex_lock(&test_mut); 21 | else if (i % 10 == 9) 22 | mutex_unlock(&test_mut); 23 | printf("test %d\n", i++); 24 | udelay(1000000); 25 | } 26 | } 27 | 28 | void kernel_main(uint32_t r0, uint32_t r1, uint32_t atags) 29 | { 30 | int i = 0; 31 | // Declare as unused 32 | (void) r0; 33 | (void) r1; 34 | (void) atags; 35 | 36 | mem_init((atag_t *)atags); 37 | gpu_init(); 38 | printf("GPU INITIALIZED\n"); 39 | printf("INITIALIZING INTERRUPTS..."); 40 | interrupts_init(); 41 | printf("DONE\n"); 42 | printf("INITIALIZING TIMER..."); 43 | timer_init(); 44 | printf("DONE\n"); 45 | printf("INITIALIZING SCHEDULER..."); 46 | process_init(); 47 | printf("DONE\n"); 48 | 49 | puts("Hello, kernel World!\n"); 50 | 51 | mutex_init(&test_mut); 52 | create_kernel_thread(test, "TEST", 4); 53 | 54 | while (1) { 55 | if (i % 10 == 0) 56 | mutex_lock(&test_mut); 57 | else if (i % 10 == 9) 58 | mutex_unlock(&test_mut); 59 | 60 | printf("main %d\n", i++); 61 | udelay(1000000); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/kernel/lock_asm.S: -------------------------------------------------------------------------------- 1 | .section ".text" 2 | 3 | .global try_lock 4 | 5 | // This function takes a pointer to a lock variable and uses atomic operations to aqcuire the lock. 6 | // Returns 0 if the lock was not acquired and 1 if it was. 7 | try_lock: 8 | mov r1, #0 9 | swp r2, r1, [r0] 10 | mov r0, r2 11 | blx lr 12 | -------------------------------------------------------------------------------- /src/kernel/mailbox.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | mail_message_t mailbox_read(int channel) { 5 | mail_status_t stat; 6 | mail_message_t res; 7 | 8 | // Make sure that the message is from the right channel 9 | do { 10 | // Make sure there is mail to recieve 11 | do { 12 | stat = *MAIL0_STATUS; 13 | } while (stat.empty); 14 | 15 | // Get the message 16 | res = *MAIL0_READ; 17 | } while (res.channel != channel); 18 | 19 | return res; 20 | } 21 | 22 | void mailbox_send(mail_message_t msg, int channel) { 23 | mail_status_t stat; 24 | msg.channel = channel; 25 | 26 | 27 | // Make sure you can send mail 28 | do { 29 | stat = *MAIL0_STATUS; 30 | } while (stat.full); 31 | 32 | // send the message 33 | *MAIL0_WRITE = msg; 34 | } 35 | 36 | /** 37 | * returns the max of the size of the request and result buffer for each tag 38 | */ 39 | static uint32_t get_value_buffer_len(property_message_tag_t * tag) { 40 | switch(tag->proptag) { 41 | case FB_ALLOCATE_BUFFER: 42 | case FB_GET_PHYSICAL_DIMENSIONS: 43 | case FB_SET_PHYSICAL_DIMENSIONS: 44 | case FB_GET_VIRTUAL_DIMENSIONS: 45 | case FB_SET_VIRTUAL_DIMENSIONS: 46 | return 8; 47 | case FB_GET_BITS_PER_PIXEL: 48 | case FB_SET_BITS_PER_PIXEL: 49 | case FB_GET_BYTES_PER_ROW: 50 | return 4; 51 | case FB_RELESE_BUFFER: 52 | default: 53 | return 0; 54 | } 55 | } 56 | 57 | int send_messages(property_message_tag_t * tags) { 58 | property_message_buffer_t * msg; 59 | mail_message_t mail; 60 | uint32_t bufsize = 0, i, len, bufpos; 61 | 62 | // Calculate the sizes of each tag 63 | for (i = 0; tags[i].proptag != NULL_TAG; i++) { 64 | bufsize += get_value_buffer_len(&tags[i]) + 3*sizeof(uint32_t); 65 | } 66 | 67 | // Add the buffer size, buffer request/response code and buffer end tag sizes 68 | bufsize += 3*sizeof(uint32_t); 69 | 70 | // buffer size must be 16 byte aligned 71 | bufsize += (bufsize % 16) ? 16 - (bufsize % 16) : 0; 72 | 73 | msg = kmalloc(bufsize); 74 | if (!msg) 75 | return -1; 76 | 77 | msg->size = bufsize; 78 | msg->req_res_code = REQUEST; 79 | 80 | // Copy the messages into the buffer 81 | for (i = 0, bufpos = 0; tags[i].proptag != NULL_TAG; i++) { 82 | len = get_value_buffer_len(&tags[i]); 83 | msg->tags[bufpos++] = tags[i].proptag; 84 | msg->tags[bufpos++] = len; 85 | msg->tags[bufpos++] = 0; 86 | memcpy(msg->tags+bufpos, &tags[i].value_buffer, len); 87 | bufpos += len/4; 88 | } 89 | 90 | msg->tags[bufpos] = 0; 91 | 92 | // Send the message 93 | mail.data = ((uint32_t)msg) >>4; 94 | 95 | mailbox_send(mail, PROPERTY_CHANNEL); 96 | mail = mailbox_read(PROPERTY_CHANNEL); 97 | 98 | 99 | if (msg->req_res_code == REQUEST) { 100 | kfree(msg); 101 | return 1; 102 | } 103 | // Check the response code 104 | if (msg->req_res_code == RESPONSE_ERROR) { 105 | kfree(msg); 106 | return 2; 107 | } 108 | 109 | 110 | // Copy the tags back into the array 111 | for (i = 0, bufpos = 0; tags[i].proptag != NULL_TAG; i++) { 112 | len = get_value_buffer_len(&tags[i]); 113 | bufpos += 3; //skip over the tag bookkepping info 114 | memcpy(&tags[i].value_buffer, msg->tags+bufpos,len); 115 | bufpos += len/4; 116 | } 117 | 118 | kfree(msg); 119 | return 0; 120 | } 121 | 122 | -------------------------------------------------------------------------------- /src/kernel/mem.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | /** 8 | * Heap Stuff 9 | */ 10 | static void heap_init(uint32_t heap_start); 11 | /** 12 | * impliment kmalloc as a linked list of allocated segments. 13 | * Segments should be 4 byte aligned. 14 | * Use best fit algorithm to find an allocation 15 | */ 16 | typedef struct heap_segment{ 17 | struct heap_segment * next; 18 | struct heap_segment * prev; 19 | uint32_t is_allocated; 20 | uint32_t segment_size; // Includes this header 21 | } heap_segment_t; 22 | 23 | static heap_segment_t * heap_segment_list_head; 24 | 25 | /** 26 | * End Heap Stuff 27 | */ 28 | 29 | 30 | extern uint8_t __end; 31 | static uint32_t num_pages; 32 | 33 | IMPLEMENT_LIST(page); 34 | 35 | static page_t * all_pages_array; 36 | page_list_t free_pages; 37 | 38 | 39 | 40 | void mem_init(atag_t * atags) { 41 | uint32_t mem_size, page_array_len, kernel_pages, page_array_end, i; 42 | 43 | // Get the total number of pages 44 | mem_size = get_mem_size(atags); 45 | num_pages = mem_size / PAGE_SIZE; 46 | 47 | // Allocate space for all those pages' metadata. Start this block just after the stack 48 | page_array_len = sizeof(page_t) * num_pages; 49 | all_pages_array = (page_t *)((uint32_t)&__end + KERNEL_STACK_SIZE); 50 | bzero(all_pages_array, page_array_len); 51 | INITIALIZE_LIST(free_pages); 52 | 53 | // Find where the page metadata ends and round up to the nearest page 54 | page_array_end = (uint32_t)all_pages_array + page_array_len; 55 | page_array_end += page_array_end % PAGE_SIZE ? PAGE_SIZE - (page_array_end % PAGE_SIZE) : 0; 56 | 57 | // Iterate over all pages and mark them with the appropriate flags 58 | // Start with kernel pages, stacks, and page metadata 59 | kernel_pages = (page_array_end) / PAGE_SIZE; 60 | for (i = 0; i < kernel_pages; i++) { 61 | all_pages_array[i].vaddr_mapped = i * PAGE_SIZE; // Identity map the kernel pages 62 | all_pages_array[i].flags.allocated = 1; 63 | all_pages_array[i].flags.kernel_page = 1; 64 | } 65 | // Reserve 1 MB for the kernel heap 66 | for(; i < kernel_pages + (KERNEL_HEAP_SIZE / PAGE_SIZE); i++){ 67 | all_pages_array[i].vaddr_mapped = i * PAGE_SIZE; // Identity map the kernel pages 68 | all_pages_array[i].flags.allocated = 1; 69 | all_pages_array[i].flags.kernel_heap_page = 1; 70 | } 71 | // Map the rest of the pages as unallocated, and add them to the free list 72 | for(; i < num_pages; i++){ 73 | all_pages_array[i].flags.allocated = 0; 74 | append_page_list(&free_pages, &all_pages_array[i]); 75 | } 76 | 77 | 78 | // Initialize the heap 79 | heap_init(page_array_end); 80 | 81 | } 82 | 83 | void * alloc_page(void) { 84 | page_t * page; 85 | void * page_mem; 86 | 87 | 88 | if (size_page_list(&free_pages) == 0) 89 | return 0; 90 | 91 | // Get a free page 92 | page = pop_page_list(&free_pages); 93 | page->flags.kernel_page = 1; 94 | page->flags.allocated = 1; 95 | 96 | // Get the address the physical page metadata refers to 97 | page_mem = (void *)((page - all_pages_array) * PAGE_SIZE); 98 | 99 | // Zero out the page, big security flaw to not do this :) 100 | bzero(page_mem, PAGE_SIZE); 101 | 102 | return page_mem; 103 | } 104 | 105 | void free_page(void * ptr) { 106 | page_t * page; 107 | 108 | // Get page metadata from the physical address 109 | page = all_pages_array + ((uint32_t)ptr / PAGE_SIZE); 110 | 111 | // Mark the page as free 112 | page->flags.allocated = 0; 113 | append_page_list(&free_pages, page); 114 | } 115 | 116 | 117 | static void heap_init(uint32_t heap_start) { 118 | heap_segment_list_head = (heap_segment_t *) heap_start; 119 | bzero(heap_segment_list_head, sizeof(heap_segment_t)); 120 | heap_segment_list_head->segment_size = KERNEL_HEAP_SIZE; 121 | } 122 | 123 | 124 | void * kmalloc(uint32_t bytes) { 125 | heap_segment_t * curr, *best = NULL; 126 | int diff, best_diff = 0x7fffffff; // Max signed int 127 | 128 | // Add the header to the number of bytes we need and make the size 16 byte aligned 129 | bytes += sizeof(heap_segment_t); 130 | bytes += bytes % 16 ? 16 - (bytes % 16) : 0; 131 | 132 | // Find the allocation that is closest in size to this request 133 | for (curr = heap_segment_list_head; curr != NULL; curr = curr->next) { 134 | diff = curr->segment_size - bytes; 135 | if (!curr->is_allocated && diff < best_diff && diff >= 0) { 136 | best = curr; 137 | best_diff = diff; 138 | } 139 | } 140 | 141 | // There must be no free memory right now :( 142 | if (best == NULL) 143 | return NULL; 144 | 145 | // If the best difference we could come up with was large, split up this segment into two. 146 | // Since our segment headers are rather large, the criterion for splitting the segment is that 147 | // when split, the segment not being requested should be twice a header size 148 | if (best_diff > (int)(2 * sizeof(heap_segment_t))) { 149 | bzero(((void*)(best)) + bytes, sizeof(heap_segment_t)); 150 | curr = best->next; 151 | best->next = ((void*)(best)) + bytes; 152 | best->next->next = curr; 153 | best->next->prev = best; 154 | best->next->segment_size = best->segment_size - bytes; 155 | best->segment_size = bytes; 156 | } 157 | 158 | best->is_allocated = 1; 159 | 160 | return best + 1; 161 | } 162 | 163 | void kfree(void *ptr) { 164 | heap_segment_t * seg; 165 | 166 | if (!ptr) 167 | return; 168 | 169 | seg = ptr - sizeof(heap_segment_t); 170 | seg->is_allocated = 0; 171 | 172 | // try to coalesce segements to the left 173 | while(seg->prev != NULL && !seg->prev->is_allocated) { 174 | seg->prev->next = seg->next; 175 | seg->next->prev = seg->prev; 176 | seg->prev->segment_size += seg->segment_size; 177 | seg = seg->prev; 178 | } 179 | // try to coalesce segments to the right 180 | while(seg->next != NULL && !seg->next->is_allocated) { 181 | seg->next->next->prev = seg; 182 | seg->next = seg->next->next; 183 | seg->segment_size += seg->next->segment_size; 184 | seg = seg->next; 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /src/kernel/model1/framebuffer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | typedef struct { 7 | uint32_t width; 8 | uint32_t height; 9 | uint32_t vwidth; 10 | uint32_t vheight; 11 | uint32_t bytes; 12 | uint32_t depth; 13 | uint32_t ignorex; 14 | uint32_t ignorey; 15 | void * pointer;; 16 | uint32_t size; 17 | } fb_init_t; 18 | 19 | fb_init_t fbinit __attribute__((aligned(16))); 20 | 21 | int framebuffer_init(void) { 22 | mail_message_t msg; 23 | 24 | fbinit.width = 640; 25 | fbinit.height = 480; 26 | fbinit.vwidth = fbinit.width; 27 | fbinit.vheight = fbinit.height; 28 | fbinit.depth = COLORDEPTH; 29 | 30 | msg.data = ((uint32_t)&fbinit + 0x40000000) >> 4; 31 | 32 | mailbox_send(msg, FRAMEBUFFER_CHANNEL); 33 | msg = mailbox_read(FRAMEBUFFER_CHANNEL); 34 | 35 | if (!msg.data) 36 | return -1; 37 | 38 | fbinfo.width = fbinit.width; 39 | fbinfo.height = fbinit.height; 40 | fbinfo.chars_width = fbinfo.width / CHAR_WIDTH; 41 | fbinfo.chars_height = fbinfo.height / CHAR_HEIGHT; 42 | fbinfo.chars_x = 0; 43 | fbinfo.chars_y = 0; 44 | fbinfo.pitch = fbinit.bytes; 45 | fbinfo.buf = fbinit.pointer; 46 | fbinfo.buf_size = fbinit.size; 47 | 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /src/kernel/model2/framebuffer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int framebuffer_init(void) { 7 | property_message_tag_t tags[5]; 8 | 9 | 10 | tags[0].proptag = FB_SET_PHYSICAL_DIMENSIONS; 11 | tags[0].value_buffer.fb_screen_size.width = 640; 12 | tags[0].value_buffer.fb_screen_size.height = 480; 13 | tags[1].proptag = FB_SET_VIRTUAL_DIMENSIONS; 14 | tags[1].value_buffer.fb_screen_size.width = 640; 15 | tags[1].value_buffer.fb_screen_size.height = 480; 16 | tags[2].proptag = FB_SET_BITS_PER_PIXEL; 17 | tags[2].value_buffer.fb_bits_per_pixel = COLORDEPTH; 18 | tags[3].proptag = NULL_TAG; 19 | 20 | 21 | // Send over the initialization 22 | if (send_messages(tags) != 0) { 23 | return -1; 24 | } 25 | 26 | fbinfo.width = tags[0].value_buffer.fb_screen_size.width; 27 | fbinfo.height = tags[0].value_buffer.fb_screen_size.height; 28 | fbinfo.chars_width = fbinfo.width / CHAR_WIDTH; 29 | fbinfo.chars_height = fbinfo.height / CHAR_HEIGHT; 30 | fbinfo.chars_x = 0; 31 | fbinfo.chars_y = 0; 32 | fbinfo.pitch = fbinfo.width*BYTES_PER_PIXEL; 33 | 34 | // request a framebuffer 35 | tags[0].proptag = FB_ALLOCATE_BUFFER; 36 | tags[0].value_buffer.fb_screen_size.width = 0; 37 | tags[0].value_buffer.fb_screen_size.height = 0; 38 | tags[0].value_buffer.fb_allocate_align = 16; 39 | tags[1].proptag = NULL_TAG; 40 | 41 | 42 | if (send_messages(tags) != 0) { 43 | return -1; 44 | } 45 | 46 | fbinfo.buf = tags[0].value_buffer.fb_allocate_res.fb_addr; 47 | fbinfo.buf_size = tags[0].value_buffer.fb_allocate_res.fb_size; 48 | 49 | return 0; 50 | } 51 | 52 | -------------------------------------------------------------------------------- /src/kernel/process.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static uint32_t next_proc_num = 1; 10 | #define NEW_PID next_proc_num++; 11 | 12 | extern uint8_t __end; 13 | extern void switch_to_thread(process_control_block_t * old, process_control_block_t * new); 14 | extern int try_lock(int * lock_var); 15 | 16 | IMPLEMENT_LIST(pcb); 17 | 18 | pcb_list_t run_queue; 19 | pcb_list_t all_proc_list; 20 | 21 | process_control_block_t * current_process; 22 | 23 | void schedule(void) { 24 | DISABLE_INTERRUPTS(); 25 | process_control_block_t * new_thread, * old_thread; 26 | 27 | // If nothing on the run queue, the current process should just continue 28 | if (size_pcb_list(&run_queue) == 0) { 29 | timer_set(10000); 30 | ENABLE_INTERRUPTS(); 31 | return; 32 | } 33 | 34 | // Get the next thread to run. For now we are using round-robin 35 | new_thread = pop_pcb_list(&run_queue); 36 | old_thread = current_process; 37 | current_process = new_thread; 38 | 39 | 40 | // Put the current thread back in the run queue 41 | append_pcb_list(&run_queue, old_thread); 42 | 43 | // Context Switch 44 | switch_to_thread(old_thread, new_thread); 45 | ENABLE_INTERRUPTS(); 46 | } 47 | 48 | void process_init(void) { 49 | process_control_block_t * main_pcb; 50 | INITIALIZE_LIST(run_queue); 51 | INITIALIZE_LIST(all_proc_list); 52 | 53 | // Allocate and initailize the block 54 | main_pcb = kmalloc(sizeof(process_control_block_t)); 55 | main_pcb->stack_page = (void *)&__end; 56 | main_pcb->pid = NEW_PID; 57 | memcpy(main_pcb->proc_name, "Init", 5); 58 | 59 | // Add self to all process list. It is already running, so dont add it to the run queue 60 | append_pcb_list(&all_proc_list, main_pcb); 61 | 62 | current_process = main_pcb; 63 | 64 | // Set the timer to go off after 10 ms 65 | timer_set(10000); 66 | 67 | } 68 | 69 | static void reap(void) { 70 | DISABLE_INTERRUPTS(); 71 | process_control_block_t * new_thread, * old_thread; 72 | 73 | // If nothing on the run queue, there is nothing to do now. just loop 74 | while (size_pcb_list(&run_queue) == 0); 75 | 76 | // Get the next thread to run. For now we are using round-robin 77 | new_thread = pop_pcb_list(&run_queue); 78 | old_thread = current_process; 79 | current_process = new_thread; 80 | 81 | // Free the resources used by the old process. Technically, we are using dangling pointers here, but since interrupts are disabled and we only have one core, it 82 | // should still be fine 83 | free_page(old_thread->stack_page); 84 | kfree(old_thread); 85 | 86 | // remove from all threads list 87 | remove_pcb(&all_proc_list, old_thread); 88 | 89 | // Context Switch 90 | switch_to_thread(old_thread, new_thread); 91 | } 92 | 93 | void create_kernel_thread(kthread_function_f thread_func, char * name, int name_len) { 94 | process_control_block_t * pcb; 95 | proc_saved_state_t * new_proc_state; 96 | 97 | // Allocate and initialize the pcb 98 | pcb = kmalloc(sizeof(process_control_block_t)); 99 | pcb->stack_page = alloc_page(); 100 | pcb->pid = NEW_PID; 101 | memcpy(pcb->proc_name, name, MIN(name_len,19)); 102 | pcb->proc_name[MIN(name_len,19)] = 0; 103 | 104 | // Get the location the stack pointer should be in when this is run 105 | new_proc_state = pcb->stack_page + PAGE_SIZE - sizeof(proc_saved_state_t); 106 | pcb->saved_state = new_proc_state; 107 | 108 | // Set up the stack that will be restored during a context switch 109 | bzero(new_proc_state, sizeof(proc_saved_state_t)); 110 | new_proc_state->lr = (uint32_t)thread_func; // lr is used as return address in switch_to_thread 111 | new_proc_state->sp = (uint32_t)reap; // When the thread function returns, this reaper routine will clean it up 112 | new_proc_state->cpsr = 0x13 | (8 << 1); // Sets the thread up to run in supervisor mode with irqs only 113 | 114 | // add the thread to the lists 115 | append_pcb_list(&all_proc_list, pcb); 116 | append_pcb_list(&run_queue, pcb); 117 | } 118 | 119 | void spin_init(spin_lock_t * lock) { 120 | *lock = 1; 121 | } 122 | 123 | void spin_lock(spin_lock_t * lock) { 124 | while (!try_lock(lock)); 125 | } 126 | 127 | void spin_unlock(spin_lock_t * lock) { 128 | *lock = 1; 129 | } 130 | 131 | void mutex_init(mutex_t * lock) { 132 | lock->lock = 1; 133 | lock->locker = 0; 134 | INITIALIZE_LIST(lock->wait_queue); 135 | } 136 | 137 | void mutex_lock(mutex_t * lock) { 138 | process_control_block_t * new_thread, * old_thread; 139 | // If you don't get the lock, take self off run queue and put on to mutex wait queue 140 | while (!try_lock(&lock->lock)) { 141 | 142 | // Get the next thread to run. For now we are using round-robin 143 | DISABLE_INTERRUPTS(); 144 | new_thread = pop_pcb_list(&run_queue); 145 | old_thread = current_process; 146 | current_process = new_thread; 147 | 148 | // Put the current thread back of this mutex's wait queue, not on the run queue 149 | append_pcb_list(&lock->wait_queue, old_thread); 150 | 151 | // Context Switch 152 | switch_to_thread(old_thread, new_thread); 153 | ENABLE_INTERRUPTS(); 154 | } 155 | lock->locker = current_process; 156 | } 157 | void mutex_unlock(mutex_t * lock) { 158 | process_control_block_t * thread; 159 | lock->lock = 1; 160 | lock->locker = 0; 161 | 162 | 163 | // If there is anyone waiting on this, put them back in the run queue 164 | if (size_pcb_list(&lock->wait_queue)) { 165 | thread = pop_pcb_list(&lock->wait_queue); 166 | push_pcb_list(&run_queue, thread); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/kernel/process_asm.S: -------------------------------------------------------------------------------- 1 | .section ".text" 2 | 3 | .global switch_to_thread 4 | 5 | // void switch_to_thread(process_control_block_t * old, process_control_block_t * new) 6 | // takes old thread off cpu and puts new thread on. 7 | // Old thread should be put back in run queue before this is called 8 | switch_to_thread: 9 | // Save the current thread's state 10 | push {lr} // Push stack pointer and link register 11 | push {sp} 12 | mrs r12, cpsr // Get the current program state register 13 | push {r0-r12} // Save all general purpose registers and program state 14 | str sp, [r0] // Store the stack pointer into the saved_state field of the current process 15 | // Restore the new thread's state 16 | ldr sp, [r1] // Load the stack pointer of the new process 17 | ldr r0, =#(10000)// Set Quantum time to 10 ms (10000 us) 18 | bl timer_set // reset the timer to go off in another quantum 19 | pop {r0-r12} // restore the general purpose registers 20 | msr cpsr_c, r12 // Restore the new thread's program status register 21 | pop {lr, pc} // we have no idea what lr should be, so just give it a garbage value. pc gets the stored lr so this function returns to there 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/kernel/timer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static timer_registers_t * timer_regs; 6 | 7 | static void timer_irq_handler(void) { 8 | schedule(); 9 | } 10 | 11 | static void timer_irq_clearer(void) { 12 | timer_regs->control.timer1_matched = 1; 13 | } 14 | 15 | void timer_init(void) { 16 | timer_regs = (timer_registers_t *) SYSTEM_TIMER_BASE; 17 | register_irq_handler(SYSTEM_TIMER_1, timer_irq_handler, timer_irq_clearer); 18 | } 19 | 20 | void timer_set(uint32_t usecs) { 21 | timer_regs->timer1 = timer_regs->counter_low + usecs; 22 | } 23 | 24 | 25 | __attribute__ ((optimize(0))) void udelay (uint32_t usecs) { 26 | volatile uint32_t curr = timer_regs->counter_low; 27 | volatile uint32_t x = timer_regs->counter_low - curr; 28 | while (x < usecs) { 29 | x = timer_regs->counter_low - curr; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/kernel/uart.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | // Memory-Mapped I/O output 7 | void mmio_write(uint32_t reg, uint32_t data) 8 | { 9 | *(volatile uint32_t*)reg = data; 10 | } 11 | 12 | // Memory-Mapped I/O input 13 | uint32_t mmio_read(uint32_t reg) 14 | { 15 | return *(volatile uint32_t*)reg; 16 | } 17 | 18 | // Loop times in a way that the compiler won't optimize away 19 | void delay(int32_t count) 20 | { 21 | asm volatile("__delay_%=: subs %[count], %[count], #1; bne __delay_%=\n" 22 | : "=r"(count): [count]"0"(count) : "cc"); 23 | } 24 | 25 | void uart_init() 26 | { 27 | uart_control_t control; 28 | // Disable UART0. 29 | bzero(&control, 4); 30 | mmio_write(UART0_CR, control.as_int); 31 | 32 | // Setup the GPIO pin 14 && 15. 33 | // Disable pull up/down for all GPIO pins & delay for 150 cycles. 34 | mmio_write(GPPUD, 0x00000000); 35 | delay(150); 36 | 37 | // Disable pull up/down for pin 14,15 & delay for 150 cycles. 38 | mmio_write(GPPUDCLK0, (1 << 14) | (1 << 15)); 39 | delay(150); 40 | 41 | // Write 0 to GPPUDCLK0 to make it take effect. 42 | mmio_write(GPPUDCLK0, 0x00000000); 43 | 44 | // Clear pending interrupts. 45 | mmio_write(UART0_ICR, 0x7FF); 46 | 47 | // Set integer & fractional part of baud rate. 48 | // Divider = UART_CLOCK/(16 * Baud) 49 | // Fraction part register = (Fractional part * 64) + 0.5 50 | // UART_CLOCK = 3000000; Baud = 115200. 51 | 52 | // Divider = 3000000 / (16 * 115200) = 1.627 = ~1. 53 | mmio_write(UART0_IBRD, 1); 54 | // Fractional part register = (.627 * 64) + 0.5 = 40.6 = ~40. 55 | mmio_write(UART0_FBRD, 40); 56 | 57 | // Enable FIFO & 8 bit data transmissio (1 stop bit, no parity). 58 | mmio_write(UART0_LCRH, (1 << 4) | (1 << 5) | (1 << 6)); 59 | 60 | // Mask all interrupts. 61 | mmio_write(UART0_IMSC, (1 << 1) | (1 << 4) | (1 << 5) | (1 << 6) | 62 | (1 << 7) | (1 << 8) | (1 << 9) | (1 << 10)); 63 | 64 | // Enable UART0, receive & transfer part of UART. 65 | control.uart_enabled = 1; 66 | control.transmit_enabled = 1; 67 | control.receive_enabled = 1; 68 | mmio_write(UART0_CR, control.as_int); 69 | } 70 | 71 | 72 | uart_flags_t read_flags(void) { 73 | uart_flags_t flags; 74 | flags.as_int = mmio_read(UART0_FR); 75 | return flags; 76 | } 77 | 78 | void uart_putc(unsigned char c) 79 | { 80 | uart_flags_t flags; 81 | // Wait for UART to become ready to transmit. 82 | 83 | do { 84 | flags = read_flags(); 85 | } 86 | while ( flags.transmit_queue_full ); 87 | mmio_write(UART0_DR, c); 88 | } 89 | 90 | unsigned char uart_getc() 91 | { 92 | // Wait for UART to have received something. 93 | uart_flags_t flags; 94 | do { 95 | flags = read_flags(); 96 | } 97 | while ( flags.recieve_queue_empty ); 98 | return mmio_read(UART0_DR); 99 | } 100 | -------------------------------------------------------------------------------- /steps.txt: -------------------------------------------------------------------------------- 1 | =============== 2 | Getting Started 3 | =============== 4 | 1) Download gcc-arm-none-eabi cross compiler from developer.arm.com 5 | 2) Download qemu-system-arm for quick testing purposes 6 | 3) Copy code from OSdev page on raspberry pi barebones 7 | 4) Put their build instructions in makefile 8 | 9 | 10 | ================================ 11 | Understanding the barebones code 12 | ================================ 13 | 1) boot.S 14 | a) _start is where control is handed off from the bootloader to the kernel 15 | b) It sets up the stack pointer 16 | c) Sets up the BSS segment (C global veriables) 17 | i) __bss_start and __bss_end are defined in the linker script 18 | ii) label 1 stores 16 bytes of zeros at a time into the bss segment 19 | iii) label 2 loops this operation until the whole BSS segment is zeroed 20 | d) Jumps to the kernel_main function 21 | 22 | 2) kernel.c 23 | a) UART is a hardware thing in the raspi cpu. It will let us do IO 24 | i) It is "memory mapped", so all interactions go through memory. The memory segment we will interact with starts at some hardcoded magic number 25 | 1) That magic number is actually a base address for all peripherals plus a magic offset 26 | ii) We interact through registers, which are fixed offsets from the base of the UART memory 27 | iii) For now, we only care about the data register (UART0_DR) and the flags register (UART0+FR). Flags tells us when we can send and recieve data, and data is where we put data to send and read data to recieve 28 | iv) flags at offset 4 tells whether or not uart has data to read, 5 tells whether uart can accpt data from a write 29 | 30 | 3) linker.ld 31 | a) Defines the memory layout of our kernel 32 | b) ENTRY(_start) is what tells the linker our _start label in boot.S is the entry point 33 | c) The script starts the memory layout at 0x8000, where the bootloader will put the kernel image 34 | d) It delcares the .text section, the executable code, with .text.boot from boot.S first, then the rest of the kernel code in .text 35 | e) It declares .rodata (constant data, write protected), .data (initialized data) and .bss (uninitialized data) 36 | f) Each section is aligned (rounded up) to the nearest 4096, so it fits neatly inside a memory page 37 | 38 | 39 | ======== 40 | Building 41 | ======== 42 | 1) Compiling boot.S 43 | a) -mcpu = cortex-a7: declare the exact model of arm cpu that is being targeted. Note that this should be "arm1176jzf-s" if compiling for raspi version 1 44 | b) -fpic: generate position independent code. idk why its needed? maybe try removing? 45 | c) -ffreestanding: Use standard C includes like stdint.h, but not standard library functions 46 | 47 | 2) Compiling kernel.c 48 | a) Similar options, nothing crazy 49 | 50 | 3) Linking into an image 51 | a) -T linker.ld: Specify our linker script to do the linking for our kernel 52 | b) -nostdlib: again, we don't have libc here, so we don't want the compiler to assume we do 53 | 54 | 4) Running 55 | a) qemu can emulate rpi2 56 | b) to run on real hardware, its a bit of a hassle. easiest to just take an sd card image of an OS that works, and replace kernel.img with ours 57 | 58 | 59 | ======================= 60 | Setting up the Makefile 61 | ======================= 62 | 1) The kernel boots, but all of the files are scattered all over the place, and the makefile is not going to scale 63 | 64 | 2) create build, src/kernel and include/kernel directories. Put boot.S and kernel.c inside src/kernel, put makefile and linker into build 65 | directory 66 | 67 | 3) Change makefile to use variables. Notably: 68 | a) RASPI_MODEL: there are a few differences in the cpu between model 1 and models 2 and 3 69 | b) SOURCES, OBJECTS, and INCLUDES: Create a list of all source files and all object files those source files will compile into. Then we have rules that compile all of the source files into object files 70 | c) Now we can just keep adding new source files without worrying about the make file 71 | 72 | 73 | =========================== 74 | Getting basic functionality 75 | =========================== 76 | 1) Since no stdlib, missing a lot of stuff 77 | 78 | 2) We can just write a bunch of stdlib and stdio functions ourselves 79 | 80 | ============== 81 | Dynamic memory 82 | ============== 83 | 1) Having dynamic memory in the kernel is going to be useful, especially if we want dynamic linked list data structures 84 | 2) Need to create a simple malloc and free implimentation 85 | 3) First we need memory to allocate. k 86 | a) Since we are the kernel, we can take whatever memory we want and do whatever we want with it. 87 | b) Since the kernel image starts at 0x8000, it seems reasonable to start our heap at 0x100000 (1MB) and give it (1MB) in size. 88 | c) Now that we have a big hunk of memory, all we need is a function to divy it up as requested, malloc 89 | 4) We are going to keep track of the memory as a linked list, where each node marks the start of an allocation. 90 | a) We will use best fit, meaning we go through the entire list to find a free allocation that is close to the size we want 91 | b) If we get something rather large, we will split it up. 92 | c) When we free, we will check if we can put allocations back together 93 | 94 | ======== 95 | HDMI OUT 96 | ======== 97 | 1) UART is all well and good, if you have the appopriate setup 98 | a) Need a serial to usb/hdmi cable to actually use the setup as it stands right now 99 | 2) I don't have an appropriate setup, need to enable hdmi and usb 100 | 3) The hdmi protocol itself is handled by the gpu. All we need to do is get access to the "framebuffer" (a region of memory the gpu will just write to the screen) 101 | 4) Talk to gpu using an interface called "mailbox" 102 | a) mailbox starts at a certain offset from the peripheral base 103 | b) offsets from there are "regisers" of the mailbox 104 | i) one for reading, one for writing, one for status 105 | ii) reading/writing into these addresses sends/recieves "mail" 106 | 5) Model 2 and 3 use "property mailbox" 107 | a) First need to craft a message 108 | i) Message consists of a size, a code determining whether the message is a request or a response, a list of tags, an empty end tag, and padding to make it 16 byte aligned 109 | ii) tags are the individual requests we want to make 110 | 1) consist of an identifier, a size of its "value buffer" (the space where parameters are passed and results are put), a spot to put a result size, and the value buffer itself. 111 | 2) Most important for us are setting the screen size, setting the color mode, and getting a pointer to the frame buffer 112 | b) Once we create a message, we can send it to the mailbox. 113 | i) mail messages have 28 bits of data and 4 bits of channel. 114 | 1) The channel for property is 8. 115 | 2) The pointer to the message goes in the data, but only the high 28 bits, so we must right shift our pointer by 4. the pointer therefore must be 16 byte aligned 116 | c) Once we send the message, we can start writing raw pixels to the framebuffer it gave us 117 | 6) Model 1 uses "framebuffer mailbox" 118 | a) create a struct where we ask for a specific resolution and depth, and space for bytes per row, pointer to the buffer and a buffer size 119 | i) Again, since mailbox data is 28 bytes, pointer to struct must be 16 byte aligned 120 | ii) must add 0x40000000, because GPU sees memory differently than cpu 121 | b) After we send the message, it fills out our struct with info that we can use exactly the same as with model 2 and 3 122 | 7) If we want text, we need to render it ourselves 123 | a) Someone else was kind enough to write out 8x8 bitmaps of all the ascii characters, so we will use that 124 | b) We will render each ascii character pixel by pixel 125 | c) having a simple statically allocated array of pixel bitmaps won't work in practice 126 | i) For some reason, the rodata section was getting corrupt when i ran on actual hardware 127 | ii) worked around by creating a function that statically allocates the data. For some reason this fixes it? 128 | 129 | ========== 130 | Interrupts 131 | ========== 132 | 1) We want to do a usb keyboard, this requires hardware interrupts 133 | 2) There are 3 kinds of hardware interrupts 134 | a) arm specific (aka basic) 135 | b) arm and gpu shared 136 | c) fast interrupt 137 | i) This is handled separately, so we are not gonna worry about it 138 | 3) Hardware interrupts fall under the more broad category of processor exceptions 139 | a) Each type of exception needs to have its own specific handler 140 | i) irq handler important right now, swi handler important later 141 | b) When an exception occurs, the processor loads a certain offset from address 0 into the pc, depending on what type of exception, and executes the instruction there 142 | i) 0 is reset, 4 is undefined instruction, 8 is software interrupt, c is prefetch abort handler, 10 is data abort handler, 14 is reserved, 18 is irq, 1c is fiq 143 | ii) To write handlers, all we need to do is define c functions to handle each exception, and add compiler attribute so that the compiler knows its an exception handler and generates appropriate prolog and epilog code 144 | c) Tricky part is to get the instructions to 0 145 | i) we can't just load the addresses into the program counter, because the assembler makes all of these references relative 146 | 1) If we move these to 0, they will still be loading an address relative to them, but the address they want is no longer there 147 | ii) We need to copy along with it the absolute location of all the addresses of the handlers 148 | 1) But wait!! the absolute addresses all think that the code starts at address 0, but it starts at 0x8000!, so we must add that 0x8000 in ourselves 149 | 4) Once the exception instructions are copied, we still need to do some setup for hardware interrrupts specifically 150 | a) when an irq comes in, the processor changes to irq mode, from whatever mode it was in 151 | b) irq mode has its own set of shadow registers, including the stack pointer 152 | c) we must initialize this stack pointer so that our stack is not overwriting anything important when we go to irq mode 153 | 5) Now that the exception handlers are all set up, we need to write the irq handler itself 154 | a) Need to check which interrupts are pending, can do this through irq peripheral 155 | b) interrupts peripheral offset is 0xb000, has following structure 156 | i) 24 bytes of pending flags. each hardware interrupt flips a certain flag 157 | 1) The first 4 bytes are basic interrupts 158 | 2) The next 8 are gpu specific 159 | a) Everything we want is going to be in the gpu shared range 160 | ii) 4 bytes of fiq control 161 | iii) 24 bytes of enable flags 162 | 1) set to 1 to enable the interrupt 163 | 2) first 8 bytes are for gpu 164 | 3) next 4 are for arm specific 165 | iv) 24 bytes of disable flags 166 | 1) set to 1 to disable (dont have to set 0 to enable flag) 167 | 2) first 8 for gpu, next 4 for arm 168 | c) use this to find which interrupt is waiting and call the appropriate handler 169 | 6) need to enable interrupts 170 | a) use cps instruction 171 | b) ie modifier means interrupts enabled, id means disabled 172 | c) i argument means enable irqs, as opposed to f for fiqs 173 | 7) Need something to interrupt us! 174 | a) usb is hard, so we will use the system timer 175 | i) System timer always runs in the background, and we can set it to interrupt us after a certain time 176 | b) timer peripheral offset is 0x3000 177 | i) control register, for checking and clearing interrupt status of timers 178 | ii) low and high timer registers 179 | a) timer is 64 bit, need two registers to hold it 180 | b) timer is 1Mhz, so low register increases once ever microsecond 181 | iii) 4 compare registers 182 | a) low timer is compared to all 4 compare registers every tick. 183 | b) if match, then interrupt 184 | c) First we define an interrupt handler and register it with the irq handler 185 | d) Then we set the timer to go off, and wait 186 | 187 | 188 | --------------------------------------------------------------------------------