diff --git a/README.md b/README.md index f33e5b3..2eda86a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,31 @@ # cowos Custom OS from scratch in C + +# Notes +This only supports 32bit for now, please DO NOT compile with 64bit or it'll fail to boot + +## compilation of any component +You are required to get your own cross C compiler. This is because normal C compilers for your Linux distro (assuming that you are on Linux) will optimize against your own libs and other things. This OS doesn't exist yet, so this is a big no no in OS development. Another thing is that you want to compile for 32 bit, so you'll need to cross compile anyways... + +### general c files +```bash +i386-elf-gcc -c kernel.c -o kernel.o -std=c99 -ffreestanding -O2 -Wall -Wextra # i386 is the 32 bit version of x86 +# also please compile with the -Iinclude flag to include the libraries like string.h in the arch +``` + +### "compiling" the "boot stub" +```bash +i386-elf-as boot.s -o boot.o +``` + +### linking the "compiled" boot stub w/compiled kernel img +``` +i386-elf-gcc -T linker.ld -o cowos.bin -ffreestanding -O2 -nostdlib boot.o kernel.o +``` + +## Getting it bootable +You'll need grub and mtools. First move the compiled cowos.bin to isodir/boot. Then run the following command: +```bash +grub-mkrescue isodir/ -o cowos.iso +``` +Now you can enjoy running this in qemu lol. diff --git a/isodir/boot/grub/grub.cfg b/isodir/boot/grub/grub.cfg new file mode 100644 index 0000000..74fa80f --- /dev/null +++ b/isodir/boot/grub/grub.cfg @@ -0,0 +1,3 @@ +menuentry "cowos" { + multiboot /boot/kernel.elf +} diff --git a/kernel/arch/i386/boot.s b/kernel/arch/i386/boot.s new file mode 100644 index 0000000..6091341 --- /dev/null +++ b/kernel/arch/i386/boot.s @@ -0,0 +1,72 @@ +.set ALIGN, 1<<0 /* align loaded modules on page boundaries */ +.set MEMINFO, 1<<1 /* provide memory map */ +.set FLAGS, ALIGN | MEMINFO /* Multiboot flag */ +.set MAGIC, 0x1BADB002 /* Magic number lets bootloader find the header */ +.set CHECKSUM, -(MAGIC + FLAGS) /* checksum of above, to prove we are multiboot (sanity check I think) */ + +/* so this basically declares the multiboot header that defines this program as a kernel, + and the following values are in the multiboot standard. The bootloader will look for the + first 8KiB of the kernel file, aligned with the 32-bit boundary, this means that it won't + work with 64bit until I keep on learning or I create my own bootloader (without multiboot) +*/ +.section .multiboot +.align 4 +.long MAGIC +.long FLAGS +.long CHECKSUM + +/* This is where my knowledge is not high enough but here is the summary: + - esp is NOT in the multiboot standard so the kernel has to provide it. + this basically means that it doesn't define the value of the stack register + (whatever that means) + - This allocates some room for a stack by creating a symbol at the bottom of it + then allocate 16 KiB and then creating a symbol on top. + - The stack grows downwards on x86. + +And many more, apparently it's re-aligned to System-V ABI standard as well? Not sure, but hopefully I can +continue learning +*/ +.section .bss +.align 16 +stack_bottom: +.skip 16384 # 16 KiB +stack_top: + +/* linker specifies a _start as the entry point to the kernel and the bootloader will jump to the position +however it doesn't make any sense to return to this after the bootloader is gone +*/ +.section .text +.global _start +.type _start, @function +_start: + /* We are now in... no printf and other things, kernel has FULL CONTROL of the cpu. No security also lol */ + + /* To set up a stack, this sets the esp to register to the point of the stack because it grows downwards on + on x86 systems. this is done in assembly because languages like C can't function without a stack */ + mov $stack_top, %esp + + /* so we need to initialize some critical processor states before the high-level kernel is entered. + interestingly a lot of things are done at the boot level, we should load GDT, enable paging, and add runtime + support (for C++). Crazy! + */ + + /* time to enter the kernel, + according to the guide, this is a well defined call as the stack was originally 16-byte aligned, we pushed a + multiple of 16-bytes to the stack (since 0 bytes were pushed so far), so alignment has been perserved. + */ + call kernel_main + + /* If there is nothing else to do, the computer will be in an infinite loop + - Disable interrupts with cli (they wil be disabled by the bootloader) + - Wait for next interrupt to arrive with halt instruction (hlt) + - Jump to hlt instruction if it ever wakes up due to a non-maskable + interrupt occuring or due to system management mode. + */ + cli +1: hlt + jmp 1b + +/* set the size of _start symbol to the current location minus its start. +Useful for debugging or if I implement call tracing +*/ +.size _start, . - _start diff --git a/kernel/arch/i386/linker.ld b/kernel/arch/i386/linker.ld new file mode 100644 index 0000000..b1f2eeb --- /dev/null +++ b/kernel/arch/i386/linker.ld @@ -0,0 +1,25 @@ +ENTRY(_start) /* Entry point symbol */ + +SECTIONS { + /* Load address in memory: 2MB for kernel */ + . = 2M; + + /* Multiboot header must be in the first 8KB of the file and 32-bit aligned */ + .text BLOCK(4K) : ALIGN(4K) { + *(.multiboot) + *(.text) + } + + .rodata BLOCK(4K) : ALIGN(4K) { + *(.rodata) + } + + .data BLOCK(4K) : ALIGN(4K) { + *(.data) + } + + .bss BLOCK(4K) : ALIGN(4K) { + *(COMMON) + *(.bss) + } +} diff --git a/kernel/drivers/video/vga.c b/kernel/drivers/video/vga.c new file mode 100644 index 0000000..a402f7e --- /dev/null +++ b/kernel/drivers/video/vga.c @@ -0,0 +1,61 @@ +#include "../../include/video/vga.h" +#include + +size_t term_row; +size_t term_col; +uint8_t term_color; +uint16_t* term_buf = (uint16_t*)VGA_MEMORY; + +void +term_init(void) +{ + term_row = 0; + term_col = 0; + term_color = vga_entry_color(VGA_COLOR_WHITE, VGA_COLOR_BLACK); + + for (size_t y = 0; y < VGA_HEIGHT; y++) { + for (size_t x = 0; x < VGA_WIDTH; x++) { + const size_t index = y * VGA_WIDTH + x; + term_buf[index] = vga_entry(' ', term_color); + } + } +} + +void +term_setcolor(uint8_t color) +{ + term_color = color; +} + +void +term_putentryat(char c, uint8_t color, size_t x, size_t y) +{ + const size_t index = y * VGA_HEIGHT + x; + term_buf[index] = vga_entry(c, color); +} + +void +term_putchar(char c) +{ + term_putentryat(c, term_color, term_row); + if (++term_col == VGA_WIDTH) { + term_col = 0; + if (++term_row == VGA_HEIGHT) { + term_row = 0; + } + } +} + +void +term_write(const char* data, size_t size) +{ + for (size_t i = 0; i < size; i++) { + term_putchar(data[i]; + } +} + +void +term_writestr(const char* data) +{ + term_write(data, strlen(data)); +} diff --git a/kernel/include/drivers/video/vga.h b/kernel/include/drivers/video/vga.h new file mode 100644 index 0000000..2073f7f --- /dev/null +++ b/kernel/include/drivers/video/vga.h @@ -0,0 +1,51 @@ +#ifndef VGA_H +#define VGA_H + +#include +#include + +/* hardware text mode const */ +enum vga_color { + VGA_COLOR_BLACK = 0, + VGA_COLOR_BLUE = 1, + VGA_COLOR_GREEN = 2, + VGA_COLOR_CYAN = 3, + VGA_COLOR_RED = 4, + VGA_COLOR_MAGENTA = 5, + VGA_COLOR_BROWN = 6, + VGA_COLOR_LIGHT_GREY = 7, + VGA_COLOR_DARK_GREY = 8, + VGA_COLOR_LIGHT_BLUE = 9, + VGA_COLOR_LIGHT_GREEN = 10, + VGA_COLOR_LIGHT_CYAN = 11, + VGA_COLOR_LIGHT_RED = 12, + VGA_COLOR_LIGHT_MAGENTA = 13, + VGA_COLOR_LIGHT_BROWN = 14, + VGA_COLOR_WHITE = 15, +} + +static inline uint8_t +vga_entry_color(enum vga_color fg, enum vga_color bg) +{ + return fg | bg << 4; +} + +static inline uint16_t +vga_entry(unsigned char uc, uint8_t color) +{ + return (uint16_t) uc | (uint16_t) color << 8; +} + +/* using mode 3 of VGA 80x25 */ +#define VGA_WIDTH 80 +#define VGA_HEIGHT 25 +#define VGA_MEMORY 0xB8000 /* VGA memory location */ + +void term init(void); +void term_setcolor(uint8_t color); +void term_putentryat(char c, uint8_t color, size_t x, size_t y); +void term_putchar(char c); +void term_write(const char* data, size_t size); +void term_writestr(const char* data); + +#endif #VGA_H diff --git a/kernel/include/lib/string.h b/kernel/include/lib/string.h new file mode 100644 index 0000000..29e142c --- /dev/null +++ b/kernel/include/lib/string.h @@ -0,0 +1,8 @@ +#ifndef STRING_H +#define STRING_H + +#include + +size_t strlen(const char* str); + +#endif // STRING_H diff --git a/kernel/init/kernel.c b/kernel/init/kernel.c new file mode 100644 index 0000000..f230534 --- /dev/null +++ b/kernel/init/kernel.c @@ -0,0 +1,10 @@ +#include "../include/drivers/video/vga.h" +#include + +void +kernel_main(void) +{ + term_init(); + + term_writestr("Hello World!\n"); +} diff --git a/kernel/lib/string.c b/kernel/lib/string.c new file mode 100644 index 0000000..55b1071 --- /dev/null +++ b/kernel/lib/string.c @@ -0,0 +1,11 @@ +#include "../include/lib/string.h" +#include +#include + +size_t +strlen(const char* str) +{ + size_t len = 0; + while (str[len]) len++; + return len; +}