From 9f8ca3a60c35cd3492f2da6469dae4d81c69eb17 Mon Sep 17 00:00:00 2001 From: Gregory Bowne Date: Wed, 17 Dec 2025 05:47:59 -0800 Subject: [PATCH 01/47] Create ps2.h --- kernel/ps2.h | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 kernel/ps2.h diff --git a/kernel/ps2.h b/kernel/ps2.h new file mode 100644 index 0000000..6b3d3d3 --- /dev/null +++ b/kernel/ps2.h @@ -0,0 +1,45 @@ +#ifndef PS2_H +#define PS2_H + +#include +#include + +/* I/O Ports */ +#define PS2_DATA_PORT 0x60 +#define PS2_STATUS_REG 0x64 +#define PS2_COMMAND_REG 0x64 + +/* Status Register Bits */ +#define PS2_STATUS_OUTPUT 0x01 // 1 = Data ready to be read +#define PS2_STATUS_INPUT 0x02 // 1 = Controller busy, don't write yet +#define PS2_STATUS_SYS 0x04 // System flag +#define PS2_STATUS_CMD_DATA 0x08 // 0 = Data written to 0x60, 1 = Cmd to 0x64 +#define PS2_STATUS_MOUSE 0x20 // 1 = Mouse data, 0 = Keyboard data + +/* Controller Commands */ +#define PS2_CMD_READ_CONFIG 0x20 +#define PS2_CMD_WRITE_CONFIG 0x60 +#define PS2_CMD_DISABLE_MS 0xA7 +#define PS2_CMD_ENABLE_MS 0xA8 +#define PS2_CMD_DISABLE_KB 0xAD +#define PS2_CMD_ENABLE_KB 0xAE +#define PS2_CMD_WRITE_MOUSE 0xD4 + +/* Mouse Commands */ +#define MOUSE_CMD_SET_DEFAULTS 0xF6 +#define MOUSE_CMD_ENABLE_SCAN 0xF4 + +typedef struct { + int8_t x_delta; + int8_t y_delta; + bool left_button; + bool right_button; + bool middle_button; +} mouse_state_t; + +/* Public API */ +void ps2_init(void); +void ps2_keyboard_handler(void); +void ps2_mouse_handler(void); + +#endif From 4e8b13ad77ef67cf3ca0e94ce72b131e4dee4de5 Mon Sep 17 00:00:00 2001 From: Gregory Bowne Date: Wed, 17 Dec 2025 05:50:02 -0800 Subject: [PATCH 02/47] Create ps2.c add initial ps/2 driver code. This will need wired up to be used --- kernel/ps2.c | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 kernel/ps2.c diff --git a/kernel/ps2.c b/kernel/ps2.c new file mode 100644 index 0000000..68f4fa9 --- /dev/null +++ b/kernel/ps2.c @@ -0,0 +1,117 @@ +#include "ps2.h" + +/* --- Low Level I/O Helpers --- */ +static inline void outb(uint16_t port, uint8_t val) { + asm volatile ("outb %0, %1" : : "a"(val), "Nd"(port)); +} + +static inline uint8_t inb(uint16_t port) { + uint8_t ret; + asm volatile ("inb %1, %0" : "=a"(ret) : "Nd"(port)); + return ret; +} + +/* --- Controller Synchronization --- */ + +// Wait until the controller is ready to receive a byte +static void ps2_wait_write() { + while (inb(PS2_STATUS_REG) & PS2_STATUS_INPUT); +} + +// Wait until the controller has a byte for us to read +static void ps2_wait_read() { + while (!(inb(PS2_STATUS_REG) & PS2_STATUS_OUTPUT)); +} + +/* --- Initialization --- */ + +void ps2_write_device(uint8_t command) { + ps2_wait_write(); + outb(PS2_DATA_PORT, command); +} + +void ps2_write_mouse(uint8_t data) { + ps2_wait_write(); + outb(PS2_COMMAND_REG, PS2_CMD_WRITE_MOUSE); // "Next byte goes to mouse" + ps2_wait_write(); + outb(PS2_DATA_PORT, data); +} + +void ps2_init(void) { + // 1. Disable Devices + ps2_wait_write(); + outb(PS2_COMMAND_REG, PS2_CMD_DISABLE_KB); + ps2_wait_write(); + outb(PS2_COMMAND_REG, PS2_CMD_DISABLE_MS); + + // 2. Flush Output Buffer + while (inb(PS2_STATUS_REG) & PS2_STATUS_OUTPUT) { + inb(PS2_DATA_PORT); + } + + // 3. Set Controller Configuration Byte + // Bit 0: KB Interrupt, Bit 1: Mouse Interrupt, Bit 6: Translation + ps2_wait_write(); + outb(PS2_COMMAND_REG, PS2_CMD_READ_CONFIG); + ps2_wait_read(); + uint8_t status = inb(PS2_DATA_PORT); + status |= (1 << 0) | (1 << 1); // Enable IRQ 1 and IRQ 12 + + ps2_wait_write(); + outb(PS2_COMMAND_REG, PS2_CMD_WRITE_CONFIG); + ps2_wait_write(); + outb(PS2_DATA_PORT, status); + + // 4. Enable Devices + ps2_wait_write(); + outb(PS2_COMMAND_REG, PS2_CMD_ENABLE_KB); + ps2_wait_write(); + outb(PS2_COMMAND_REG, PS2_CMD_ENABLE_MS); + + // 5. Initialize Mouse (The mouse won't send IRQs until you tell it to) + ps2_write_mouse(MOUSE_CMD_SET_DEFAULTS); + ps2_wait_read(); inb(PS2_DATA_PORT); // Read ACK (0xFA) + + ps2_write_mouse(MOUSE_CMD_ENABLE_SCAN); + ps2_wait_read(); inb(PS2_DATA_PORT); // Read ACK (0xFA) +} + +/* --- IRQ Handlers --- */ + +// Called from IRQ 1 (Keyboard) +void ps2_keyboard_handler(void) { + uint8_t scancode = inb(PS2_DATA_PORT); + // Process scancode (e.g., put it into a circular buffer) +} + +// Called from IRQ 12 (Mouse) +static uint8_t mouse_cycle = 0; +static uint8_t mouse_bytes[3]; + +void ps2_mouse_handler(void) { + uint8_t status = inb(PS2_STATUS_REG); + + // Ensure this is actually mouse data + if (!(status & PS2_STATUS_MOUSE)) return; + + mouse_bytes[mouse_cycle++] = inb(PS2_DATA_PORT); + + if (mouse_cycle == 3) { + mouse_cycle = 0; + + // Byte 0: Flags (Buttons, Signs) + // Byte 1: X Delta + // Byte 2: Y Delta + + mouse_state_t state; + state.left_button = (mouse_bytes[0] & 0x01); + state.right_button = (mouse_bytes[0] & 0x02); + state.middle_button = (mouse_bytes[0] & 0x04); + + // Handle negative deltas (signed 9-bit logic) + state.x_delta = (int8_t)mouse_bytes[1]; + state.y_delta = (int8_t)mouse_bytes[2]; + + // Update your kernel's internal mouse position here + } +} From cc92ade8fd3056a21d24fac76d376463e8f9b9c2 Mon Sep 17 00:00:00 2001 From: Gregory Bowne Date: Mon, 26 Jan 2026 17:06:44 -0800 Subject: [PATCH 03/47] Update ps2.c Fixing asm to __asm__ --- kernel/ps2.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/kernel/ps2.c b/kernel/ps2.c index 68f4fa9..283fbb6 100644 --- a/kernel/ps2.c +++ b/kernel/ps2.c @@ -1,13 +1,12 @@ #include "ps2.h" -/* --- Low Level I/O Helpers --- */ static inline void outb(uint16_t port, uint8_t val) { - asm volatile ("outb %0, %1" : : "a"(val), "Nd"(port)); + __asm__ __volatile__ ("outb %b0, %w1" : : "a"(val), "Nd"(port)); } static inline uint8_t inb(uint16_t port) { uint8_t ret; - asm volatile ("inb %1, %0" : "=a"(ret) : "Nd"(port)); + __asm__ __volatile__ ("inb %w1, %b0" : "=a"(ret) : "Nd"(port)); return ret; } From a37f94de44462f791e9b1f2a778ece8b081cd3e0 Mon Sep 17 00:00:00 2001 From: Gregory Bowne Date: Tue, 27 Jan 2026 08:16:44 -0800 Subject: [PATCH 04/47] Update ps2.c Fixed the `__volatile__` into a volatile keyword --- kernel/ps2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/ps2.c b/kernel/ps2.c index 283fbb6..3816b9b 100644 --- a/kernel/ps2.c +++ b/kernel/ps2.c @@ -1,12 +1,12 @@ #include "ps2.h" static inline void outb(uint16_t port, uint8_t val) { - __asm__ __volatile__ ("outb %b0, %w1" : : "a"(val), "Nd"(port)); + __asm__ volatile ("outb %b0, %w1" : : "a"(val), "Nd"(port)); } static inline uint8_t inb(uint16_t port) { uint8_t ret; - __asm__ __volatile__ ("inb %w1, %b0" : "=a"(ret) : "Nd"(port)); + __asm__ volatile ("inb %w1, %b0" : "=a"(ret) : "Nd"(port)); return ret; } From 903061551c8586638ffded560d644b336cf86599 Mon Sep 17 00:00:00 2001 From: Gregory Bowne Date: Tue, 27 Jan 2026 12:41:17 -0800 Subject: [PATCH 05/47] Update ps2.c Remove the definition of the in/out assembly and add io.h include instead of inline --- kernel/ps2.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/kernel/ps2.c b/kernel/ps2.c index 3816b9b..d908bd9 100644 --- a/kernel/ps2.c +++ b/kernel/ps2.c @@ -1,14 +1,5 @@ #include "ps2.h" - -static inline void outb(uint16_t port, uint8_t val) { - __asm__ volatile ("outb %b0, %w1" : : "a"(val), "Nd"(port)); -} - -static inline uint8_t inb(uint16_t port) { - uint8_t ret; - __asm__ volatile ("inb %w1, %b0" : "=a"(ret) : "Nd"(port)); - return ret; -} +#include "io.h" /* --- Controller Synchronization --- */ From c503709ff47fc61b20e07697d284a5a06c717eae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Borna=20=C5=A0o=C5=A1tari=C4=87?= Date: Fri, 19 Dec 2025 22:47:34 +0100 Subject: [PATCH 06/47] add configure script for setting up cross compilation tools --- .gitignore | 3 +- Makefile | 13 +++-- configure | 160 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+), 5 deletions(-) create mode 100755 configure diff --git a/.gitignore b/.gitignore index c795b05..26b8d0d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -build \ No newline at end of file +build +cross diff --git a/Makefile b/Makefile index be85c52..55f5094 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,12 @@ +MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) +MAKEFILE_DIR := $(dir $(MAKEFILE_PATH)) + AS = nasm ASFLAGS = -f elf32 -g -F dwarf -CC = gcc -LD = ld +CC = $(MAKEFILE_DIR)cross/bin/i386-elf-gcc +LD = $(MAKEFILE_DIR)cross/bin/i386-elf-ld QEMU= qemu-system-i386 +OBJCOPY = $(MAKEFILE_DIR)cross/bin/i386-elf-objcopy BUILD_DIR = build DISK_IMG = $(BUILD_DIR)/disk.img @@ -19,7 +23,7 @@ all: $(DISK_IMG) stage1: $(BUILD_DIR) $(AS) $(ASFLAGS) -o $(BUILD_DIR)/$@.o bootloader/$@.asm $(LD) -Ttext=0x7c00 -melf_i386 -o $(BUILD_DIR)/$@.elf $(BUILD_DIR)/$@.o - objcopy -O binary $(BUILD_DIR)/$@.elf $(BUILD_DIR)/$@.bin + $(OBJCOPY) -O binary $(BUILD_DIR)/$@.elf $(BUILD_DIR)/$@.bin # NOTE: Stage2 final size should be checked against `$(STAGE2_SIZE)` by the build system to avoid an overflow. # Alternatively, convey the final stage2 size through other means to stage1. @@ -27,7 +31,7 @@ stage2: $(BUILD_DIR) $(AS) $(ASFLAGS) -o $(BUILD_DIR)/stage2.o bootloader/stage2.asm $(CC) -std=c11 -ffreestanding -nostdlib -fno-stack-protector -m32 -g -c -o $(BUILD_DIR)/stage2_load.o bootloader/stage2_load.c $(LD) -Tbootloader/stage2.ld -melf_i386 -o $(BUILD_DIR)/$@.elf $(BUILD_DIR)/stage2.o $(BUILD_DIR)/stage2_load.o - objcopy -O binary $(BUILD_DIR)/$@.elf $(BUILD_DIR)/$@.bin + $(OBJCOPY) -O binary $(BUILD_DIR)/$@.elf $(BUILD_DIR)/$@.bin truncate -s $(STAGE2_SIZE) $(BUILD_DIR)/$@.bin $(BUILD_DIR)/asm_%.o: kernel/%.asm @@ -56,3 +60,4 @@ gdb: clean: rm -rf $(BUILD_DIR) + rm -rf $(CROSS_DIR) diff --git a/configure b/configure new file mode 100755 index 0000000..d53fd78 --- /dev/null +++ b/configure @@ -0,0 +1,160 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Configuration +TARGET="i386-elf" +BINUTILS_VERSION="2.45" +GCC_VERSION="15.2.0" + +# Paths +SCRIPT_PATH="$(realpath "${BASH_SOURCE[0]}")" +SCRIPT_DIR="$(dirname "$SCRIPT_PATH")" +PREFIX="$SCRIPT_DIR/cross" +SRC_DIR="$PREFIX/src" + +BINUTILS_SRC="$SRC_DIR/binutils-$BINUTILS_VERSION" +BINUTILS_BUILD="$PREFIX/build-binutils" +GCC_SRC="$SRC_DIR/gcc-$GCC_VERSION" +GCC_BUILD="$PREFIX/build-gcc" + +# Flags +DEBUG=0 +HELP=0 + +# Parse arguments +for arg in "$@"; do + case "$arg" in + -h|--help) + HELP=1 + ;; + -d|--debug) + DEBUG=1 + ;; + *) + echo "Unknown option: $arg" + echo "Use -h or --help for usage information" + exit 1 + ;; + esac +done + +# Show help +if [[ "$HELP" -eq 1 ]]; then + cat << EOF +Usage: $0 [OPTIONS] + +Build a cross-compiler toolchain for $TARGET. + +OPTIONS: + -h, --help Show this help message + -d, --debug Enable debug mode (set -x) + +This script will: + 1. Download binutils $BINUTILS_VERSION and GCC $GCC_VERSION + 2. Build and install them to: $PREFIX + +EOF + exit 0 +fi + +# Enable debug mode +if [[ "$DEBUG" -eq 1 ]]; then + set -x +fi + +# Print configuration +cat << EOF + +=== Build Configuration === +Target : $TARGET +Prefix : $PREFIX +Binutils : $BINUTILS_VERSION +GCC : $GCC_VERSION +=========================== + +EOF + +# Create directory structure +echo "Setting up directories..." +mkdir -p "$SRC_DIR" + +# Download sources +cd "$SRC_DIR" + +if [[ ! -d "$BINUTILS_SRC" ]]; then + echo "Downloading binutils $BINUTILS_VERSION..." + wget "https://ftp.gnu.org/gnu/binutils/binutils-$BINUTILS_VERSION.tar.gz" + echo "Extracting binutils..." + tar xf "binutils-$BINUTILS_VERSION.tar.gz" + rm "binutils-$BINUTILS_VERSION.tar.gz" +else + echo "Binutils source already exists, skipping download" +fi + +if [[ ! -d "$GCC_SRC" ]]; then + echo "Downloading GCC $GCC_VERSION..." + wget "https://ftp.gnu.org/gnu/gcc/gcc-$GCC_VERSION/gcc-$GCC_VERSION.tar.gz" + echo "Extracting GCC..." + tar xf "gcc-$GCC_VERSION.tar.gz" + rm "gcc-$GCC_VERSION.tar.gz" +else + echo "GCC source already exists, skipping download" +fi + +# Download GCC prerequisites +if [[ ! -d "$GCC_SRC/gmp" ]]; then + echo "Downloading GCC prerequisites..." + cd "$GCC_SRC" + ./contrib/download_prerequisites + cd "$SRC_DIR" +else + echo "GCC prerequisites already downloaded, skipping" +fi + +# Build binutils +if [[ ! -f "$PREFIX/bin/$TARGET-ld" ]]; then + echo "Building binutils..." + mkdir -p "$BINUTILS_BUILD" + cd "$BINUTILS_BUILD" + + "$BINUTILS_SRC/configure" \ + --target="$TARGET" \ + --prefix="$PREFIX" \ + --with-sysroot \ + --disable-nls \ + --disable-werror + + make -j"$(nproc)" + make install +else + echo "Binutils already installed, skipping build" +fi + +# Build GCC +if [[ ! -f "$PREFIX/bin/$TARGET-gcc" ]]; then + echo "Building GCC..." + mkdir -p "$GCC_BUILD" + cd "$GCC_BUILD" + + "$GCC_SRC/configure" \ + --target="$TARGET" \ + --prefix="$PREFIX" \ + --disable-nls \ + --enable-languages=c \ + --without-headers + + make all-gcc -j"$(nproc)" + make all-target-libgcc -j"$(nproc)" + make install-gcc + make install-target-libgcc +else + echo "GCC already installed, skipping build" +fi + +cd "$SCRIPT_DIR" + +echo "" +echo "=== Build Complete ===" +echo "Toolchain installed to: $PREFIX" +echo "Add to PATH: export PATH=\"$PREFIX/bin:\$PATH\"" +echo "======================" From a182bbca198193712802067fcb66db28e05cbf74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Borna=20=C5=A0o=C5=A1tari=C4=87?= Date: Fri, 19 Dec 2025 23:41:32 +0100 Subject: [PATCH 07/47] generate .build.env as part of configure script --- .gitignore | 1 + configure | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 26b8d0d..7012c23 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ +.build.env build cross diff --git a/configure b/configure index d53fd78..2ba63c2 100755 --- a/configure +++ b/configure @@ -153,8 +153,17 @@ fi cd "$SCRIPT_DIR" +# Generate .build.env file +cat > .build.env << EOF +# Generated by configure on $(date) +# Source this file to add the cross-compiler toolchain to your PATH +export PATH="$PREFIX/bin:\$PATH" +EOF + echo "" echo "=== Build Complete ===" echo "Toolchain installed to: $PREFIX" -echo "Add to PATH: export PATH=\"$PREFIX/bin:\$PATH\"" +echo "" +echo "To use the toolchain, run:" +echo " source .build.env" echo "======================" From ac0fde28a07e507a2e5baf8af8a891c4ffa37729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Borna=20=C5=A0o=C5=A1tari=C4=87?= Date: Fri, 19 Dec 2025 23:44:00 +0100 Subject: [PATCH 08/47] update readme --- README.md | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 2232bcf..791f89f 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![Platform](https://img.shields.io/badge/platform-x86_IA32-lightgrey?style=flat-square)](https://en.wikipedia.org/wiki/IA-32) [![Made with](https://img.shields.io/badge/made%20with-C%20%26%20NASM-9cf?style=flat-square)](#) -> **ClassicOS** is a 32-bit Intel x86 operating system built from scratch using C, NASM, and GCC. +> **ClassicOS** is a 32-bit Intel x86 operating system built from scratch using C, NASM, and GCC. > Designed for 386, 486, and Pentium-class CPUs, it runs in protected mode, outputs to VGA text mode and serial ports, and supports floppy/HDD boot with basic FAT support. --- @@ -35,6 +35,7 @@ You’ll need the following tools installed: - `qemu-system-i386` Optional: + - `gdb` - `vncviewer` (TigerVNC or similar) @@ -42,13 +43,27 @@ Optional: ## 🛠️ Building ClassicOS -Clone and build: +Clone repository: -```bash +```sh git clone https://github.com/gbowne1/ClassicOS.git cd ClassicOS -make ``` -build kernel -for %f in (*.c) do gcc -m32 -O0 -Wall -Wextra -Werror -pedantic -ffreestanding -nostdlib -fno-pic -fno-stack-protector -fno-pie -march=i386 -mtune=i386 -c "%f" -o "%f.o" +Run `configure` script to build a cross-compiler toolchain for `i386-elf`: + +```sh +./configure +``` + +Source the `.build.env` file to add the cross-compiler toolchain to your PATH: + +```sh +source .build.env +``` + +Build the kernel: + +```sh +make +``` From 7bda5c25b8c1847f9f25a8e7b124d39827f604a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Borna=20=C5=A0o=C5=A1tari=C4=87?= Date: Fri, 19 Dec 2025 23:46:03 +0100 Subject: [PATCH 09/47] lessen indirection in the makefile --- Makefile | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 55f5094..a99cafe 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,9 @@ -MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) -MAKEFILE_DIR := $(dir $(MAKEFILE_PATH)) - AS = nasm ASFLAGS = -f elf32 -g -F dwarf -CC = $(MAKEFILE_DIR)cross/bin/i386-elf-gcc -LD = $(MAKEFILE_DIR)cross/bin/i386-elf-ld +CC = i386-elf-gcc +LD = i386-elf-ld QEMU= qemu-system-i386 -OBJCOPY = $(MAKEFILE_DIR)cross/bin/i386-elf-objcopy +OBJCOPY = i386-elf-objcopy BUILD_DIR = build DISK_IMG = $(BUILD_DIR)/disk.img From 8b1ea16c56aef7ffeb948621cc545c39d8d00ca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Borna=20=C5=A0o=C5=A1tari=C4=87?= Date: Sat, 27 Dec 2025 11:19:42 +0100 Subject: [PATCH 10/47] initial implementation of klibc fix linker error about ctx_switch --- .gitignore | 2 + Makefile | 21 ++- .../{context_switch.s => context_switch.asm} | 2 +- kernel/memory.c | 123 +----------------- kernel/types.h | 1 - kernel/utils.c | 7 - kernel/utils.h | 2 + klibc/include/stdarg.h | 14 ++ klibc/include/stdbool.h | 6 + klibc/include/stddef.h | 10 ++ klibc/include/stdint.h | 16 +++ klibc/include/stdio.h | 4 + klibc/include/stdlib.h | 4 + klibc/include/string.h | 14 ++ klibc/src/string.c | 107 +++++++++++++++ 15 files changed, 198 insertions(+), 135 deletions(-) rename kernel/{context_switch.s => context_switch.asm} (98%) create mode 100644 klibc/include/stdarg.h create mode 100644 klibc/include/stdbool.h create mode 100644 klibc/include/stddef.h create mode 100644 klibc/include/stdint.h create mode 100644 klibc/include/stdio.h create mode 100644 klibc/include/stdlib.h create mode 100644 klibc/include/string.h create mode 100644 klibc/src/string.c diff --git a/.gitignore b/.gitignore index 7012c23..432f88b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ .build.env build cross +.cache/ +compile_commands.json diff --git a/Makefile b/Makefile index a99cafe..1787090 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,7 @@ QEMU= qemu-system-i386 OBJCOPY = i386-elf-objcopy BUILD_DIR = build +CROSS_DIR = cross DISK_IMG = $(BUILD_DIR)/disk.img STAGE2_SIZE = 2048 @@ -14,6 +15,9 @@ KERNEL_ASM_SRC = $(wildcard kernel/*.asm) KERNEL_OBJ = $(patsubst kernel/%.c, $(BUILD_DIR)/%.o, $(KERNEL_C_SRC)) KERNEL_OBJ += $(patsubst kernel/%.asm, $(BUILD_DIR)/asm_%.o, $(KERNEL_ASM_SRC)) +KLIBC_SRC = $(wildcard klibc/src/*.c) +KLIBC_OBJ = $(patsubst klibc/src/%.c, $(BUILD_DIR)/klibc/%.o, $(KLIBC_SRC)) + all: $(DISK_IMG) .PHONY: stage1 stage2 kernel run gdb clean @@ -26,7 +30,7 @@ stage1: $(BUILD_DIR) # Alternatively, convey the final stage2 size through other means to stage1. stage2: $(BUILD_DIR) $(AS) $(ASFLAGS) -o $(BUILD_DIR)/stage2.o bootloader/stage2.asm - $(CC) -std=c11 -ffreestanding -nostdlib -fno-stack-protector -m32 -g -c -o $(BUILD_DIR)/stage2_load.o bootloader/stage2_load.c + $(CC) -std=c11 -ffreestanding -nostdlib -nostdinc -fno-stack-protector -m32 -Iklibc/include -g -c -o $(BUILD_DIR)/stage2_load.o bootloader/stage2_load.c $(LD) -Tbootloader/stage2.ld -melf_i386 -o $(BUILD_DIR)/$@.elf $(BUILD_DIR)/stage2.o $(BUILD_DIR)/stage2_load.o $(OBJCOPY) -O binary $(BUILD_DIR)/$@.elf $(BUILD_DIR)/$@.bin truncate -s $(STAGE2_SIZE) $(BUILD_DIR)/$@.bin @@ -35,10 +39,13 @@ $(BUILD_DIR)/asm_%.o: kernel/%.asm $(AS) $(ASFLAGS) -o $@ $< $(BUILD_DIR)/%.o: kernel/%.c - $(CC) -std=c11 -ffreestanding -nostdlib -fno-stack-protector -m32 -g -c -o $@ $< + $(CC) -std=c11 -ffreestanding -nostdlib -nostdinc -fno-stack-protector -m32 -Iklibc/include -g -c -o $@ $< -kernel: $(KERNEL_OBJ) | $(BUILD_DIR) - $(LD) -melf_i386 -Tkernel/linker.ld -o $(BUILD_DIR)/kernel.elf $(KERNEL_OBJ) +$(BUILD_DIR)/klibc/%.o: klibc/src/%.c + $(CC) -std=c11 -ffreestanding -nostdlib -nostdinc -fno-stack-protector -m32 -Iklibc/include -g -c -o $@ $< + +kernel: $(KERNEL_OBJ) | $(BUILD_DIR) $(KLIBC_OBJ) + $(LD) -melf_i386 -Tkernel/linker.ld -o $(BUILD_DIR)/kernel.elf $(KERNEL_OBJ) $(KLIBC_OBJ) $(DISK_IMG): stage1 stage2 kernel dd if=$(BUILD_DIR)/stage1.bin of=$@ @@ -48,6 +55,7 @@ $(DISK_IMG): stage1 stage2 kernel $(BUILD_DIR): mkdir -p $@ + mkdir -p $(BUILD_DIR)/klibc run: qemu-system-i386 -s -S $(DISK_IMG) @@ -57,4 +65,9 @@ gdb: clean: rm -rf $(BUILD_DIR) + +clean-cross: rm -rf $(CROSS_DIR) + rm -rf .build.env + +clean-all: clean clean-cross diff --git a/kernel/context_switch.s b/kernel/context_switch.asm similarity index 98% rename from kernel/context_switch.s rename to kernel/context_switch.asm index 0e68648..89a910d 100644 --- a/kernel/context_switch.s +++ b/kernel/context_switch.asm @@ -1,4 +1,4 @@ -.global ctx_switch +global ctx_switch ; void ctx_switch(uint32_t **old_sp_ptr, uint32_t *new_sp); ; Arguments on stack (cdecl convention): diff --git a/kernel/memory.c b/kernel/memory.c index 9f4d02e..dc6aae2 100644 --- a/kernel/memory.c +++ b/kernel/memory.c @@ -1,6 +1,6 @@ #include "memory.h" -/* note: this is a stub, please use care as theres duplicate functions in utils implementation +/* note: this is a stub, please use care as theres duplicate functions in utils implementation /* --------------------------------------------------------------------- * * Helper: copy a single byte (used by both memcpy and memmove) * --------------------------------------------------------------------- */ @@ -15,124 +15,3 @@ static inline void byte_copy_backward(uint8_t *dst, const uint8_t *src, size_t n while (n--) *--dst = *--src; } -/* --------------------------------------------------------------------- * - * memcpy – no overlap allowed (behaviour undefined if overlap) - * --------------------------------------------------------------------- */ -void *memcpy(void *restrict dst, const void *restrict src, size_t n) -{ - uint8_t *d = (uint8_t *)dst; - const uint8_t *s = (const uint8_t *)src; - -#if defined(MEMORY_OPTIMIZED) - /* Align destination to 4-byte boundary */ - size_t align = (uintptr_t)d & 3U; - if (align) { - size_t head = 4 - align; - if (head > n) head = n; - byte_copy_forward(d, s, head); - d += head; s += head; n -= head; - } - - /* 32-bit word copy – safe because we already aligned dst */ - { - uint32_t *d32 = (uint32_t *)d; - const uint32_t *s32 = (const uint32_t *)s; - size_t words = n / 4; - while (words--) *d32++ = *s32++; - d = (uint8_t *)d32; - s = (const uint8_t *)s32; - n &= 3; - } -#endif - - byte_copy_forward(d, s, n); - return dst; -} - -/* --------------------------------------------------------------------- * - * memmove – handles overlapping regions correctly - * --------------------------------------------------------------------- */ -void *memmove(void *dst, const void *src, size_t n) -{ - uint8_t *d = (uint8_t *)dst; - const uint8_t *s = (const uint8_t *)src; - - if (n == 0 || dst == src) - return dst; - - if (d < s) { /* copy forward */ -#if defined(MEMORY_OPTIMIZED) - /* Same fast path as memcpy when no overlap */ - size_t align = (uintptr_t)d & 3U; - if (align) { - size_t head = 4 - align; - if (head > n) head = n; - byte_copy_forward(d, s, head); - d += head; s += head; n -= head; - } - { - uint32_t *d32 = (uint32_t *)d; - const uint32_t *s32 = (const uint32_t *)s; - size_t words = n / 4; - while (words--) *d32++ = *s32++; - d = (uint8_t *)d32; - s = (const uint8_t *)s32; - n &= 3; - } -#endif - byte_copy_forward(d, s, n); - } else { /* copy backward */ - byte_copy_backward(d, s, n); - } - return dst; -} - -/* --------------------------------------------------------------------- * - * memcmp – lexicographical compare - * --------------------------------------------------------------------- */ -int memcmp(const void *s1, const void *s2, size_t n) -{ - const uint8_t *a = (const uint8_t *)s1; - const uint8_t *b = (const uint8_t *)s2; - -#if defined(MEMORY_OPTIMIZED) - /* Align to 4-byte boundary */ - size_t align = (uintptr_t)a & 3U; - if (align && align == ((uintptr_t)b & 3U)) { - size_t head = 4 - align; - if (head > n) head = n; - while (head--) { - int diff = *a++ - *b++; - if (diff) return diff; - } - n -= head; - } - - { - const uint32_t *a32 = (const uint32_t *)a; - const uint32_t *b32 = (const uint32_t *)b; - size_t words = n / 4; - while (words--) { - uint32_t va = *a32++, vb = *b32++; - if (va != vb) { - /* byte-wise fallback for the differing word */ - const uint8_t *pa = (const uint8_t *)(a32 - 1); - const uint8_t *pb = (const uint8_t *)(b32 - 1); - for (int i = 0; i < 4; ++i) { - int diff = pa[i] - pb[i]; - if (diff) return diff; - } - } - } - a = (const uint8_t *)a32; - b = (const uint8_t *)b32; - n &= 3; - } -#endif - - while (n--) { - int diff = *a++ - *b++; - if (diff) return diff; - } - return 0; -} diff --git a/kernel/types.h b/kernel/types.h index 32fc7ff..332aa4e 100644 --- a/kernel/types.h +++ b/kernel/types.h @@ -27,7 +27,6 @@ typedef enum { false = 0, true = 1 } bool; // ---------------------------- // OS subsystem types // ---------------------------- -typedef uint32_t size_t; typedef int32_t ssize_t; typedef uint32_t phys_addr_t; // Physical address diff --git a/kernel/utils.c b/kernel/utils.c index 0cb4b04..a0f2035 100644 --- a/kernel/utils.c +++ b/kernel/utils.c @@ -76,10 +76,3 @@ char* utoa(unsigned int value, char* str, int base) { reverse(str, i); return str; } - -void *memset(void *dest, int value, size_t len) { - unsigned char *ptr = (unsigned char *)dest; - while (len-- > 0) - *ptr++ = (unsigned char)value; - return dest; -} diff --git a/kernel/utils.h b/kernel/utils.h index 802db8c..53fab8a 100644 --- a/kernel/utils.h +++ b/kernel/utils.h @@ -1,6 +1,8 @@ #ifndef UTILS_H #define UTILS_H +#include + #include "types.h" // Convert integer to string (base is typically 10, 16, etc.) diff --git a/klibc/include/stdarg.h b/klibc/include/stdarg.h new file mode 100644 index 0000000..07d50ca --- /dev/null +++ b/klibc/include/stdarg.h @@ -0,0 +1,14 @@ +#ifndef CLASSICOS_KLIBC_STDARG_H +#define CLASSICOS_KLIBC_STDARG_H + +typedef __builtin_va_list va_list; + +#ifndef va_start +#define va_start(ap, param) __builtin_va_start(ap, param) +#endif + +#define va_end(ap) __builtin_va_end(ap) +#define va_arg(ap, type) __builtin_va_arg(ap, type) +#define va_copy(dest, src) __builtin_va_copy(dest, src) + +#endif // CLASSICOS_KLIBC_STDARG_H diff --git a/klibc/include/stdbool.h b/klibc/include/stdbool.h new file mode 100644 index 0000000..0cd5393 --- /dev/null +++ b/klibc/include/stdbool.h @@ -0,0 +1,6 @@ +#ifndef CLASSICOS_KLIBC_STDBOOL_H +#define CLASSICOS_KLIBC_STDBOOL_H + +typedef enum { false = 0, true = 1 } bool; + +#endif // CLASSICOS_KLIBC_STDBOOL_H diff --git a/klibc/include/stddef.h b/klibc/include/stddef.h new file mode 100644 index 0000000..3b67bd7 --- /dev/null +++ b/klibc/include/stddef.h @@ -0,0 +1,10 @@ +#ifndef CLASSICOS_KLIBC_STDDEF_H +#define CLASSICOS_KLIBC_STDDEF_H + +typedef __SIZE_TYPE__ size_t; +typedef __PTRDIFF_TYPE__ ptrdiff_t; + +#undef NULL +#define NULL ((void*)0) + +#endif // CLASSICOS_KLIBC_STDDEF_H diff --git a/klibc/include/stdint.h b/klibc/include/stdint.h new file mode 100644 index 0000000..a0db679 --- /dev/null +++ b/klibc/include/stdint.h @@ -0,0 +1,16 @@ +#ifndef CLASSICOS_KLIBC_STDINT_H +#define CLASSICOS_KLIBC_STDINT_H + +typedef signed char int8_t; +typedef short int int16_t; +typedef int int32_t; +typedef long long int int64_t; + +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long int uint64_t; + +typedef unsigned int uintptr_t; + +#endif // CLASSICOS_KLIBC_STDINT_H diff --git a/klibc/include/stdio.h b/klibc/include/stdio.h new file mode 100644 index 0000000..af112bf --- /dev/null +++ b/klibc/include/stdio.h @@ -0,0 +1,4 @@ +#ifndef CLASSICOS_KLIBC_STDIO_H +#define CLASSICOS_KLIBC_STDIO_H + +#endif // CLASSICOS_KLIBC_STDIO_H diff --git a/klibc/include/stdlib.h b/klibc/include/stdlib.h new file mode 100644 index 0000000..5046538 --- /dev/null +++ b/klibc/include/stdlib.h @@ -0,0 +1,4 @@ +#ifndef CLASSICOS_KLIBC_STDLIB_H +#define CLASSICOS_KLIBC_STDLIB_H + +#endif // CLASSICOS_KLIBC_STDLIB_H diff --git a/klibc/include/string.h b/klibc/include/string.h new file mode 100644 index 0000000..4f01fc1 --- /dev/null +++ b/klibc/include/string.h @@ -0,0 +1,14 @@ +#ifndef CLASSICOS_KLIBC_STRING_H +#define CLASSICOS_KLIBC_STRING_H + +#include + +extern int memcmp(const void* s1, const void* s2, size_t n); +extern void* memmove(void* dst, const void* src, size_t n); +extern void* memcpy(void* dst, const void* src, size_t n); +extern void* memset(void* dst, int c, size_t n); + +extern size_t strlen(const char* s); +extern int strcmp(const char* s1, const char* s2); + +#endif // CLASSICOS_KLIBC_STRING_H diff --git a/klibc/src/string.c b/klibc/src/string.c new file mode 100644 index 0000000..a9a8a84 --- /dev/null +++ b/klibc/src/string.c @@ -0,0 +1,107 @@ +#include + +int memcmp(const void* s1, const void* s2, size_t n) { + const unsigned char* c1 = s1; + const unsigned char* c2 = s2; + int d = 0; + + while (n--) { + d = (int)*c1++ - (int)*c2++; + if (d) break; + } + + return d; +} + +void* memmove(void* dst, const void* src, size_t n) { + const char* p = src; + char* q = dst; +#if defined(__i386__) || defined(__x86_64__) + if (q < p) { + __asm__ volatile("cld; rep; movsb" : "+c"(n), "+S"(p), "+D"(q)); + } else { + p += (n - 1); + q += (n - 1); + __asm__ volatile("std; rep; movsb; cld" : "+c"(n), "+S"(p), "+D"(q)); + } +#else + if (q < p) { + while (n--) { + *q++ = *p++; + } + } else { + p += n; + q += n; + while (n--) { + *--q = *--p; + } + } +#endif + + return dst; +} + +void* memcpy(void* dst, const void* src, size_t n) { + const char* p = src; + char* q = dst; +#if defined(__i386__) + size_t nl = n >> 2; + __asm__ volatile("cld ; rep ; movsl ; movl %3,%0 ; rep ; movsb" + : "+c"(nl), "+S"(p), "+D"(q) + : "r"(n & 3)); +#elif defined(__x86_64__) + size_t nq = n >> 3; + __asm__ volatile("cld ; rep ; movsq ; movl %3,%%ecx ; rep ; movsb" + : "+c"(nq), "+S"(p), "+D"(q) + : "r"((uint32_t)(n & 7))); +#else + while (n--) { + *q++ = *p++; + } +#endif + + return dst; +} + +void* memset(void* dst, int c, size_t n) { + char* q = dst; + +#if defined(__i386__) + size_t nl = n >> 2; + __asm__ volatile("cld ; rep ; stosl ; movl %3,%0 ; rep ; stosb" + : "+c"(nl), "+D"(q) + : "a"((unsigned char)c * 0x01010101U), "r"(n & 3)); +#elif defined(__x86_64__) + size_t nq = n >> 3; + __asm__ volatile("cld ; rep ; stosq ; movl %3,%%ecx ; rep ; stosb" + : "+c"(nq), "+D"(q) + : "a"((unsigned char)c * 0x0101010101010101U), + "r"((uint32_t)n & 7)); +#else + while (n--) { + *q++ = c; + } +#endif + + return dst; +} + +size_t strlen(const char* s) { + const char* ss = s; + while (*ss) ss++; + return ss - s; +} + +int strcmp(const char* s1, const char* s2) { + const unsigned char* c1 = (const unsigned char*)s1; + const unsigned char* c2 = (const unsigned char*)s2; + unsigned char ch; + int d = 0; + + while (1) { + d = (int)(ch = *c1++) - (int)*c2++; + if (d || !ch) break; + } + + return d; +} From 155563e4345ee5f38494338f437e504be8497c08 Mon Sep 17 00:00:00 2001 From: vmttmv Date: Tue, 30 Dec 2025 05:08:54 +0200 Subject: [PATCH 11/47] fix stage2.asm: disk reads wait for BSY --- bootloader/stage2.asm | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/bootloader/stage2.asm b/bootloader/stage2.asm index 02d9077..d062ab2 100644 --- a/bootloader/stage2.asm +++ b/bootloader/stage2.asm @@ -40,6 +40,13 @@ ata_lba_read: push edx push edi + ; Wait BSY=0 before proceeding to write the regs +.wait_rdy: + mov edx, 0x1F7 + in al, dx + test al, 0x80 + jnz .wait_rdy + mov eax, [ebp+8] ; arg #1 = LBA mov cl, [ebp+12] ; arg #2 = # of sectors mov edi, [ebp+16] ; arg #3 = buffer address @@ -74,21 +81,23 @@ ata_lba_read: mov al, 0x20 ; Read with retry. out dx, al - mov bl, cl ; Save # of sectors in BL + mov bl, cl ; Save # of sectors in BL -.wait_drq: +.wait_rdy2: mov edx, 0x1F7 -.do_wait_drq: +.do_wait_rdy2: in al, dx - test al, 8 ; the sector buffer requires servicing. - jz .do_wait_drq ; keep polling until the sector buffer is ready. + test al, 0x80 ; BSY? + jnz .do_wait_rdy2 + test al, 0x8 ; DRQ? + jz .do_wait_rdy2 mov edx, 0x1F0 ; Data port, in and out mov ecx, 256 rep insw ; in to [RDI] dec bl ; are we... - jnz .wait_drq ; ...done? + jnz .wait_rdy2 ; ...done? pop edi pop edx From 8c529c6fe4323b342d2baec9dcdd61a9a85926f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Borna=20=C5=A0o=C5=A1tari=C4=87?= Date: Wed, 31 Dec 2025 01:39:18 +0100 Subject: [PATCH 12/47] fix RWX perms warnings in link step --- kernel/linker.ld | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/kernel/linker.ld b/kernel/linker.ld index 4e7add9..97e38c2 100644 --- a/kernel/linker.ld +++ b/kernel/linker.ld @@ -1,18 +1,30 @@ ENTRY(kmain) +PHDRS { + text PT_LOAD FLAGS(5); /* Read + Execute */ + rodata PT_LOAD FLAGS(4); /* Read only */ + data PT_LOAD FLAGS(6); /* Read + Write */ +} + SECTIONS { . = 1M; .text : { *(.text*) - } + } :text + + .rodata : { + *(.rodata*) + } :rodata + + .data : { + *(.data*) + } :data - .rodata : { *(.rodata*) } - .data : { *(.data*) } .bss : { *(.bss*) *(COMMON) - } + } :data .stack (NOLOAD) : { . = ALIGN(4); From 235fd2636d5925721b6e6c68d9f7e40a2411a69a Mon Sep 17 00:00:00 2001 From: vmttmv Date: Tue, 6 Jan 2026 00:39:29 +0200 Subject: [PATCH 13/47] Fix non-aligned disk reads in bootloader --- bootloader/stage2.asm | 81 --------------------- bootloader/stage2_load.c | 153 +++++++++++++++++++++++++++++---------- 2 files changed, 115 insertions(+), 119 deletions(-) diff --git a/bootloader/stage2.asm b/bootloader/stage2.asm index d062ab2..e11f67b 100644 --- a/bootloader/stage2.asm +++ b/bootloader/stage2.asm @@ -24,84 +24,3 @@ _start: call load_kernel jmp eax - -; ---------------------------------------------------------------------------- -; ATA read sectors (LBA mode) -; -; sysv32 abi signature: -; void ata_lba_read(uint32_t lba, uint8_t nsect, void *addr); -; ---------------------------------------------------------------------------- -ata_lba_read: - push ebp - mov ebp, esp - - push ebx - push ecx - push edx - push edi - - ; Wait BSY=0 before proceeding to write the regs -.wait_rdy: - mov edx, 0x1F7 - in al, dx - test al, 0x80 - jnz .wait_rdy - - mov eax, [ebp+8] ; arg #1 = LBA - mov cl, [ebp+12] ; arg #2 = # of sectors - mov edi, [ebp+16] ; arg #3 = buffer address - and eax, 0x0FFFFFFF - - mov ebx, eax ; Save LBA in RBX - - mov edx, 0x01F6 ; Port to send drive and bit 24 - 27 of LBA - shr eax, 24 ; Get bit 24 - 27 in al - or al, 11100000b ; Set bit 6 in al for LBA mode - out dx, al - - mov edx, 0x01F2 ; Port to send number of sectors - mov al, cl ; Get number of sectors from CL - out dx, al - - mov edx, 0x1F3 ; Port to send bit 0 - 7 of LBA - mov eax, ebx ; Get LBA from EBX - out dx, al - - mov edx, 0x1F4 ; Port to send bit 8 - 15 of LBA - mov eax, ebx ; Get LBA from EBX - shr eax, 8 ; Get bit 8 - 15 in AL - out dx, al - - mov edx, 0x1F5 ; Port to send bit 16 - 23 of LBA - mov eax, ebx ; Get LBA from EBX - shr eax, 16 ; Get bit 16 - 23 in AL - out dx, al - - mov edx, 0x1F7 ; Command port - mov al, 0x20 ; Read with retry. - out dx, al - - mov bl, cl ; Save # of sectors in BL - -.wait_rdy2: - mov edx, 0x1F7 -.do_wait_rdy2: - in al, dx - test al, 0x80 ; BSY? - jnz .do_wait_rdy2 - test al, 0x8 ; DRQ? - jz .do_wait_rdy2 - - mov edx, 0x1F0 ; Data port, in and out - mov ecx, 256 - rep insw ; in to [RDI] - - dec bl ; are we... - jnz .wait_rdy2 ; ...done? - - pop edi - pop edx - pop ecx - pop ebx - pop ebp - ret diff --git a/bootloader/stage2_load.c b/bootloader/stage2_load.c index 83dd973..f123e96 100644 --- a/bootloader/stage2_load.c +++ b/bootloader/stage2_load.c @@ -1,11 +1,33 @@ #include +// ATA IO Ports +#define ATA_PRIMARY_DATA 0x1F0 +#define ATA_PRIMARY_ERR_FEATURES 0x1F1 +#define ATA_PRIMARY_SEC_COUNT 0x1F2 +#define ATA_PRIMARY_LBA_LOW 0x1F3 +#define ATA_PRIMARY_LBA_MID 0x1F4 +#define ATA_PRIMARY_LBA_HIGH 0x1F5 +#define ATA_PRIMARY_DRIVE_SEL 0x1F6 +#define ATA_PRIMARY_COMM_STAT 0x1F7 + +// ATA Commands +#define ATA_CMD_READ_PIO 0x20 +#define ATA_CMD_WRITE_PIO 0x30 + // ELF Ident indexes -#define EI_NIDENT 16 +#define EI_NIDENT 16 // Program header types -#define PT_NULL 0 -#define PT_LOAD 1 +#define PT_NULL 0 +#define PT_LOAD 1 + +// Disk sector size +#define SECTOR_SIZE 512 + +// Kernel start LBA +#define KERN_START_SECT 5 + +extern uint8_t read_buf[]; // ELF Header (32-bit) typedef struct { @@ -37,8 +59,84 @@ typedef struct { uint32_t p_align; } __attribute__((packed)) Elf32_Phdr; +static inline uint8_t inb(uint16_t port) +{ + uint8_t ret; + __asm__ volatile ("inb %1, %0" + : "=a"(ret) + : "Nd"(port)); + return ret; +} + +static inline void outb(uint16_t port, uint8_t val) +{ + __asm__ volatile ("outb %0, %1" + : + : "a"(val), "Nd"(port)); +} + +static inline uint16_t inw(uint16_t port) +{ + uint16_t ret; + __asm__ volatile ("inw %1, %0" + : "=a"(ret) + : "Nd"(port)); + return ret; +} + +static inline void ata_wait_bsy() { + while (inb(ATA_PRIMARY_COMM_STAT) & 0x80); +} + +static inline void ata_wait_drq() { + while (!(inb(ATA_PRIMARY_COMM_STAT) & 0x08)); +} + +static void ata_read_sector(void *addr, uint32_t lba) { + ata_wait_bsy(); + + outb(ATA_PRIMARY_DRIVE_SEL, 0xE0 | ((lba >> 24) & 0x0F)); + outb(ATA_PRIMARY_SEC_COUNT, 1); + outb(ATA_PRIMARY_LBA_LOW, (uint8_t)lba); + outb(ATA_PRIMARY_LBA_MID, (uint8_t)(lba >> 8)); + outb(ATA_PRIMARY_LBA_HIGH, (uint8_t)(lba >> 16)); + outb(ATA_PRIMARY_COMM_STAT, ATA_CMD_READ_PIO); + + uint16_t* ptr = (uint16_t*)addr; + ata_wait_bsy(); + ata_wait_drq(); + for (int i = 0; i < 256; i++) { + *ptr++ = inw(ATA_PRIMARY_DATA); + } +} + +static void load_segment(uint8_t *addr, uint32_t offset, uint32_t size) +{ + uint32_t lba = KERN_START_SECT + offset / SECTOR_SIZE; + uint32_t off = offset % 512; + uint8_t data[512]; + + while (size > 0) { + ata_read_sector(data, lba); + + uint32_t copy = 512 - off; + if (copy > size) { + copy = size; + } + + for (uint32_t i = 0; i < copy; i++) { + addr[i] = data[off + i]; + } + + addr += copy; + size -= copy; + lba++; + off = 0; + } +} + // Load an ELF executable into memory. -static int elf_load(const void* data, void (*load_segment)(uint8_t *vaddr, uint32_t src, uint32_t size)) { +static int elf_load(const void *data) { const Elf32_Ehdr* header = (const Elf32_Ehdr*)data; const Elf32_Phdr* ph = (const Elf32_Phdr*)((uint8_t*)data + header->e_phoff); @@ -51,8 +149,6 @@ static int elf_load(const void* data, void (*load_segment)(uint8_t *vaddr, uint3 uint32_t filesz = ph[i].p_filesz; uint32_t memsz = ph[i].p_memsz; - // Copy data segment - //load_segment((uint8_t *)vaddr, offset, filesz); load_segment((uint8_t *)vaddr, offset, filesz); // Zero remaining BSS (if any) @@ -67,52 +163,33 @@ static int elf_load(const void* data, void (*load_segment)(uint8_t *vaddr, uint3 return header->e_entry; } -#define KERN_START_SECT 5 -#define MAX(a, b) ((a)>(b) ? (a) : (b)) - -extern void ata_lba_read(uint32_t lba, uint8_t nsect, void *addr); -extern uint8_t read_buf[]; - static uint32_t -total_header_size(const Elf32_Ehdr *header) { +total_headers_size(const Elf32_Ehdr *header) { uint32_t phend = header->e_phoff + header->e_phentsize*header->e_phnum; - // Align to 512 - return (phend + 511) & ~511; -} - -static void read_sectors(uint8_t *vaddr, uint32_t offset, uint32_t size) { - // # of sectors to read - uint32_t rem_nsect = ((size + 511) & ~511) / 512; - - // Current lba address, offset by the first sector already read - uint32_t lba = KERN_START_SECT + offset / 512; - - // Max 255 sectors at a time - while (rem_nsect) { - uint8_t nsect = rem_nsect > 255 ? 255 : rem_nsect; - ata_lba_read(lba, nsect, vaddr); - - vaddr += nsect * 512; - rem_nsect -= nsect; - lba += nsect; - } + // Align to sector size + uint32_t a = SECTOR_SIZE-1; + return (phend + a) & ~a; } void *load_kernel(void) { // Read the first sector - ata_lba_read(KERN_START_SECT, 1, read_buf); + ata_read_sector(read_buf, KERN_START_SECT); const Elf32_Ehdr* header = (const Elf32_Ehdr*)read_buf; // Remaining data size, subtract the first 512B already read - uint32_t rem = total_header_size(header) - 512; + uint32_t rem = total_headers_size(header) - SECTOR_SIZE; // Read the rest if necessary - if (rem) - read_sectors(read_buf+512, 512, rem); + if (rem) { + uint8_t *dst = read_buf + SECTOR_SIZE; + for (uint32_t i = 0; i < rem / SECTOR_SIZE; i++, dst += 512) { + ata_read_sector(dst, KERN_START_SECT + i + 1); + } + } - elf_load(read_buf, read_sectors); + elf_load(read_buf); return (void *)header->e_entry; } From a450ac06f2077ec0a9816da1222546c7423588a1 Mon Sep 17 00:00:00 2001 From: vmttmv Date: Wed, 7 Jan 2026 02:31:14 +0200 Subject: [PATCH 14/47] Establish well-defined read buffers for bl, implement error printing --- bootloader/stage2.ld | 2 - bootloader/stage2_load.c | 120 ++++++++++++++++++++++++++------------- 2 files changed, 80 insertions(+), 42 deletions(-) diff --git a/bootloader/stage2.ld b/bootloader/stage2.ld index 48b7953..95b3fd2 100644 --- a/bootloader/stage2.ld +++ b/bootloader/stage2.ld @@ -9,6 +9,4 @@ SECTIONS { *(.bss*) *(COMMON) } - - read_buf = .; } diff --git a/bootloader/stage2_load.c b/bootloader/stage2_load.c index f123e96..5e9aa43 100644 --- a/bootloader/stage2_load.c +++ b/bootloader/stage2_load.c @@ -1,3 +1,4 @@ +#include #include // ATA IO Ports @@ -23,11 +24,16 @@ // Disk sector size #define SECTOR_SIZE 512 +#define PH_PER_SECTOR (SECTOR_SIZE / sizeof(Elf32_Phdr)) // Kernel start LBA #define KERN_START_SECT 5 -extern uint8_t read_buf[]; +// VGA +// Expects bios initialization for text mode (3), buffer at 0xb8000 +#define VGA_ADDRESS 0xB8000 +#define VGA_COLS 80 +#define VGA_ROWS 25 // ELF Header (32-bit) typedef struct { @@ -110,8 +116,9 @@ static void ata_read_sector(void *addr, uint32_t lba) { } } -static void load_segment(uint8_t *addr, uint32_t offset, uint32_t size) +static void ata_read_sectors(uint8_t *addr, uint32_t offset, uint32_t size) { + // Reads are offset from the starting sector of the kernel uint32_t lba = KERN_START_SECT + offset / SECTOR_SIZE; uint32_t off = offset % 512; uint8_t data[512]; @@ -135,61 +142,94 @@ static void load_segment(uint8_t *addr, uint32_t offset, uint32_t size) } } +static void on_error(const char *msg) +{ + uint16_t *ptr = (uint16_t *)VGA_ADDRESS; + + // Clear + uint16_t val = 0x0f | (uint8_t)' '; + for (size_t i = 0; i < VGA_COLS * VGA_ROWS; i++) { + ptr[i] = val; + } + + // Print error + for (size_t i = 0; msg[i]; i++) { + ptr[i] = 0xf00 | (uint8_t)msg[i]; + } + + // Halt + while (1) { + __asm__("hlt"); + } +} + // Load an ELF executable into memory. -static int elf_load(const void *data) { - const Elf32_Ehdr* header = (const Elf32_Ehdr*)data; - const Elf32_Phdr* ph = (const Elf32_Phdr*)((uint8_t*)data + header->e_phoff); +// NOTE: Only 32-byte program headers are supported. +// Returns the entry point to the program. +static void *elf_load(const void *data) { + const Elf32_Ehdr *header = (const Elf32_Ehdr*)data; + + if (header->e_phentsize != sizeof(Elf32_Phdr)) { + // The bootloader only handles 32-byte program header entries + on_error("ERROR: Unsupported program header entry size, halting..."); + } + + // Buffer for the program headers + uint8_t file_buf[SECTOR_SIZE]; + + // Current file offset to the next program header + uint32_t file_offset = header->e_phoff; for (int i = 0; i < header->e_phnum; i++) { - if (ph[i].p_type != PT_LOAD) + // Check for sector boundary. + // Program headers are read in a sector at a time + // 512 / 32 = 16 PH per sector + if (i % PH_PER_SECTOR == 0) { + uint32_t count = (header->e_phnum - i) * sizeof(Elf32_Phdr); + if (count > SECTOR_SIZE) { + count = SECTOR_SIZE; + } + + // Reads + ata_read_sectors(file_buf, file_offset, count); + file_offset += count; + } + + // PH being processed currently, index mod 16 as headers + // are being loaded in sector by sector. + const Elf32_Phdr *ph = (const Elf32_Phdr *)file_buf + (i % PH_PER_SECTOR); + + // Discard non-load segments + if (ph->p_type != PT_LOAD) continue; - uint32_t offset = ph[i].p_offset; - uint32_t vaddr = ph[i].p_vaddr; - uint32_t filesz = ph[i].p_filesz; - uint32_t memsz = ph[i].p_memsz; - - load_segment((uint8_t *)vaddr, offset, filesz); + // Load in the segment + uint32_t offset = ph->p_offset; + uint32_t filesz = ph->p_filesz; + uint32_t memsz = ph->p_memsz; + uint8_t *vaddr = (uint8_t *)ph->p_vaddr; + ata_read_sectors(vaddr, offset, filesz); // Zero remaining BSS (if any) if (memsz > filesz) { - uint8_t* bss_start = (uint8_t*)(vaddr + filesz); + uint8_t* bss_start = vaddr + filesz; for (uint32_t j = 0; j < memsz - filesz; j++) { bss_start[j] = 0; } } } - return header->e_entry; -} - -static uint32_t -total_headers_size(const Elf32_Ehdr *header) { - uint32_t phend = header->e_phoff + header->e_phentsize*header->e_phnum; - - // Align to sector size - uint32_t a = SECTOR_SIZE-1; - return (phend + a) & ~a; + // Return the entry point + return (void *)header->e_entry; } void *load_kernel(void) { - // Read the first sector - ata_read_sector(read_buf, KERN_START_SECT); + // ELF header buffer + uint8_t header_buf[SECTOR_SIZE]; - const Elf32_Ehdr* header = (const Elf32_Ehdr*)read_buf; + // Read the first sector (contains the ELF header) + ata_read_sector(header_buf, KERN_START_SECT); - // Remaining data size, subtract the first 512B already read - uint32_t rem = total_headers_size(header) - SECTOR_SIZE; - - // Read the rest if necessary - if (rem) { - uint8_t *dst = read_buf + SECTOR_SIZE; - for (uint32_t i = 0; i < rem / SECTOR_SIZE; i++, dst += 512) { - ata_read_sector(dst, KERN_START_SECT + i + 1); - } - } - - elf_load(read_buf); - - return (void *)header->e_entry; + // `elf_load()` returns the entry point + return elf_load(header_buf); } From dfb161a15aaebaeec8625122bfd503005d4e592c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Borna=20=C5=A0o=C5=A1tari=C4=87?= Date: Thu, 8 Jan 2026 05:20:42 +0100 Subject: [PATCH 15/47] fix vga clear section in `on_error` --- bootloader/stage2_load.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootloader/stage2_load.c b/bootloader/stage2_load.c index 5e9aa43..b5673cb 100644 --- a/bootloader/stage2_load.c +++ b/bootloader/stage2_load.c @@ -147,7 +147,7 @@ static void on_error(const char *msg) uint16_t *ptr = (uint16_t *)VGA_ADDRESS; // Clear - uint16_t val = 0x0f | (uint8_t)' '; + uint16_t val = 0x0f00 | (uint8_t)' '; for (size_t i = 0; i < VGA_COLS * VGA_ROWS; i++) { ptr[i] = val; } From 41281de743705e969e1408dfa9a110c9e12b123d Mon Sep 17 00:00:00 2001 From: Gregory Bowne Date: Mon, 5 Jan 2026 00:42:05 -0800 Subject: [PATCH 16/47] Implement CPUID support check and CPU info printing Added functions to check CPUID support and print CPU details. --- kernel/cpu.c | 92 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 77 insertions(+), 15 deletions(-) diff --git a/kernel/cpu.c b/kernel/cpu.c index a18041b..5982d72 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -2,36 +2,98 @@ #include "serial.h" #include "terminal.h" #include "utils.h" -#include "print.h" -void cpuid(uint32_t function, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) { - __asm__( - "cpuid" - : "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx) - : "a"(function) - ); +// Helper to print a labeled decimal value +void print_val(const char* label, uint32_t val) { + char buf[12]; + utoa(val, buf, 10); + terminal_write(label); + terminal_write(buf); + terminal_write(" "); +} + +// Safely check if CPUID is supported by attempting to flip bit 21 of EFLAGS +int check_cpuid_supported() { + uint32_t f1, f2; + __asm__ volatile ( + "pushfl\n\t" + "pushfl\n\t" + "popl %0\n\t" + "movl %0, %1\n\t" + "xorl $0x200000, %0\n\t" + "pushl %0\n\t" + "popfl\n\t" + "pushfl\n\t" + "popl %0\n\t" + "popfl\n\t" + : "=&r" (f1), "=&r" (f2)); + return ((f1 ^ f2) & 0x200000) != 0; } void identify_cpu() { + if (!check_cpuid_supported()) { + terminal_write("CPUID not supported. Likely a 386 or early 486.\n"); + return; + } + uint32_t eax, ebx, ecx, edx; char vendor[13]; + // Leaf 0: Vendor String & Max Leaf cpuid(0, &eax, &ebx, &ecx, &edx); - + uint32_t max_leaf = eax; *(uint32_t *)&vendor[0] = ebx; *(uint32_t *)&vendor[4] = edx; *(uint32_t *)&vendor[8] = ecx; vendor[12] = '\0'; - terminal_write("CPU Vendor: "); + terminal_write("Vendor: "); terminal_write(vendor); terminal_write("\n"); - serial_write("CPU Vendor: "); - serial_write(vendor); - serial_write("\n"); + // Leaf 1: Family, Model, Stepping + if (max_leaf >= 1) { + cpuid(1, &eax, &ebx, &ecx, &edx); + + uint32_t stepping = eax & 0xF; + uint32_t model = (eax >> 4) & 0xF; + uint32_t family = (eax >> 8) & 0xF; + uint32_t type = (eax >> 12) & 0x3; - terminal_write("CPUID max leaf: "); - print_hex(eax, false, false); // You must implement this (see below) - terminal_write("\n"); + // Handle Extended Family/Model (Required for Pentium 4 and newer) + if (family == 0xF) { + family += (eax >> 20) & 0xFF; + model += ((eax >> 16) & 0xF) << 4; + } + + print_val("Family:", family); + print_val("Model:", model); + print_val("Step:", stepping); + terminal_write("\n"); + } + + // Leaf 2: Cache Descriptors + if (max_leaf >= 2) { + cpuid(2, &eax, &ebx, &ecx, &edx); + + terminal_write("Cache Descriptors: "); + // Note: Leaf 2 returns a list of 1-byte descriptors in the registers. + // We look for common Intel ones: + uint32_t regs[4] = {eax, ebx, ecx, edx}; + for (int i = 0; i < 4; i++) { + if (regs[i] & 0x80000000) continue; // Reserved bit + for (int j = 0; j < 4; j++) { + uint8_t desc = (regs[i] >> (j * 8)) & 0xFF; + if (desc == 0) continue; + + // Example decoding for specific chips you mentioned: + if (desc == 0x06) terminal_write("8KB L1 I-Cache "); + if (desc == 0x0A) terminal_write("8KB L1 D-Cache "); + if (desc == 0x41) terminal_write("128KB L2 "); + if (desc == 0x43) terminal_write("512KB L2 "); + if (desc == 0x2C) terminal_write("32KB L1 D-Cache "); + } + } + terminal_write("\n"); + } } From 16057d41d6cddf515a402acbf3ec9b6677986f01 Mon Sep 17 00:00:00 2001 From: Gregory Bowne Date: Mon, 5 Jan 2026 00:46:14 -0800 Subject: [PATCH 17/47] Enhance cpu.h with Intel model definitions and struct Added Intel model definitions and CPU info structure. --- kernel/cpu.h | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/kernel/cpu.h b/kernel/cpu.h index d2e165f..4eefd2e 100644 --- a/kernel/cpu.h +++ b/kernel/cpu.h @@ -2,8 +2,42 @@ #define CPU_H #include +#include -void cpuid(uint32_t function, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx); +// Specific Intel Model Definitions for your targets +#define INTEL_FAM4_486_DX 0x00 // Also 0x01 +#define INTEL_FAM4_486_SX 0x02 +#define INTEL_FAM4_486_DX2 0x03 +#define INTEL_FAM4_486_DX4 0x08 +#define INTEL_FAM5_PENTIUM 0x01 // P5 +#define INTEL_FAM5_PENTIUM_MMX 0x04 // P55C +#define INTEL_FAM6_PENTIUM_PRO 0x01 // P6 +#define INTEL_FAM6_PENTIUM_II 0x05 // Deschutes +#define INTEL_FAM6_PENTIUM_III 0x07 // Katmai/Coppermine +#define INTEL_FAM15_P4_WILLY 0x00 // Willamette +#define INTEL_FAM15_P4_NORTH 0x02 // Northwood +#define INTEL_FAM15_P4_PRES 0x03 // Prescott + +typedef struct { + char vendor[13]; + uint32_t family; + uint32_t model; + uint32_t stepping; + uint32_t type; + uint32_t max_leaf; + + // Feature flags (optional, but very helpful later) + bool has_fpu; + bool has_mmx; + bool has_sse; +} cpu_info_t; + +// Function Prototypes +void cpuid(uint32_t leaf, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx); +bool cpu_check_cpuid_support(void); void identify_cpu(void); +// Helper to get the current CPU info after identification +cpu_info_t* cpu_get_info(void); + #endif // CPU_H From 4f992c8fc5c9aa5f2a056d44f5035e1d392d1e9e Mon Sep 17 00:00:00 2001 From: Gregory Bowne Date: Thu, 8 Jan 2026 21:03:10 -0800 Subject: [PATCH 18/47] Fix extern declaration for disk_read_sector function --- kernel/fat12.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/fat12.h b/kernel/fat12.h index 730b144..9615cc7 100644 --- a/kernel/fat12.h +++ b/kernel/fat12.h @@ -58,7 +58,7 @@ typedef struct { // You must implement this in your disk driver (e.g., floppy.c) // Returns 0 on success, non-zero on error. -extern int disk_read_sector(uint32_t lba, uint8_t *buffer); +int disk_read_sector(uint32_t lba, uint8_t *buffer); void fat12_init(); file_t fat12_open(const char *filename); From 8be984d5652cdf35bfc61712ba51f423da612c93 Mon Sep 17 00:00:00 2001 From: Gregory Bowne Date: Thu, 8 Jan 2026 21:10:35 -0800 Subject: [PATCH 19/47] Fix k_memcmp return logic and add disk_read_sector Refactor k_memcmp to return correct difference and add disk_read_sector function. --- kernel/fat12.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/kernel/fat12.c b/kernel/fat12.c index 7f0a1da..a296ea2 100644 --- a/kernel/fat12.c +++ b/kernel/fat12.c @@ -15,9 +15,16 @@ static uint8_t g_sector_buffer[FAT12_SECTOR_SIZE]; static int k_memcmp(const void *s1, const void *s2, uint32_t n) { const uint8_t *p1 = (const uint8_t *)s1; const uint8_t *p2 = (const uint8_t *)s2; + for (uint32_t i = 0; i < n; i++) { - if (p1[i] != p2[i]) return p1[i] - p2[i]; + if (p1[i] != p2[i]) { + // Correct way to return the difference: + // If p1[i] > p2[i], returns positive. + // If p1[i] < p2[i], returns negative. + return (int)p1[i] - (int)p2[i]; + } } + return 0; } @@ -182,3 +189,8 @@ uint32_t fat12_read(file_t *file, uint8_t *buffer, uint32_t bytes_to_read) { return total_read; } + +int disk_read_sector(uint32_t lba, uint8_t *buffer) { + // For now, do nothing and return success + return 0; +} From d0c9c9c4e01f0615b1d5242723c9753c35713f14 Mon Sep 17 00:00:00 2001 From: vmttmv Date: Mon, 12 Jan 2026 02:42:02 +0200 Subject: [PATCH 20/47] Remove types.c/.h, use klibc headers, amend stdbool.h, reimplement cpuid() --- kernel/cpu.c | 8 ++++++ kernel/irq.h | 2 +- kernel/isr.c | 1 + kernel/print.h | 2 +- kernel/threading.c | 4 +-- kernel/types.c | 1 - kernel/types.h | 61 ----------------------------------------- kernel/utils.h | 2 -- klibc/include/stdbool.h | 8 +++++- 9 files changed, 20 insertions(+), 69 deletions(-) delete mode 100644 kernel/types.c delete mode 100644 kernel/types.h diff --git a/kernel/cpu.c b/kernel/cpu.c index 5982d72..90949ad 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -3,6 +3,14 @@ #include "terminal.h" #include "utils.h" +void cpuid(uint32_t leaf, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) { + __asm__( + "cpuid" + : "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx) + : "a"(leaf) + ); +} + // Helper to print a labeled decimal value void print_val(const char* label, uint32_t val) { char buf[12]; diff --git a/kernel/irq.h b/kernel/irq.h index edc39a4..d6ca7a1 100644 --- a/kernel/irq.h +++ b/kernel/irq.h @@ -1,7 +1,7 @@ #ifndef IRQ_H #define IRQ_H -#include "types.h" +#include void irq_remap(void); void irq_install(void); diff --git a/kernel/isr.c b/kernel/isr.c index f6a0222..fd13cef 100644 --- a/kernel/isr.c +++ b/kernel/isr.c @@ -1,3 +1,4 @@ +#include #include "terminal.h" #include "serial.h" #include "isr.h" diff --git a/kernel/print.h b/kernel/print.h index bf07e4a..f172347 100644 --- a/kernel/print.h +++ b/kernel/print.h @@ -1,7 +1,7 @@ #ifndef PRINT_H #define PRINT_H -#include "types.h" +#include void print_string(const char *str); void my_printf(const char *format, ...); diff --git a/kernel/threading.c b/kernel/threading.c index c7b6ba5..ff6d15b 100644 --- a/kernel/threading.c +++ b/kernel/threading.c @@ -1,9 +1,9 @@ +#include +#include #include "malloc.h" #include "print.h" #include "threading.h" -#include "types.h" #include "utils.h" -#include #define MAX_THREADS 16 // Maximum number of threads #define THREAD_STACK_SIZE 8192 // Stack size for each thread diff --git a/kernel/types.c b/kernel/types.c deleted file mode 100644 index 8e71fd1..0000000 --- a/kernel/types.c +++ /dev/null @@ -1 +0,0 @@ -#include "types.h" diff --git a/kernel/types.h b/kernel/types.h deleted file mode 100644 index 332aa4e..0000000 --- a/kernel/types.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef TYPES_H -#define TYPES_H - -// ---------------------------- -// Fixed-width integer types -// ---------------------------- -typedef unsigned char uint8_t; -typedef signed char int8_t; -typedef unsigned short uint16_t; -typedef signed short int16_t; -typedef unsigned int uint32_t; -typedef signed int int32_t; -typedef unsigned long long uint64_t; -typedef signed long long int64_t; - -// ---------------------------- -// Boolean & NULL definitions -// ---------------------------- -#ifndef __cplusplus -typedef enum { false = 0, true = 1 } bool; -#endif - -#ifndef NULL -#define NULL ((void*)0) -#endif - -// ---------------------------- -// OS subsystem types -// ---------------------------- -typedef int32_t ssize_t; - -typedef uint32_t phys_addr_t; // Physical address -typedef uint32_t virt_addr_t; // Virtual address - -typedef uint32_t pid_t; // Process ID -typedef uint32_t tid_t; // Thread ID - -// ---------------------------- -// Bitfield & utility macros -// ---------------------------- -#define BIT(n) (1U << (n)) -#define BITS(m, n) (((1U << ((n) - (m) + 1)) - 1) << (m)) - -// Align value to next multiple of alignment -#define ALIGN_UP(val, align) (((val) + ((align)-1)) & ~((align)-1)) -#define ALIGN_DOWN(val, align) ((val) & ~((align)-1)) - -// ---------------------------- -// Attributes for structures -// ---------------------------- -#define PACKED __attribute__((packed)) -#define ALIGN(x) __attribute__((aligned(x))) - -// ---------------------------- -// Likely/unlikely branch hints -// (for future optimization use) -// ---------------------------- -#define likely(x) __builtin_expect(!!(x), 1) -#define unlikely(x) __builtin_expect(!!(x), 0) - -#endif // TYPES_H diff --git a/kernel/utils.h b/kernel/utils.h index 53fab8a..0c25890 100644 --- a/kernel/utils.h +++ b/kernel/utils.h @@ -3,8 +3,6 @@ #include -#include "types.h" - // Convert integer to string (base is typically 10, 16, etc.) char* itoa(int value, char* str, int base); diff --git a/klibc/include/stdbool.h b/klibc/include/stdbool.h index 0cd5393..51807b5 100644 --- a/klibc/include/stdbool.h +++ b/klibc/include/stdbool.h @@ -1,6 +1,12 @@ #ifndef CLASSICOS_KLIBC_STDBOOL_H #define CLASSICOS_KLIBC_STDBOOL_H -typedef enum { false = 0, true = 1 } bool; +#ifndef __cplusplus +#define bool _Bool +#define true 1 +#define false 0 +#endif + +#define __bool_true_false_are_defined 1 #endif // CLASSICOS_KLIBC_STDBOOL_H From d480fbcc80a0b0c4be72ac0cb0673f088b326c1c Mon Sep 17 00:00:00 2001 From: vmttmv Date: Mon, 12 Jan 2026 03:48:02 +0200 Subject: [PATCH 21/47] Fix PDE/PTE definitions, header cleanup - Fixes PDE/PTE definitions in kernel/paging.h - removes memset declaration from kernel/utils.h, uses klibc string.h as needed --- kernel/paging.c | 43 ++++---------- kernel/paging.h | 42 ++++++------- kernel/threading.c | 145 +++++++++++++++++++++++---------------------- kernel/utils.h | 2 - 4 files changed, 107 insertions(+), 125 deletions(-) diff --git a/kernel/paging.c b/kernel/paging.c index 321a404..1bb1f20 100644 --- a/kernel/paging.c +++ b/kernel/paging.c @@ -1,21 +1,17 @@ -#include "paging.h" -#include "io.h" #include -#include +#include +#include "io.h" +#include "paging.h" page_directory_entry_t *page_directory = (page_directory_entry_t *)0x200000; page_table_entry_t *page_table = (page_table_entry_t *)0x201000; -page_table_entry_t *heap_page_table = (page_table_entry_t *)0x202000; // Helper function to set up the page directory entry void set_page_directory(page_directory_entry_t *dir) { - for (int i = 0; i < PAGE_DIRECTORY_SIZE; i++) { - dir[i].present = 0; - } + // Set first PDE dir[0].present = 1; dir[0].rw = 1; - dir[0].user = 0; - dir[0].frame = (uint32_t)page_table >> 12; + dir[0].addr = (uint32_t)page_table >> 12; } // Helper function to set up the page table entry @@ -23,12 +19,8 @@ void set_page_table(page_table_entry_t *table) { for (int i = 0; i < PAGE_TABLE_SIZE; i++) { // Set up page table entries with identity mapping table[i].present = 1; - table[i].rw = 1; // Read/Write - table[i].user = 0; // Kernel mode - table[i].write_through = 0; - table[i].cache_disabled = 0; - table[i].accessed = 0; - table[i].frame = i; // Identity mapping + table[i].rw = 1; // Read/Write + table[i].addr = i; // Identity mapping } } @@ -47,26 +39,13 @@ void enable_paging() { // Initialize paging: set up the page directory and enable paging void paging_init() { + // Zero out the tables + memset(page_directory, 0x00, PAGE_DIRECTORY_SIZE * sizeof *page_directory); + memset(page_table, 0x00, PAGE_TABLE_SIZE * sizeof *page_table); + // Set up identity-mapped page directory + table set_page_directory(page_directory); set_page_table(page_table); - // === Set up heap mapping at 0xC0100000 === - for (int i = 0; i < PAGE_TABLE_SIZE; i++) { - heap_page_table[i].present = 1; - heap_page_table[i].rw = 1; - heap_page_table[i].user = 0; - heap_page_table[i].write_through = 0; - heap_page_table[i].cache_disabled = 0; - heap_page_table[i].accessed = 0; - heap_page_table[i].frame = (256 + i); // Start physical heap at 1MB (256*4KB = 1MB) - } - - // Index 772 = 0xC0100000 / 4MB - page_directory[772].present = 1; - page_directory[772].rw = 1; - page_directory[772].user = 0; - page_directory[772].frame = (uint32_t)heap_page_table >> 12; - enable_paging(); } diff --git a/kernel/paging.h b/kernel/paging.h index 01e73aa..57b2253 100644 --- a/kernel/paging.h +++ b/kernel/paging.h @@ -10,31 +10,31 @@ // Page Directory and Page Table structure typedef struct { - uint32_t present : 1; // Present bit (1: page is present in memory) - uint32_t rw : 1; // Read-Write bit (1: page is read-write) - uint32_t user : 1; // User-supervisor bit (1: user mode access) - uint32_t write_through : 1; // Write-through cache - uint32_t cache_disabled : 1; // Cache disabled - uint32_t accessed : 1; // Accessed bit - uint32_t reserved : 1; // Reserved bit - uint32_t page_size : 1; // Page size (0: 4KB, 1: 4MB) - uint32_t global : 1; // Global page (can be used across different processes) - uint32_t available : 3; // Available bits for the system - uint32_t frame : 20; // Frame address (physical address) + uint32_t present : 1; // Present bit (1: page is present in memory) + uint32_t rw : 1; // Read-Write bit (1: page is read-write) + uint32_t user : 1; // User-supervisor bit (1: user mode access) + uint32_t write_through : 1; // Write-through cache + uint32_t cache_disabled : 1; // Cache disabled + uint32_t accessed : 1; // Accessed bit + uint32_t dirty : 1; // Dirty bit + uint32_t attribute : 1; // Page size (0: 4KB, 1: 4MB) + uint32_t global : 1; // Global page (can be used across different processes) + uint32_t reserved : 3; // Unused + uint32_t addr : 20; // Page frame address (physical address) } __attribute__((packed)) page_table_entry_t; // Define page directory entry typedef struct { - uint32_t present : 1; - uint32_t rw : 1; - uint32_t user : 1; - uint32_t write_through : 1; - uint32_t cache_disabled : 1; - uint32_t accessed : 1; - uint32_t reserved : 1; - uint32_t zero : 5; // Must be zero for page directory - uint32_t reserved_2 : 7; // Reserved bits - uint32_t frame : 20; // Frame address of the page table + uint32_t present : 1; // Present bit (1: PTE is present in memory) + uint32_t rw : 1; // Read-Write bit (1: pages are read-write) + uint32_t user : 1; // User-supervisor bit (1: user mode access) + uint32_t write_through : 1; // Write-through cache + uint32_t cache_disabled : 1; // Cache disabled + uint32_t accessed : 1; // Accessed bit + uint32_t available : 1; // Unused + uint32_t page_size : 1; // Page size (0: 4KB, 1: 4MB) + uint32_t available_2 : 4; // Unused + uint32_t addr : 20; // Page table address } __attribute__((packed)) page_directory_entry_t; extern page_directory_entry_t *page_directory; diff --git a/kernel/threading.c b/kernel/threading.c index ff6d15b..23221a6 100644 --- a/kernel/threading.c +++ b/kernel/threading.c @@ -1,12 +1,14 @@ +#include "threading.h" + #include -#include +#include + #include "malloc.h" #include "print.h" -#include "threading.h" #include "utils.h" -#define MAX_THREADS 16 // Maximum number of threads -#define THREAD_STACK_SIZE 8192 // Stack size for each thread +#define MAX_THREADS 16 // Maximum number of threads +#define THREAD_STACK_SIZE 8192 // Stack size for each thread // The thread table stores information about all threads static Thread thread_table[MAX_THREADS]; @@ -17,103 +19,106 @@ static uint32_t num_threads = 0; // Number of active threads static volatile int mutex_locked = 0; // Function declaration for context_switch -void context_switch(Thread *next); +void context_switch(Thread* next); // Initialize the threading system void thread_init(void) { - memset(thread_table, 0, sizeof(thread_table)); - num_threads = 0; + memset(thread_table, 0, sizeof(thread_table)); + num_threads = 0; } // Create a new thread -void thread_create(Thread *thread __attribute__((unused)), void (*start_routine)(void *), void *arg) { - if (num_threads >= MAX_THREADS) { - my_printf("Error: Maximum thread count reached.\n"); - return; - } +void thread_create(Thread* thread __attribute__((unused)), + void (*start_routine)(void*), void* arg) { + if (num_threads >= MAX_THREADS) { + my_printf("Error: Maximum thread count reached.\n"); + return; + } - // Find an empty slot for the new thread - int index = num_threads++; - thread_table[index] = (Thread){0}; - - // Set up the new thread - thread_table[index].start_routine = start_routine; - thread_table[index].arg = arg; - thread_table[index].stack_size = THREAD_STACK_SIZE; - thread_table[index].stack = (uint32_t*)malloc(THREAD_STACK_SIZE); - thread_table[index].stack_top = thread_table[index].stack + THREAD_STACK_SIZE / sizeof(uint32_t); + // Find an empty slot for the new thread + int index = num_threads++; + thread_table[index] = (Thread){0}; - // Initialize the stack (simulate pushing the function's return address) - uint32_t *stack_top = thread_table[index].stack_top; - *(--stack_top) = (uint32_t)start_routine; // Return address (the thread's entry point) - *(--stack_top) = (uint32_t)arg; // Argument to pass to the thread + // Set up the new thread + thread_table[index].start_routine = start_routine; + thread_table[index].arg = arg; + thread_table[index].stack_size = THREAD_STACK_SIZE; + thread_table[index].stack = (uint32_t*)malloc(THREAD_STACK_SIZE); + thread_table[index].stack_top = + thread_table[index].stack + THREAD_STACK_SIZE / sizeof(uint32_t); - // Set the thread's state to ready - thread_table[index].state = THREAD_READY; + // Initialize the stack (simulate pushing the function's return address) + uint32_t* stack_top = thread_table[index].stack_top; + *(--stack_top) = + (uint32_t)start_routine; // Return address (the thread's entry point) + *(--stack_top) = (uint32_t)arg; // Argument to pass to the thread - // If this is the first thread, switch to it - if (index == 0) { - scheduler(); - } + // Set the thread's state to ready + thread_table[index].state = THREAD_READY; + + // If this is the first thread, switch to it + if (index == 0) { + scheduler(); + } } // Yield the CPU to another thread void thread_yield(void) { - // Find the next thread in a round-robin manner - uint32_t next_thread = (current_thread + 1) % num_threads; - while (next_thread != current_thread && thread_table[next_thread].state != THREAD_READY) { - next_thread = (next_thread + 1) % num_threads; - } + // Find the next thread in a round-robin manner + uint32_t next_thread = (current_thread + 1) % num_threads; + while (next_thread != current_thread && + thread_table[next_thread].state != THREAD_READY) { + next_thread = (next_thread + 1) % num_threads; + } - if (next_thread != current_thread) { - current_thread = next_thread; - scheduler(); - } + if (next_thread != current_thread) { + current_thread = next_thread; + scheduler(); + } } // Exit the current thread void thread_exit(void) { - thread_table[current_thread].state = THREAD_BLOCKED; // Mark the thread as blocked (finished) - free(thread_table[current_thread].stack); // Free the thread's stack - num_threads--; // Decrease thread count + thread_table[current_thread].state = + THREAD_BLOCKED; // Mark the thread as blocked (finished) + free(thread_table[current_thread].stack); // Free the thread's stack + num_threads--; // Decrease thread count - // Yield to the next thread - thread_yield(); + // Yield to the next thread + thread_yield(); } // Scheduler: This function selects the next thread to run void scheduler(void) { - // Find the next ready thread - uint32_t next_thread = (current_thread + 1) % num_threads; - while (thread_table[next_thread].state != THREAD_READY) { - next_thread = (next_thread + 1) % num_threads; - } + // Find the next ready thread + uint32_t next_thread = (current_thread + 1) % num_threads; + while (thread_table[next_thread].state != THREAD_READY) { + next_thread = (next_thread + 1) % num_threads; + } - if (next_thread != current_thread) { - current_thread = next_thread; - context_switch(&thread_table[current_thread]); - } + if (next_thread != current_thread) { + current_thread = next_thread; + context_switch(&thread_table[current_thread]); + } } -// Context switch to the next thread (assembly would go here to save/load registers) -void context_switch(Thread *next) { - // For simplicity, context switching in this example would involve saving/restoring registers. - // In a real system, you would need to save the CPU state (registers) and restore the next thread's state. - my_printf("Switching to thread...\n"); - next->start_routine(next->arg); // Start running the next thread +// Context switch to the next thread (assembly would go here to save/load +// registers) +void context_switch(Thread* next) { + // For simplicity, context switching in this example would involve + // saving/restoring registers. In a real system, you would need to save the + // CPU state (registers) and restore the next thread's state. + my_printf("Switching to thread...\n"); + next->start_routine(next->arg); // Start running the next thread } // Simple mutex functions (spinlock) -void mutex_init(void) { - mutex_locked = 0; -} +void mutex_init(void) { mutex_locked = 0; } void mutex_lock(void) { - while (__sync_lock_test_and_set(&mutex_locked, 1)) { - // Busy wait (spinlock) - } + while (__sync_lock_test_and_set(&mutex_locked, 1)) { + // Busy wait (spinlock) + } } -void mutex_unlock(void) { - __sync_lock_release(&mutex_locked); -} +void mutex_unlock(void) { __sync_lock_release(&mutex_locked); } diff --git a/kernel/utils.h b/kernel/utils.h index 0c25890..cb4d566 100644 --- a/kernel/utils.h +++ b/kernel/utils.h @@ -9,6 +9,4 @@ char* itoa(int value, char* str, int base); // Convert unsigned integer to string (base is typically 10, 16, etc.) char* utoa(unsigned int value, char* str, int base); -void *memset(void *dest, int value, size_t len); - #endif // UTILS_H From 13b915a99de2c8f6803db87c589bdc5e27a5b46d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Borna=20=C5=A0o=C5=A1tari=C4=87?= Date: Sun, 18 Jan 2026 10:10:39 +0100 Subject: [PATCH 22/47] add compile_commands.json genereation --- Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 1787090..b1149d0 100644 --- a/Makefile +++ b/Makefile @@ -18,9 +18,9 @@ KERNEL_OBJ += $(patsubst kernel/%.asm, $(BUILD_DIR)/asm_%.o, $(KERNEL_ASM_SRC)) KLIBC_SRC = $(wildcard klibc/src/*.c) KLIBC_OBJ = $(patsubst klibc/src/%.c, $(BUILD_DIR)/klibc/%.o, $(KLIBC_SRC)) +.PHONY: all stage1 stage2 kernel compile-commands $(BUILD_DIR)/compile_commands.json run gdb clean clean-cross clean-all all: $(DISK_IMG) -.PHONY: stage1 stage2 kernel run gdb clean stage1: $(BUILD_DIR) $(AS) $(ASFLAGS) -o $(BUILD_DIR)/$@.o bootloader/$@.asm $(LD) -Ttext=0x7c00 -melf_i386 -o $(BUILD_DIR)/$@.elf $(BUILD_DIR)/$@.o @@ -57,6 +57,10 @@ $(BUILD_DIR): mkdir -p $@ mkdir -p $(BUILD_DIR)/klibc +compile-commands: $(BUILD_DIR)/compile_commands.json +$(BUILD_DIR)/compile_commands.json: $(BUILD_DIR) + bear --output $@ -- make -B + run: qemu-system-i386 -s -S $(DISK_IMG) From 4ee0ddb9ef213942ec5d45e91f7eac9d6fd9a7f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Borna=20=C5=A0o=C5=A1tari=C4=87?= Date: Sun, 18 Jan 2026 10:15:38 +0100 Subject: [PATCH 23/47] add clangd config file --- .clangd | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .clangd diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..7728b54 --- /dev/null +++ b/.clangd @@ -0,0 +1,6 @@ +CompileFlags: + CompilationDatabase: build + +Diagnostics: + UnusedIncludes: Strict + MissingIncludes: Strict From 2b32a29890608b791563e6817b99dbb825a52139 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Borna=20=C5=A0o=C5=A1tari=C4=87?= Date: Sun, 18 Jan 2026 10:18:23 +0100 Subject: [PATCH 24/47] added editorconfig file --- .editorconfig | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..3f415b2 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[Makefile] +indent_style = tab +indent_size = 8 +tab_width = 8 From f7b6a78b593763a3b8ea82c456d91c69b6250a94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Borna=20=C5=A0o=C5=A1tari=C4=87?= Date: Sun, 18 Jan 2026 10:40:34 +0100 Subject: [PATCH 25/47] add .clang-format --- .clang-format | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..d9bcc1f --- /dev/null +++ b/.clang-format @@ -0,0 +1,19 @@ +BasedOnStyle: Google +IndentWidth: 4 +TabWidth: 4 +UseTab: Never +ColumnLimit: 80 + +DerivePointerAlignment: false +PointerAlignment: Right +ReferenceAlignment: Right + +IncludeBlocks: Regroup +IncludeCategories: + # Std headers + - Regex: '<[[:alnum:]_.]+>' + Priority: 2 + + # Other headers + - Regex: '.*' + Priority: 1 From 4d2197aa2d75d4cecd90c0329fc130ffb76c67d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Borna=20=C5=A0o=C5=A1tari=C4=87?= Date: Sun, 18 Jan 2026 10:54:18 +0100 Subject: [PATCH 26/47] add rules for formatting consecutives --- .clang-format | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.clang-format b/.clang-format index d9bcc1f..c97b399 100644 --- a/.clang-format +++ b/.clang-format @@ -8,6 +8,11 @@ DerivePointerAlignment: false PointerAlignment: Right ReferenceAlignment: Right +AlignConsecutiveMacros: Consecutive +AlignTrailingComments: + Kind: Always + OverEmptyLines: 0 + IncludeBlocks: Regroup IncludeCategories: # Std headers From 40462e55e60bbbb153f1e9ff90e6a014493c3a76 Mon Sep 17 00:00:00 2001 From: Gregory Bowne Date: Wed, 26 Nov 2025 15:53:58 -0800 Subject: [PATCH 27/47] Update display.h updated header for display driver display.c and display.h this will need to be finished wired up. Old display driver would have done nothing. --- kernel/display.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/kernel/display.h b/kernel/display.h index 1ae4765..fa84652 100644 --- a/kernel/display.h +++ b/kernel/display.h @@ -2,13 +2,21 @@ #define DISPLAY_H #include +#include "vga.h" // Include VGA functions -#define VGA_PORT 0x3C0 // Base port for VGA +#define VGA_PORT 0x3C0 // Base port for VGA (Often used for general control, though 0x3D4/0x3D5 are used for cursor) // Function prototypes void init_display(void); void enumerate_displays(void); -void set_display_mode(uint8_t mode); +void set_display_mode(uint8_t mode); // In this context, modes are typically BIOS or VESA modes, which are complex. + // We'll treat this as a placeholder/simple mode call. void clear_display(void); +// New function to write a string using the VGA driver +void display_write_string(const char* str); + +// New function to print a formatted string using the VGA driver +void display_printf(const char* format, ...); + #endif // DISPLAY_H From 6ad8644752c017e2ed170b47d18d22ed15a38898 Mon Sep 17 00:00:00 2001 From: Gregory Bowne Date: Wed, 26 Nov 2025 16:02:07 -0800 Subject: [PATCH 28/47] Update display.c Added the 95% completely wired up display driver implementation file --- kernel/display.c | 77 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 60 insertions(+), 17 deletions(-) diff --git a/kernel/display.c b/kernel/display.c index 3f8d14c..a22dc24 100644 --- a/kernel/display.c +++ b/kernel/display.c @@ -1,36 +1,79 @@ #include "display.h" -#include "io.h" // Include your I/O header for port access +#include "io.h" #include "vga.h" // Initialize the display void init_display(void) { - // Initialize VGA settings, if necessary - // This could involve setting up the VGA mode, etc. - set_display_mode(0x13); // Example: Set to 320x200 256-color mode + // Initialize the VGA driver. This typically sets up the 80x25 text mode, + // clears the screen, and sets the cursor. + vga_init(); } // Enumerate connected displays void enumerate_displays(void) { - // This is a simplified example. Actual enumeration may require - // reading from specific VGA registers or using BIOS interrupts. + // This function is often a complex operation in a real driver. + // In this simplified kernel/VGA text mode environment, we use printf + // to output a message and rely on the fact that VGA is present. - // For demonstration, we will just print a message - // In a real driver, you would check the VGA registers - // to determine connected displays. - clear_display(); - // Here you would typically read from VGA registers to find connected displays - // For example, using inb() to read from VGA ports + // Clear the display before printing a message + vga_clear(vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK)); + + // Output a simplified enumeration message + vga_printf("Display: Standard VGA Text Mode (80x25) Detected.\n"); + + // In a real driver, you would use inb() and outb() with specific VGA ports + // to read information (e.g., from the CRTC registers 0x3D4/0x3D5) + // to check for display presence or configuration. } // Set the display mode +// NOTE: Setting arbitrary VGA modes (like 0x13 for 320x200) is very complex +// and requires writing hundreds of register values, often done via BIOS in +// real mode. Since we are in protected mode and have a simple text driver, +// this function is kept simple or treated as a placeholder for full mode changes. void set_display_mode(uint8_t mode) { - // Set the VGA mode by writing to the appropriate registers - outb(VGA_PORT, mode); // Example function to write to a port + // Check if the requested mode is a known mode (e.g., VGA Text Mode 3) + // For this example, we simply acknowledge the call. + // A true mode set would involve complex register sequencing. + + // The provided vga.c is a Text Mode driver, so a graphical mode set + // like 0x13 (320x200 256-color) would break the existing vga_printf functionality. + + // A simplified text-mode-specific response: + if (mode == 0x03) { // Mode 3 is standard 80x25 text mode + vga_printf("Display mode set to 80x25 Text Mode (Mode 0x03).\n"); + vga_init(); // Re-initialize the text mode + } else { + // Simple I/O example based on the original structure (Caution: Incomplete for full mode set) + outb(VGA_PORT, mode); // Example function to write to a port + vga_printf("Attempting to set display mode to 0x%x. (Warning: May break current display)\n", mode); + } } // Clear the display void clear_display(void) { - // Clear the display by filling it with a color - // This is a placeholder for actual clearing logic - // You would typically write to video memory here + // Use the VGA driver's clear function, typically clearing to black on light grey + // or black on black. We'll use the black on light grey from vga_init for consistency. + vga_clear(vga_entry_color(VGA_COLOR_BLACK, VGA_COLOR_LIGHT_GREY)); + // Reset cursor to 0, 0 + vga_set_cursor_position(0, 0); +} + +// Helper function to write a string +void display_write_string(const char* str) { + // Use the VGA driver's string writing function + vga_write_string(str, my_strlen(str)); +} + +// Helper function to print a formatted string +void display_printf(const char* format, ...) { + // Use the VGA driver's printf function + va_list args; + va_start(args, format); + + // The vga_printf function already handles the va_list internally, + // so we can just call it directly. + vga_printf(format, args); + + va_end(args); } From 57ac458a4f0a2f0533aafa2aeaab31625272edf1 Mon Sep 17 00:00:00 2001 From: Gregory Bowne Date: Thu, 15 Jan 2026 17:00:37 -0800 Subject: [PATCH 29/47] Update vga.h Add vga_init(); function prototype --- kernel/vga.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/vga.h b/kernel/vga.h index e37b2cf..9dfc859 100644 --- a/kernel/vga.h +++ b/kernel/vga.h @@ -35,6 +35,7 @@ typedef enum { // Function prototypes uint8_t vga_entry_color(vga_color fg, vga_color bg); uint16_t vga_entry(unsigned char uc, uint8_t color); +void vga_init(void); void vga_put_entry_at(char c, uint8_t color, size_t x, size_t y); void vga_clear(uint8_t color); @@ -50,4 +51,4 @@ void vga_set_cursor_blink_rate(uint8_t rate); void vga_printf(const char* format, ...); -#endif \ No newline at end of file +#endif From bb6f9d1a169f8596fcada104672e1e34d2e26b71 Mon Sep 17 00:00:00 2001 From: Gregory Bowne Date: Fri, 16 Jan 2026 01:19:01 -0800 Subject: [PATCH 30/47] Update vga.c Change my_strlen to use our klibc strlen in klibc/include/string.h --- kernel/vga.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/vga.c b/kernel/vga.c index 0f21c62..4a070ac 100644 --- a/kernel/vga.c +++ b/kernel/vga.c @@ -3,7 +3,7 @@ #include #include #include -#include "string_utils.h" +#include "klibc/include/string.h" void outb(uint16_t port, uint8_t value) { __asm__ volatile("outb %0, %1" : : "a"(value), "Nd"(port)); @@ -134,7 +134,7 @@ void vga_printf(const char* format, ...) { va_end(args); // Now you can use the buffer with vga_write_string - vga_write_string(buffer, my_strlen(buffer)); // Use my_strlen instead of strlen + vga_write_string(buffer, strlen(buffer)); // Use my_strlen instead of strlen } void vga_init(void) { From f08204a2b3497abdc813b0df116581d61f9ffc20 Mon Sep 17 00:00:00 2001 From: vmttmv Date: Fri, 16 Jan 2026 20:49:06 +0200 Subject: [PATCH 31/47] Fix includes for string.h/string_utils.h --- kernel/display.c | 3 ++- kernel/vga.c | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/kernel/display.c b/kernel/display.c index a22dc24..c4acb37 100644 --- a/kernel/display.c +++ b/kernel/display.c @@ -1,3 +1,4 @@ +#include #include "display.h" #include "io.h" #include "vga.h" @@ -62,7 +63,7 @@ void clear_display(void) { // Helper function to write a string void display_write_string(const char* str) { // Use the VGA driver's string writing function - vga_write_string(str, my_strlen(str)); + vga_write_string(str, strlen(str)); } // Helper function to print a formatted string diff --git a/kernel/vga.c b/kernel/vga.c index 4a070ac..205d392 100644 --- a/kernel/vga.c +++ b/kernel/vga.c @@ -1,9 +1,9 @@ -#include "vga.h" #include #include #include #include -#include "klibc/include/string.h" +#include "string_utils.h" +#include "vga.h" void outb(uint16_t port, uint8_t value) { __asm__ volatile("outb %0, %1" : : "a"(value), "Nd"(port)); From fa664331792321b95daae8c934e9dc9c3178c639 Mon Sep 17 00:00:00 2001 From: Gregory Bowne Date: Sun, 18 Jan 2026 16:20:43 -0800 Subject: [PATCH 32/47] Create hid.h Adding base HID device support for early HID standards 1.0 --- kernel/hid.h | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 kernel/hid.h diff --git a/kernel/hid.h b/kernel/hid.h new file mode 100644 index 0000000..68029ae --- /dev/null +++ b/kernel/hid.h @@ -0,0 +1,46 @@ +#ifndef HID_H +#define HID_H + +#include +#include + +// HID Report types +#define HID_REPORT_INPUT 0x01 +#define HID_REPORT_OUTPUT 0x02 +#define HID_REPORT_FEATURE 0x03 + +// HID usage page constants (USB HID) +#define HID_USAGE_PAGE_GENERIC 0x01 +#define HID_USAGE_KEYBOARD 0x06 +#define HID_USAGE_MOUSE 0x02 + +// HID keyboard and mouse data +typedef struct { + uint8_t modifier; // Modifier keys (shift, ctrl, alt, etc.) + uint8_t reserved; // Reserved byte + uint8_t keycodes[6]; // Keycodes for keys pressed +} keyboard_hid_report_t; + +typedef struct { + uint8_t buttons; // Mouse buttons (bitwise: 0x01 = left, 0x02 = right, 0x04 = middle) + int8_t x; // X axis movement + int8_t y; // Y axis movement + int8_t wheel; // Mouse wheel +} mouse_hid_report_t; + +// Initialize the HID subsystem +void hid_init(void); + +// Process an incoming HID report +void hid_process_report(uint8_t* report, uint8_t length); + +// Process HID keyboard report +void hid_process_keyboard_report(const keyboard_hid_report_t* report); + +// Process HID mouse report +void hid_process_mouse_report(const mouse_hid_report_t* report); + +// USB HID report descriptor parsing +bool hid_parse_descriptor(uint8_t* descriptor, uint32_t length); + +#endif // HID_H From 4b981a5d1876ec7f57ff2843e95f604cceba9ef4 Mon Sep 17 00:00:00 2001 From: Gregory Bowne Date: Sun, 18 Jan 2026 16:21:45 -0800 Subject: [PATCH 33/47] Create hid.c Add bass HID implementation --- kernel/hid.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 kernel/hid.c diff --git a/kernel/hid.c b/kernel/hid.c new file mode 100644 index 0000000..5192d01 --- /dev/null +++ b/kernel/hid.c @@ -0,0 +1,66 @@ +#include "hid.h" +#include "usb.h" +#include "mouse.h" +#include "keyboard.h" +#include "print.h" +#include +#include + +// Global variables +static bool hid_initialized = false; + +void hid_init(void) { + if (hid_initialized) return; + hid_initialized = true; + + // Initialize keyboard and mouse HID handling + keyboard_init(); + // Assume USB mouse has been initialized and is connected. + usb_hid_init(); // Initializes USB HID for both keyboard and mouse +} + +void hid_process_report(uint8_t* report, uint8_t length) { + // Process the HID report based on its type + if (length == 8) { // Assuming a standard 8-byte report for HID keyboard + keyboard_hid_report_t* k_report = (keyboard_hid_report_t*) report; + hid_process_keyboard_report(k_report); + } else if (length == 3) { // Assuming a standard 3-byte report for HID mouse + mouse_hid_report_t* m_report = (mouse_hid_report_t*) report; + hid_process_mouse_report(m_report); + } +} + +// Handle HID keyboard report +void hid_process_keyboard_report(const keyboard_hid_report_t* report) { + // Iterate over the keycodes and process key presses + for (int i = 0; i < 6; i++) { + uint8_t keycode = report->keycodes[i]; + if (keycode != 0) { + char key = scancode_map[keycode]; + if (key) { + keyboard_buffer_add(key); + } + } + } +} + +// Handle HID mouse report +void hid_process_mouse_report(const mouse_hid_report_t* report) { + // Process mouse movement and button clicks + mouse_data.x += report->x; + mouse_data.y += report->y; + mouse_data.left_button = (report->buttons & 0x01) != 0; + mouse_data.right_button = (report->buttons & 0x02) != 0; + + // Print mouse movement for debugging + print_hex(mouse_data.x, 1, 1); + print_hex(mouse_data.y, 1, 1); + print_hex(report->buttons, 1, 1); +} + +// Parse the HID descriptor (for parsing USB HID device descriptors) +bool hid_parse_descriptor(uint8_t* descriptor, uint32_t length) { + // HID descriptors are defined in the USB HID specification, we'll need to parse them here. + // For now, just return true assuming we have a valid descriptor. + return true; +} From b36356f10c5a83f3b0c861423824cf84d8a5d269 Mon Sep 17 00:00:00 2001 From: Gregory Bowne Date: Sat, 24 Jan 2026 22:13:32 -0800 Subject: [PATCH 34/47] Update keyboard.c remove static --- kernel/keyboard.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/keyboard.c b/kernel/keyboard.c index 8d83d8e..4984dc9 100644 --- a/kernel/keyboard.c +++ b/kernel/keyboard.c @@ -13,7 +13,7 @@ static uint8_t buffer_count = 0; static uint8_t buffer_index = 0; // Basic US QWERTY keymap (scancode to ASCII) -static const char scancode_map[128] = { +const char scancode_map[128] = { 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', // 0x00 - 0x09 '9', '0', '-', '=', '\b', '\t', 'q', 'w', 'e', 'r', // 0x0A - 0x13 't', 'y', 'z', 'u', 'i', 'o', 'p', '[', ']', '\n', // 0x14 - 0x1D From 0f2528e07b59e109e1a130e11b20b172f8e49352 Mon Sep 17 00:00:00 2001 From: Gregory Bowne Date: Sat, 24 Jan 2026 22:14:37 -0800 Subject: [PATCH 35/47] Update keyboard.h Add a extern const for the scancode map --- kernel/keyboard.h | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/keyboard.h b/kernel/keyboard.h index 4f269ed..46cef82 100644 --- a/kernel/keyboard.h +++ b/kernel/keyboard.h @@ -3,5 +3,6 @@ void keyboard_init(void); char keyboard_get_char(void); // Blocking read from buffer +extern const char scancode_map[128]; #endif From 45da511bfa3fd0a69aab95b4740ef50b5c99e7d5 Mon Sep 17 00:00:00 2001 From: Gregory Bowne Date: Sat, 24 Jan 2026 22:21:44 -0800 Subject: [PATCH 36/47] Update keyboard.h fixing missing includes and definition --- kernel/keyboard.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kernel/keyboard.h b/kernel/keyboard.h index 46cef82..f60ee49 100644 --- a/kernel/keyboard.h +++ b/kernel/keyboard.h @@ -1,7 +1,10 @@ #ifndef KEYBOARD_H #define KEYBOARD_H +#include + void keyboard_init(void); +void keyboard_buffer_add(char c); char keyboard_get_char(void); // Blocking read from buffer extern const char scancode_map[128]; From f64e40d4609cea1ebafb03fc35ac33a1278fa4c4 Mon Sep 17 00:00:00 2001 From: Gregory Bowne Date: Sat, 24 Jan 2026 22:31:50 -0800 Subject: [PATCH 37/47] Update keyboard.c Fix missing definitions so theres nothing that would break the build --- kernel/keyboard.c | 97 ++++++++++++++++++++++++++++++----------------- 1 file changed, 62 insertions(+), 35 deletions(-) diff --git a/kernel/keyboard.c b/kernel/keyboard.c index 4984dc9..4e34d72 100644 --- a/kernel/keyboard.c +++ b/kernel/keyboard.c @@ -2,64 +2,91 @@ #include "io.h" #include "isr.h" #include "terminal.h" +#include #define KEYBOARD_DATA_PORT 0x60 #define KEY_BUFFER_SIZE 256 -static char key_buffer[KEY_BUFFER_SIZE]; -static uint8_t buffer_head = 0; // Write position (interrupt) -static uint8_t buffer_tail = 0; // Read position (get_char) -static uint8_t buffer_count = 0; -static uint8_t buffer_index = 0; +// Use volatile so the compiler knows these change inside interrupts +static volatile char key_buffer[KEY_BUFFER_SIZE]; +static volatile uint8_t buffer_head = 0; +static volatile uint8_t buffer_tail = 0; +static volatile uint8_t buffer_count = 0; -// Basic US QWERTY keymap (scancode to ASCII) +// Exported map: Removed 'static' so hid.c can reference it if needed const char scancode_map[128] = { - 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', // 0x00 - 0x09 - '9', '0', '-', '=', '\b', '\t', 'q', 'w', 'e', 'r', // 0x0A - 0x13 - 't', 'y', 'z', 'u', 'i', 'o', 'p', '[', ']', '\n', // 0x14 - 0x1D - 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', // 0x1E - 0x27 - ';', '\'', '`', 0, '\\', 'x', 'c', 'v', 'b', // 0x28 - 0x31 - 'n', 'm', ',', '.', '/', 0, '*', 0, ' ', 0, // 0x32 - 0x3B - // rest can be filled as needed + 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', + '9', '0', '-', '=', '\b', '\t', 'q', 'w', 'e', 'r', + 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', 0, + 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', + '\'', '`', 0, '\\', 'z', 'x', 'c', 'v', 'b', 'n', + 'm', ',', '.', '/', 0, '*', 0, ' ', 0 }; -// Interrupt handler for IRQ1 -void keyboard_callback(void) { - uint8_t scancode = inb(KEYBOARD_DATA_PORT); - - if (scancode & 0x80) return; // Ignore key release - - char c = scancode_map[scancode]; +/** + * Shared function used by both PS/2 (callback) and USB (hid.c) + * This fixes the "undefined reference to keyboard_buffer_add" error. + */ +void keyboard_buffer_add(char c) { if (!c) return; uint8_t next_head = (buffer_head + 1) % KEY_BUFFER_SIZE; - // Drop key if buffer full - if (next_head == buffer_tail) return; + // If buffer is full, we must drop the key + if (next_head == buffer_tail) { + return; + } key_buffer[buffer_head] = c; buffer_head = next_head; buffer_count++; + // Echo to terminal terminal_putchar(c); } -void keyboard_init() { - register_interrupt_handler(33, keyboard_callback); // IRQ1 = int 33 (0x21) +/** + * Hardware Interrupt Handler for PS/2 + */ +void keyboard_callback(void) { + uint8_t scancode = inb(KEYBOARD_DATA_PORT); + + // Ignore break codes (key release) + if (scancode & 0x80) return; + + char c = scancode_map[scancode]; + keyboard_buffer_add(c); } -// Blocking read (returns one char) +void keyboard_init(void) { + buffer_head = 0; + buffer_tail = 0; + buffer_count = 0; + // IRQ1 is usually mapped to IDT entry 33 + register_interrupt_handler(33, keyboard_callback); +} + +/** + * Blocking read with a safe HLT to prevent CPU 100% usage + */ char keyboard_get_char(void) { - while (buffer_count == 0) { - __asm__ __volatile__("hlt"); // Better than busy loop - } - char c; - __asm__ __volatile__("cli"); - c = key_buffer[buffer_tail]; - buffer_tail = (buffer_tail + 1) % KEY_BUFFER_SIZE; - buffer_count--; - __asm__ __volatile__("sti"); - return c; + while (1) { + __asm__ __volatile__("cli"); // Disable interrupts to check buffer_count safely + + if (buffer_count > 0) { + c = key_buffer[buffer_tail]; + buffer_tail = (buffer_tail + 1) % KEY_BUFFER_SIZE; + buffer_count--; + __asm__ __volatile__("sti"); // Re-enable interrupts after reading + return c; + } + + /* * IMPORTANT: 'sti' followed by 'hlt' is guaranteed by x86 + * to execute 'hlt' BEFORE the next interrupt can trigger. + * This prevents the race condition hang. + */ + __asm__ __volatile__("sti; hlt"); + } } From 8aea3c3c0ede84cda9d04973343510c9462bab7e Mon Sep 17 00:00:00 2001 From: Gregory Bowne Date: Sat, 24 Jan 2026 22:46:03 -0800 Subject: [PATCH 38/47] Update mouse.c Make mouse_data non static --- kernel/mouse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/mouse.c b/kernel/mouse.c index 8d6ece6..c03e014 100644 --- a/kernel/mouse.c +++ b/kernel/mouse.c @@ -5,7 +5,7 @@ #include // Mouse buffer -static mouse_data_t mouse_data; +mouse_data_t mouse_data; // Read USB mouse data mouse_data_t usb_read_mouse(void) { From d2b19ce376dbb6baf94ea27632a001e5a4c2d421 Mon Sep 17 00:00:00 2001 From: Gregory Bowne Date: Sat, 24 Jan 2026 22:49:07 -0800 Subject: [PATCH 39/47] Update mouse.h Fixes static --- kernel/mouse.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/mouse.h b/kernel/mouse.h index 96a4f85..d8ec913 100644 --- a/kernel/mouse.h +++ b/kernel/mouse.h @@ -12,6 +12,8 @@ typedef struct { bool right_button; } mouse_data_t; +extern mouse_data_t mouse_data; + // Function declarations for USB 1.x HID mouse support bool usb_mouse_init(void); bool usb_mouse_detected(void); From df33351d609952fe59f4b7b979342359a6eb816f Mon Sep 17 00:00:00 2001 From: Gregory Bowne Date: Sun, 25 Jan 2026 08:24:43 -0800 Subject: [PATCH 40/47] Update keyboard.h fixed typo in header include --- kernel/keyboard.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/kernel/keyboard.h b/kernel/keyboard.h index f60ee49..86e6bde 100644 --- a/kernel/keyboard.h +++ b/kernel/keyboard.h @@ -1,11 +1,12 @@ #ifndef KEYBOARD_H #define KEYBOARD_H -#include +#include void keyboard_init(void); void keyboard_buffer_add(char c); -char keyboard_get_char(void); // Blocking read from buffer -extern const char scancode_map[128]; +char keyboard_get_char(void); + +extern const char scancode_map[128]; #endif From cae3731f916a9492c0d1a8c99ad71d09ac713ada Mon Sep 17 00:00:00 2001 From: Gregory Bowne Date: Sun, 25 Jan 2026 08:45:45 -0800 Subject: [PATCH 41/47] Update hid.c Fixing print_hex error --- kernel/hid.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/kernel/hid.c b/kernel/hid.c index 5192d01..45578f2 100644 --- a/kernel/hid.c +++ b/kernel/hid.c @@ -51,11 +51,10 @@ void hid_process_mouse_report(const mouse_hid_report_t* report) { mouse_data.y += report->y; mouse_data.left_button = (report->buttons & 0x01) != 0; mouse_data.right_button = (report->buttons & 0x02) != 0; - - // Print mouse movement for debugging - print_hex(mouse_data.x, 1, 1); - print_hex(mouse_data.y, 1, 1); - print_hex(report->buttons, 1, 1); + + print_hex((uint32_t)mouse_data.x, 1, 1); + print_hex((uint32_t)mouse_data.y, 1, 1); + print_hex((uint32_t)report->buttons, 1, 1); } // Parse the HID descriptor (for parsing USB HID device descriptors) From eecc2561da8878d943ff0d9f7b6d0b424401a827 Mon Sep 17 00:00:00 2001 From: Gregory Bowne Date: Sun, 18 Jan 2026 15:40:23 -0800 Subject: [PATCH 42/47] Update gui.c Add base gui implementation --- kernel/gui.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/kernel/gui.c b/kernel/gui.c index e69de29..281d531 100644 --- a/kernel/gui.c +++ b/kernel/gui.c @@ -0,0 +1,66 @@ +#include "gui.h" +#include "vga.h" // VGA functions for drawing and clearing screen +#include "framebuffer.h" // For pixel manipulation if needed + +// Initialize the GUI (could set up any global state or variables here) +void gui_init(void) { + // Clear the screen with black or any color + gui_clear(vga_entry_color(VGA_COLOR_BLACK, VGA_COLOR_WHITE)); +} + +// Draw a window (simple rectangle with a title) +void gui_draw_window(gui_window_t* window) { + // Draw the window's border + for (uint32_t y = 0; y < window->height; ++y) { + for (uint32_t x = 0; x < window->width; ++x) { + // Check if we are at the border + if (x == 0 || y == 0 || x == window->width - 1 || y == window->height - 1) { + vga_put_entry_at('#', vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK), window->x + x, window->y + y); + } else { + // Fill the inside of the window + vga_put_entry_at(' ', vga_entry_color(VGA_COLOR_BLACK, VGA_COLOR_BLACK), window->x + x, window->y + y); + } + } + } + + // Draw the title at the top + if (window->title) { + size_t i = 0; + while (window->title[i] != '\0' && i < window->width - 2) { + vga_put_entry_at(window->title[i], vga_entry_color(VGA_COLOR_WHITE, VGA_COLOR_BLACK), window->x + i + 1, window->y); + i++; + } + } +} + +// Draw a button (a simple rectangle with text in the middle) +void gui_draw_button(gui_button_t* button) { + for (uint32_t y = 0; y < button->height; ++y) { + for (uint32_t x = 0; x < button->width; ++x) { + // Check if we are at the border + if (x == 0 || y == 0 || x == button->width - 1 || y == button->height - 1) { + vga_put_entry_at('#', vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK), button->x + x, button->y + y); + } else { + // Fill the inside of the button + vga_put_entry_at(' ', vga_entry_color(VGA_COLOR_BLACK, VGA_COLOR_BLACK), button->x + x, button->y + y); + } + } + } + + // Draw the label in the center of the button + size_t label_len = 0; + while (button->label[label_len] != '\0') { + label_len++; + } + + size_t start_x = button->x + (button->width - label_len) / 2; + size_t start_y = button->y + (button->height - 1) / 2; + for (size_t i = 0; i < label_len; ++i) { + vga_put_entry_at(button->label[i], vga_entry_color(VGA_COLOR_WHITE, VGA_COLOR_BLACK), start_x + i, start_y); + } +} + +// Clear the screen with a color +void gui_clear(uint32_t color) { + vga_clear(color); // Just clear the VGA screen with a solid color +} From 9d37b7a9447ef6430da9a22ef43aa01d35ae01b7 Mon Sep 17 00:00:00 2001 From: Gregory Bowne Date: Sun, 18 Jan 2026 15:43:03 -0800 Subject: [PATCH 43/47] Update gui.h Adds gui base header --- kernel/gui.h | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/kernel/gui.h b/kernel/gui.h index e69de29..3c39760 100644 --- a/kernel/gui.h +++ b/kernel/gui.h @@ -0,0 +1,34 @@ +#ifndef GUI_H +#define GUI_H + +#include +#include + +#define GUI_WINDOW_WIDTH 80 +#define GUI_WINDOW_HEIGHT 25 +#define GUI_BUTTON_WIDTH 10 +#define GUI_BUTTON_HEIGHT 3 + +// Window structure +typedef struct { + uint32_t x, y; + uint32_t width, height; + uint32_t color; // Background color + const char* title; +} gui_window_t; + +// Button structure +typedef struct { + uint32_t x, y; + uint32_t width, height; + uint32_t color; // Background color + const char* label; +} gui_button_t; + +// Function prototypes for GUI elements +void gui_init(void); +void gui_draw_window(gui_window_t* window); +void gui_draw_button(gui_button_t* button); +void gui_clear(uint32_t color); + +#endif // GUI_H From 0a4f0e59e328307ae869a959822c5bb3dc2951b8 Mon Sep 17 00:00:00 2001 From: Gregory Bowne Date: Sun, 18 Jan 2026 17:46:26 -0800 Subject: [PATCH 44/47] Create ata.h Add base ATA PIO mode driver so that filesystems like fat16 fat32 work. --- kernel/ata.h | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 kernel/ata.h diff --git a/kernel/ata.h b/kernel/ata.h new file mode 100644 index 0000000..c503d5c --- /dev/null +++ b/kernel/ata.h @@ -0,0 +1,43 @@ +#ifndef ATA_H +#define ATA_H + +#include +#include + +/* ATA I/O ports */ +#define ATA_PRIMARY_IO 0x1F0 +#define ATA_PRIMARY_CTRL 0x3F6 + +/* ATA registers */ +#define ATA_REG_DATA 0x00 +#define ATA_REG_ERROR 0x01 +#define ATA_REG_FEATURES 0x01 +#define ATA_REG_SECCOUNT0 0x02 +#define ATA_REG_LBA0 0x03 +#define ATA_REG_LBA1 0x04 +#define ATA_REG_LBA2 0x05 +#define ATA_REG_HDDEVSEL 0x06 +#define ATA_REG_COMMAND 0x07 +#define ATA_REG_STATUS 0x07 + +/* ATA commands */ +#define ATA_CMD_READ_PIO 0x20 +#define ATA_CMD_WRITE_PIO 0x30 +#define ATA_CMD_IDENTIFY 0xEC + +/* Status flags */ +#define ATA_SR_BSY 0x80 +#define ATA_SR_DRDY 0x40 +#define ATA_SR_DRQ 0x08 +#define ATA_SR_ERR 0x01 + +/* Drive select */ +#define ATA_MASTER 0x00 +#define ATA_SLAVE 0x10 + +/* Public API */ +bool ata_init(void); +bool ata_read_sector(uint32_t lba, uint8_t* buffer); +bool ata_write_sector(uint32_t lba, const uint8_t* buffer); + +#endif From 42f0b62e509d5558ba9bcbd11d23fe250f4f8b56 Mon Sep 17 00:00:00 2001 From: Gregory Bowne Date: Sun, 18 Jan 2026 17:48:39 -0800 Subject: [PATCH 45/47] Create ata.c --- kernel/ata.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 kernel/ata.c diff --git a/kernel/ata.c b/kernel/ata.c new file mode 100644 index 0000000..181503e --- /dev/null +++ b/kernel/ata.c @@ -0,0 +1,91 @@ +#include "ata.h" +#include "io.h" +#include "print.h" + +#define ATA_TIMEOUT 100000 + +static inline void ata_delay(void) { + /* 400ns delay by reading alternate status */ + inb(ATA_PRIMARY_CTRL); + inb(ATA_PRIMARY_CTRL); + inb(ATA_PRIMARY_CTRL); + inb(ATA_PRIMARY_CTRL); +} + +static bool ata_wait(uint8_t mask) { + for (int i = 0; i < ATA_TIMEOUT; i++) { + uint8_t status = inb(ATA_PRIMARY_IO + ATA_REG_STATUS); + if (!(status & ATA_SR_BSY) && (status & mask)) + return true; + } + return false; +} + +bool ata_init(void) { + outb(ATA_PRIMARY_IO + ATA_REG_HDDEVSEL, 0xA0); // master + ata_delay(); + + outb(ATA_PRIMARY_IO + ATA_REG_COMMAND, ATA_CMD_IDENTIFY); + ata_delay(); + + uint8_t status = inb(ATA_PRIMARY_IO + ATA_REG_STATUS); + if (status == 0) + return false; // no drive + + if (!ata_wait(ATA_SR_DRQ)) + return false; + + uint16_t identify[256]; + for (int i = 0; i < 256; i++) + identify[i] = inw(ATA_PRIMARY_IO); + + print_string("[ATA] Primary master detected\n"); + return true; +} + +bool ata_read_sector(uint32_t lba, uint8_t* buffer) { + if (!buffer) return false; + + outb(ATA_PRIMARY_IO + ATA_REG_HDDEVSEL, 0xE0 | ((lba >> 24) & 0x0F)); + outb(ATA_PRIMARY_IO + ATA_REG_SECCOUNT0, 1); + outb(ATA_PRIMARY_IO + ATA_REG_LBA0, (uint8_t)(lba)); + outb(ATA_PRIMARY_IO + ATA_REG_LBA1, (uint8_t)(lba >> 8)); + outb(ATA_PRIMARY_IO + ATA_REG_LBA2, (uint8_t)(lba >> 16)); + outb(ATA_PRIMARY_IO + ATA_REG_COMMAND, ATA_CMD_READ_PIO); + + if (!ata_wait(ATA_SR_DRQ)) + return false; + + for (int i = 0; i < 256; i++) { + uint16_t data = inw(ATA_PRIMARY_IO); + buffer[i * 2] = data & 0xFF; + buffer[i * 2 + 1] = data >> 8; + } + + ata_delay(); + return true; +} + +bool ata_write_sector(uint32_t lba, const uint8_t* buffer) { + if (!buffer) return false; + + outb(ATA_PRIMARY_IO + ATA_REG_HDDEVSEL, 0xE0 | ((lba >> 24) & 0x0F)); + outb(ATA_PRIMARY_IO + ATA_REG_SECCOUNT0, 1); + outb(ATA_PRIMARY_IO + ATA_REG_LBA0, (uint8_t)(lba)); + outb(ATA_PRIMARY_IO + ATA_REG_LBA1, (uint8_t)(lba >> 8)); + outb(ATA_PRIMARY_IO + ATA_REG_LBA2, (uint8_t)(lba >> 16)); + outb(ATA_PRIMARY_IO + ATA_REG_COMMAND, ATA_CMD_WRITE_PIO); + + if (!ata_wait(ATA_SR_DRQ)) + return false; + + for (int i = 0; i < 256; i++) { + uint16_t word = + buffer[i * 2] | + (buffer[i * 2 + 1] << 8); + outw(ATA_PRIMARY_IO, word); + } + + ata_delay(); + return true; +} From 59dfca9ebde516ef8d1da5d4488a06f928f574c1 Mon Sep 17 00:00:00 2001 From: Gregory Bowne Date: Mon, 26 Jan 2026 17:16:31 -0800 Subject: [PATCH 46/47] Update ata.c Updated ata.c to include fixed BSY status register --- kernel/ata.c | 46 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/kernel/ata.c b/kernel/ata.c index 181503e..20ddd12 100644 --- a/kernel/ata.c +++ b/kernel/ata.c @@ -12,9 +12,22 @@ static inline void ata_delay(void) { inb(ATA_PRIMARY_CTRL); } +bool ata_wait_ready(void) { + for (int i = 0; i < ATA_TIMEOUT; i++) { + uint8_t status = inb(ATA_PRIMARY_IO + ATA_REG_STATUS); + /* Must NOT be busy AND must be ready */ + if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRDY)) + return true; + } + return false; +} + static bool ata_wait(uint8_t mask) { for (int i = 0; i < ATA_TIMEOUT; i++) { uint8_t status = inb(ATA_PRIMARY_IO + ATA_REG_STATUS); + /* If ERR is set, stop waiting and return failure */ + if (status & ATA_SR_ERR) return false; + if (!(status & ATA_SR_BSY) && (status & mask)) return true; } @@ -22,16 +35,17 @@ static bool ata_wait(uint8_t mask) { } bool ata_init(void) { - outb(ATA_PRIMARY_IO + ATA_REG_HDDEVSEL, 0xA0); // master + /* Select drive */ + outb(ATA_PRIMARY_IO + ATA_REG_HDDEVSEL, ATA_MASTER); ata_delay(); + /* Check if drive exists */ + uint8_t status = inb(ATA_PRIMARY_IO + ATA_REG_STATUS); + if (status == 0xFF || status == 0) return false; + outb(ATA_PRIMARY_IO + ATA_REG_COMMAND, ATA_CMD_IDENTIFY); ata_delay(); - uint8_t status = inb(ATA_PRIMARY_IO + ATA_REG_STATUS); - if (status == 0) - return false; // no drive - if (!ata_wait(ATA_SR_DRQ)) return false; @@ -46,20 +60,28 @@ bool ata_init(void) { bool ata_read_sector(uint32_t lba, uint8_t* buffer) { if (!buffer) return false; + /* 1. Wait for drive to be ready for command */ + if (!ata_wait_ready()) return false; + + /* 2. Setup Task File (LBA28) */ outb(ATA_PRIMARY_IO + ATA_REG_HDDEVSEL, 0xE0 | ((lba >> 24) & 0x0F)); outb(ATA_PRIMARY_IO + ATA_REG_SECCOUNT0, 1); outb(ATA_PRIMARY_IO + ATA_REG_LBA0, (uint8_t)(lba)); outb(ATA_PRIMARY_IO + ATA_REG_LBA1, (uint8_t)(lba >> 8)); outb(ATA_PRIMARY_IO + ATA_REG_LBA2, (uint8_t)(lba >> 16)); + + /* 3. Issue Read Command */ outb(ATA_PRIMARY_IO + ATA_REG_COMMAND, ATA_CMD_READ_PIO); + /* 4. Wait for Data Request (DRQ) */ if (!ata_wait(ATA_SR_DRQ)) return false; + /* 5. Transfer data */ for (int i = 0; i < 256; i++) { uint16_t data = inw(ATA_PRIMARY_IO); buffer[i * 2] = data & 0xFF; - buffer[i * 2 + 1] = data >> 8; + buffer[i * 2 + 1] = (data >> 8) & 0xFF; } ata_delay(); @@ -69,20 +91,26 @@ bool ata_read_sector(uint32_t lba, uint8_t* buffer) { bool ata_write_sector(uint32_t lba, const uint8_t* buffer) { if (!buffer) return false; + /* 1. Wait for drive to be ready for command */ + if (!ata_wait_ready()) return false; + + /* 2. Setup Task File */ outb(ATA_PRIMARY_IO + ATA_REG_HDDEVSEL, 0xE0 | ((lba >> 24) & 0x0F)); outb(ATA_PRIMARY_IO + ATA_REG_SECCOUNT0, 1); outb(ATA_PRIMARY_IO + ATA_REG_LBA0, (uint8_t)(lba)); outb(ATA_PRIMARY_IO + ATA_REG_LBA1, (uint8_t)(lba >> 8)); outb(ATA_PRIMARY_IO + ATA_REG_LBA2, (uint8_t)(lba >> 16)); + + /* 3. Issue Write Command */ outb(ATA_PRIMARY_IO + ATA_REG_COMMAND, ATA_CMD_WRITE_PIO); + /* 4. Wait for drive to request data */ if (!ata_wait(ATA_SR_DRQ)) return false; + /* 5. Transfer data */ for (int i = 0; i < 256; i++) { - uint16_t word = - buffer[i * 2] | - (buffer[i * 2 + 1] << 8); + uint16_t word = buffer[i * 2] | (buffer[i * 2 + 1] << 8); outw(ATA_PRIMARY_IO, word); } From dee91ca211aa215989c30254761435eab9e60950 Mon Sep 17 00:00:00 2001 From: Gregory Bowne Date: Mon, 26 Jan 2026 17:18:39 -0800 Subject: [PATCH 47/47] Update ata.h Updated header to match ata.c --- kernel/ata.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kernel/ata.h b/kernel/ata.h index c503d5c..3da5aff 100644 --- a/kernel/ata.h +++ b/kernel/ata.h @@ -32,12 +32,13 @@ #define ATA_SR_ERR 0x01 /* Drive select */ -#define ATA_MASTER 0x00 -#define ATA_SLAVE 0x10 +#define ATA_MASTER 0xA0 +#define ATA_SLAVE 0xB0 /* Public API */ bool ata_init(void); bool ata_read_sector(uint32_t lba, uint8_t* buffer); bool ata_write_sector(uint32_t lba, const uint8_t* buffer); +bool ata_wait_ready(void); #endif