diff options
| author | Nic Gaffney <gaffney_nic@protonmail.com> | 2023-11-05 06:13:22 -0600 |
|---|---|---|
| committer | Nic Gaffney <gaffney_nic@protonmail.com> | 2023-11-05 06:13:22 -0600 |
| commit | 036398ab051f2df5b70303e03f4f6506d36cb6eb (patch) | |
| tree | 0067db99aa1417bb58b3b4d65b960a47b1100475 | |
| download | gftos-036398ab051f2df5b70303e03f4f6506d36cb6eb.tar.gz | |
Initial Commit
| -rw-r--r-- | .gitignore | 3 | ||||
| -rw-r--r-- | Makefile | 60 | ||||
| -rw-r--r-- | README.md | 3 | ||||
| -rw-r--r-- | out/grub.cfg | 3 | ||||
| -rw-r--r-- | out/isodir/boot/grub/grub.cfg | 3 | ||||
| -rw-r--r-- | src/header/boot.s | 117 | ||||
| -rw-r--r-- | src/include/print.h | 31 | ||||
| -rw-r--r-- | src/include/str.h | 5 | ||||
| -rw-r--r-- | src/kernel/compile_flags.txt | 1 | ||||
| -rw-r--r-- | src/kernel/kernel.c | 21 | ||||
| -rw-r--r-- | src/kernel/print.c | 133 | ||||
| -rw-r--r-- | src/linker.ld | 44 | ||||
| -rw-r--r-- | src/utils/str.c | 33 |
13 files changed, 457 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2b673ac --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build/ +out/myos.iso +out/isodir/boot/myos.bin diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a64980d --- /dev/null +++ b/Makefile @@ -0,0 +1,60 @@ +OUT_DIR = out +SRC = src +BUILD = build +TARGET = i686-elf + +ISO = myos + +ASM := $(sort $(shell find $(SRC) -name '*.s')) +C := $(sort $(shell find $(SRC) -name '*.c')) + +INCLUDE_DIR = $(SRC)/include +INCLUDES := -I$(INCLUDE_DIR) + +ASSEMBLER = $(TARGET)-as +CC = $(TARGET)-gcc + +CFLAGS = $(INCLUDES) -std=gnu99 -ffreestanding -O2 -Wall -Wextra +TEST_FLAGS = -nographic + + +LINK_FILE = linker.ld +LDFLAGS = -O2 -nostdlib -lgcc -ffreestanding + +OBJS = +C_OBJS := $(C:$(SRC)/%.c=$(BUILD)/C/%.o) +ASM_OBJS := $(ASM:$(SRC)/%.s=$(BUILD)/ASM/%.o) + +OBJS += $(C_OBJS) +OBJS += $(ASM_OBJS) +DEPS := $(OBJS:.o=.d) + +.PHONY: all +all: $(OUT_DIR)/$(ISO).iso + +run: all + qemu-system-i386 -cdrom $(OUT_DIR)/$(ISO).iso + + +$(OUT_DIR)/$(ISO).iso : $(OUT_DIR)/isodir/boot/$(ISO).bin + grub-mkrescue -o $@ $(OUT_DIR)/isodir + +$(OUT_DIR)/isodir/boot/$(ISO).bin : $(OBJS) + $(CC) -T $(SRC)/$(LINK_FILE) -o $@ $(LDFLAGS) $^ + +$(BUILD)/ASM/%.o: $(SRC)/%.s + mkdir -p $(BUILD) + mkdir -p $(BUILD)/ASM + mkdir -p $(@D) + $(ASSEMBLER) $^ -o $@ + +$(BUILD)/C/%.o: $(SRC)/%.c + mkdir -p $(BUILD) + mkdir -p $(BUILD)/C + mkdir -p $(@D) + $(CC) $(CFLAGS) -c $^ -o $@ + +clean: + rm -rf $(BUILD) + rm -rf $(OUT_DIR)/$(ISO).iso + rm -rf $(OUT_DIR)/isodir/boot/$(ISO).bin diff --git a/README.md b/README.md new file mode 100644 index 0000000..bbc4689 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Basic setup for OSdev + +These files are well commented and are meant to remind you how to actually develop an OS. read from osdev.org to learn more. diff --git a/out/grub.cfg b/out/grub.cfg new file mode 100644 index 0000000..f064685 --- /dev/null +++ b/out/grub.cfg @@ -0,0 +1,3 @@ +menuentry "myos" { + multiboot /boot/myos.bin +} diff --git a/out/isodir/boot/grub/grub.cfg b/out/isodir/boot/grub/grub.cfg new file mode 100644 index 0000000..f064685 --- /dev/null +++ b/out/isodir/boot/grub/grub.cfg @@ -0,0 +1,3 @@ +menuentry "myos" { + multiboot /boot/myos.bin +} diff --git a/src/header/boot.s b/src/header/boot.s new file mode 100644 index 0000000..2276e3d --- /dev/null +++ b/src/header/boot.s @@ -0,0 +1,117 @@ +/* multiboot header constants */ +.set ALIGN, 1<<0 /* Align loaded modules on page boundries */ +.set MEMINFO, 1<<1 /* Provide memory map*/ +.set FLAGS, ALIGN | MEMINFO /* Multiboot flag field*/ +.set MAGIC, 0x1BADB002 /*Lets bootloader find header*/ +.set CHECKSUM, -(MAGIC + FLAGS) /* Proves we are multiboot via checksum*/ + +/* + * Declare a multiboot header that marks the program as a kernel. + * These are magic values that are docymented in the multiboot standard. + * The bootloader will search fot this signature in the first 8KiB of + * the kernel file, aligned at a 32-bit boundry. + * The signature is in its own section so the header can be forced to be + * within the first 8KiB of the kernel file. + */ +.section .multiboot +.align 4 +.long MAGIC +.long FLAGS +.long CHECKSUM + +/* + * The multiboot standard doesn't define the value of the "stack pointer register," + * or esp, as it is up to the kernel to provide a stack. + * This allocates room for a small stack through a few steps: + * 1. Create a symbol at the bottom of the stack + * 2. Allocate 16 KiB for the stack + * 3. Create a symbol at the top of the stack. + * The stack grows DOWN on x86. + * Since the stack is in its own section*, it can be marked "nobits" + * which means the kernel file is smaller since it doesn't contain + * an uninitialized stack. + * On x86, the stack must be 16-byte aligned** according to System V ABI standard + * and de-facto extentions. + * The compiler assumes the stack is properly aligned, so failure to align will + * result in UB. + */ +.section bss +.align 16 +.stack_bottom: +.skip 16384 +stack_top: + +/* + * Our linker script specifies _start as te entry point to the kernel. + * The bootloader will jump here once the kernel is loaded. + * We won't return, since the bootloader would be gone at that point + */ +.section .text +.global _start +.type _start, @function +_start: + /* + * The bootloader loads us into 32-bit mode on x86 machines. + * Interrupts and paging are disabled. + * The multiboot standard defines our current processor state. + * The kernel has full control over the CPU + * The kernel can only use two things + * 1. hardware features + * 2. its own code + * No printing is available (Unless we make it). + * No security restrictions, safeguards, or debugging mechanisms. + * We only have what the kernel provides. + * ABSOLUTE POWER OVER THE MACHINE!! + */ + + /* + * To setup a stack, make the esp register point to the top of the stack. + * We have to do this in assembly since C wont even function without the stack. + */ + mov $stack_top, %esp + + /* + * This is a good point to initialize crucial processor state before our + * high level kernel is entered. + * It's best to minize early environment where crucial features are offline. + * The processor is not fully initialized yet, therefore + * features such as floating point instructions and instruction set extentions + * are ALSO not initialized. + * The GDT (global descriptor table) should be loaded here. + * Paging should be enabled here. + */ + + /* + * Enter our high level kernel. + * the ABI (?) requres the stack is 16-byte aligned at the time of + * the call instruction (which afterward will push the return pointer + * of size 4 bytes). + * The stack was 16-byte aligned above, and we have pushed a multiple of + * 16 bytes to the stack (we have pushed zero bytes so far), so + * the alignment is preserved, and the call is well defined. + */ + call kernel_main + + /* + * If the system has nothing else to do, we will infinitely loop. + * To do this, we must: + * 1. Disable interrupts with `cli` + * (clear interrupt enable in eflags.) + * They are already disabled by the bootloader, so we can skip this. + * Keep in mind, we may have to do this later AND return from main + * (for some nonsensical reason) + * 2. wait for the next interrupt to arrive with `hlt` (halt instruction) + * Since interrupts are disabled, this locks up the computer (yay!) + * 3. jump to the `hlt` instruction if it ever wakes up due to a + * non-maskable interrupt occuring or due to a system management mode. + */ + cli +1: hlt + jmp 1 + +/* + * Set the size of _start to { current_location - (start of _start) } + * This is useful for debugging or for call tracing later on. + */ +.size _start, . - _start + diff --git a/src/include/print.h b/src/include/print.h new file mode 100644 index 0000000..a8e228b --- /dev/null +++ b/src/include/print.h @@ -0,0 +1,31 @@ +#pragma once + +#include <stddef.h> +#include <stdint.h> + +// Define colors for printing +enum { + PRINT_COLOR_BLACK = 0, + PRINT_COLOR_BLUE = 1, + PRINT_COLOR_GREEN = 2, + PRINT_COLOR_CYAN = 3, + PRINT_COLOR_RED = 4, + PRINT_COLOR_MAGENTA = 5, + PRINT_COLOR_BROWN = 6, + PRINT_COLOR_LIGHT_GRAY = 7, + PRINT_COLOR_DARK_GRAY = 8, + PRINT_COLOR_LIGHT_BLUE = 9, + PRINT_COLOR_LIGHT_GREEN = 10, + PRINT_COLOR_LIGHT_CYAN = 11, + PRINT_COLOR_LIGHT_RED = 12, + PRINT_COLOR_PINK = 13, + PRINT_COLOR_YELLOW = 14, + PRINT_COLOR_WHITE = 15, +}; + +// Prototypes +void print_clear(); +void print_char(char); +void print_str(char *); +void print_set_color(uint8_t, uint8_t); +void printf(const char *, ...); diff --git a/src/include/str.h b/src/include/str.h new file mode 100644 index 0000000..11b8408 --- /dev/null +++ b/src/include/str.h @@ -0,0 +1,5 @@ +#pragma once +#include <stddef.h> +char *itoa(int, char *); +size_t strlen(const char *); +char *str_reverse(char *); diff --git a/src/kernel/compile_flags.txt b/src/kernel/compile_flags.txt new file mode 100644 index 0000000..11ef7f9 --- /dev/null +++ b/src/kernel/compile_flags.txt @@ -0,0 +1 @@ +-I../include/ diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c new file mode 100644 index 0000000..50da3c6 --- /dev/null +++ b/src/kernel/kernel.c @@ -0,0 +1,21 @@ +#include "print.h" + +// Check if the compiler thinks we are targeting the wrong OS +#if defined(__linux__) +#error "Not using cross compiler!" +#endif + +// Only works for 32 bit ix86 target +#if !defined(__i386__) +#error "Must use ix86-elf compiler" +#endif + +// Color Constants + +void kernel_main(void) { + print_clear(); + print_set_color(PRINT_COLOR_YELLOW, PRINT_COLOR_BLACK); + printf("printf\n\tint: %d\n\tstring: %s\n\tchar: %c\n\tpercent: " + "%%\n\tnothing: %n", + 1287, "World! ", 't'); +} diff --git a/src/kernel/print.c b/src/kernel/print.c new file mode 100644 index 0000000..35533cd --- /dev/null +++ b/src/kernel/print.c @@ -0,0 +1,133 @@ +#include "print.h" +#include "str.h" +#include <stdarg.h> +// Constants +static const size_t NUM_COLS = 80; +static const size_t NUM_ROWS = 25; + +// Char struct +struct Char { + uint8_t character; + uint8_t color; +}; + +// Set video mem buffer +struct Char *buffer = (struct Char *)0xb8000; +// Set current row and col +size_t col = 0; +size_t row = 0; + +// Default colors +uint8_t color = PRINT_COLOR_WHITE | PRINT_COLOR_BLACK << 4; + +// clear row +void clear_row(size_t row) { + // Define empty character + struct Char empty = (struct Char){ + character : ' ', + color : color, + }; + + // For every col in row, set vmem to empty + for (size_t col = 0; col < NUM_COLS; col++) { + buffer[col + NUM_COLS * row] = empty; + } +} + +// Call clear_row for every row +void print_clear() { + for (size_t i = 0; i < NUM_ROWS; i++) { + clear_row(i); + } +} + +// newline +void print_newline() { + // Reset col and iterate row if not at bottom of screen + col = 0; + if (row < NUM_ROWS - 1) { + row++; + return; + } + + // If at bottom of screen, move all the text up one row + for (size_t row = 1; row < NUM_ROWS; row++) { + for (size_t col = 0; col < NUM_COLS; col++) { + struct Char character = buffer[col + NUM_COLS * row]; + buffer[col + NUM_COLS * (row - 1)] = character; + } + } + + // Clear + clear_row(NUM_ROWS - 1); +} + +// printchar +void print_char(char character) { + // If \n, call newline + if (character == '\n') { + print_newline(); + return; + } + + if (character == '\t') { + print_str(" \0"); + return; + } + + // if cols is too large, overflow + if (col > NUM_COLS) { + print_newline(); + } + + // set video memeory to requested character + buffer[col + NUM_COLS * row] = (struct Char){ + character : (uint8_t)character, + color : color, + }; + + // iterate col + col++; +} + +// print_str +void print_str(char *str) { + // while character != '\0' print char + for (size_t i = 0; str[i] != '\0'; ++i) + print_char(str[i]); +} + +// Set color byte to requested colors using color enum from print.h +void print_set_color(uint8_t foreground, uint8_t background) { + color = foreground + (background << 4); +} + +void printf(const char *str, ...) { + va_list args; + va_start(args, str); + char temp_str[256]; + + for (size_t i = 0; str[i] != '\0'; ++i) { + if (str[i] == '%') { + switch (str[++i]) { + case 'i': + case 'd': + print_str(itoa(va_arg(args, int), temp_str)); + break; + case 's': + print_str(va_arg(args, char *)); + break; + case 'c': + print_char(va_arg(args, int)); + break; + case '%': + print_char('%'); + case 'n': + break; + } + continue; + } + print_char(str[i]); + } + va_end(args); +} diff --git a/src/linker.ld b/src/linker.ld new file mode 100644 index 0000000..8a9d5f8 --- /dev/null +++ b/src/linker.ld @@ -0,0 +1,44 @@ +/* The bootloader witll look at this image and start execution at + * the symbol desugbated as ENTRY + * + * In this case, it is _start in boot.s + */ +ENTRY (_start) + +/* Tell where the sections of the object files (.o) will be put in the + * final image + */ +SECTIONS +{ + /* Put sections at 1MiB, the conventional place for kernels to be + * loaded at by the bootloader. */ + . = 1M; + + /* First, put the multiboot header. It is required to be put + * VERY early in the image or the bootloader wont recognize the file + * format. Next, we will put the .text section*/ + .text BLOCK(4K) : ALIGN(4K) + { + *(.multiboot) + *(.text) + } + /* Read only data */ + .rodata BLOCK(4K) : ALIGN(4K) + { + *(.rodata) + } + /* Read write data (initialized) */ + .data BLOCK(4K) : ALIGN(4K) + { + *(.data) + } + /* Read write data (uninitialized) and stack */ + .bss BLOCK(4K) : ALIGN(4K) + { + *(COMMON) + *(.bss) + } + + /* The compiler may produce other sections, but they will be put in + * a section with the same name by default. Add stuff here as needed */ +} diff --git a/src/utils/str.c b/src/utils/str.c new file mode 100644 index 0000000..499f781 --- /dev/null +++ b/src/utils/str.c @@ -0,0 +1,33 @@ +#include "str.h" +#include <stddef.h> + +size_t strlen(const char *str) { + size_t size = 0; + for (; (str[size]) != '\0'; size++) { + } + return size; +} + +char *str_reverse(char *str) { + char *str_ptr = str; + size_t size = strlen(str); + char return_str[size]; + + for (size_t i = 0; i < size; i++) + return_str[size - i - 1] = str[i]; + + for (size_t i = 0; i < size; i++) + *str_ptr++ = return_str[i]; + + return str; +} + +char *itoa(int num, char *str) { + char *str_ptr = str; + for (; num;) { + *str_ptr++ = num % 10 + 48; + num /= 10; + } + str_reverse(str); + return str; +} |
