commit 59e39d9842acb450fc8367e57bc412e2736bee8c
parent 13389f961718039f83366315a96324629249721c
Author: cowmonk <rekketstone@duck.com>
Date: Thu, 17 Apr 2025 21:32:48 -0700
Branch sync and push, reformated and made the OS look more "official"
Diffstat:
9 files changed, 270 insertions(+), 0 deletions(-)
diff --git 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
@@ -0,0 +1,3 @@
+menuentry "cowos" {
+ multiboot /boot/kernel.elf
+}
diff --git a/kernel/arch/i386/boot.s 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
@@ -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
@@ -0,0 +1,61 @@
+#include "../../include/video/vga.h"
+#include <string.h>
+
+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
@@ -0,0 +1,51 @@
+#ifndef VGA_H
+#define VGA_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+/* 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
@@ -0,0 +1,8 @@
+#ifndef STRING_H
+#define STRING_H
+
+#include <stddef.h>
+
+size_t strlen(const char* str);
+
+#endif // STRING_H
diff --git a/kernel/init/kernel.c b/kernel/init/kernel.c
@@ -0,0 +1,10 @@
+#include "../include/drivers/video/vga.h"
+#include <string.h>
+
+void
+kernel_main(void)
+{
+ term_init();
+
+ term_writestr("Hello World!\n");
+}
diff --git a/kernel/lib/string.c b/kernel/lib/string.c
@@ -0,0 +1,11 @@
+#include "../include/lib/string.h"
+#include <stddef.h>
+#include <stdint.h>
+
+size_t
+strlen(const char* str)
+{
+ size_t len = 0;
+ while (str[len]) len++;
+ return len;
+}