From 34e24c99794d61114bdb4b19c641ac3ce975f831 Mon Sep 17 00:00:00 2001 From: Sebastiaan de Schaetzen Date: Mon, 23 Feb 2026 07:08:34 +0100 Subject: [PATCH 1/8] Add PROMPT.md with original AI prompt (AI) Document the prompt given to the AI to implement ClaudeOS, making the request auditable as described in the README task list requirements. --- PROMPT.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 PROMPT.md diff --git a/PROMPT.md b/PROMPT.md new file mode 100644 index 0000000..40fa180 --- /dev/null +++ b/PROMPT.md @@ -0,0 +1,7 @@ +# Prompt + +The following prompt was given to GitHub Copilot (Claude Sonnet 4.6) on 23 February 2026: + +> Please implement the task list in this readme + +The task list referred to is the one found in README.md, which describes how to build ClaudeOS — an AI-built operating system written in C, targeting 80386 hardware, providing a UNIX-like shell environment. -- 2.49.1 From f3ef92be4f720c51a56d82f0a71286b62a4a4445 Mon Sep 17 00:00:00 2001 From: Sebastiaan de Schaetzen Date: Mon, 23 Feb 2026 07:09:04 +0100 Subject: [PATCH 2/8] Create directory structure (AI) Set up the project directory structure as specified in the README: - apps/: for application source code (e.g. ls, sh) - docs/: for subsystem documentation - libs/: for shared libraries used by apps and/or the kernel - src/: for kernel source code - src/it/: for integration tests - vendor/: for 3rd-party source code (AI will not modify this) The build/ directory is excluded via .gitignore as it contains only build artifacts. ISO and floppy image files are also gitignored. --- .gitignore | 3 +++ apps/.gitkeep | 0 docs/.gitkeep | 0 libs/.gitkeep | 0 src/.gitkeep | 0 src/it/.gitkeep | 0 vendor/.gitkeep | 0 7 files changed, 3 insertions(+) create mode 100644 .gitignore create mode 100644 apps/.gitkeep create mode 100644 docs/.gitkeep create mode 100644 libs/.gitkeep create mode 100644 src/.gitkeep create mode 100644 src/it/.gitkeep create mode 100644 vendor/.gitkeep diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c2755d --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build/ +*.iso +*.img diff --git a/apps/.gitkeep b/apps/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/.gitkeep b/docs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/libs/.gitkeep b/libs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/.gitkeep b/src/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/it/.gitkeep b/src/it/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/vendor/.gitkeep b/vendor/.gitkeep new file mode 100644 index 0000000..e69de29 -- 2.49.1 From bbd7d3725ba412881716ef71535d08bb701a534e Mon Sep 17 00:00:00 2001 From: Sebastiaan de Schaetzen Date: Mon, 23 Feb 2026 07:13:49 +0100 Subject: [PATCH 3/8] Add devcontainer for i386 kernel development (AI) Provides a reproducible Debian-based build environment with: - Clang + LLD for cross-compilation to i386-elf targets - GRUB 2 tools (grub-pc-bin, xorriso, mtools) for bootable ISO and floppy image generation - QEMU (i386) for integration test execution - CMake + Ninja for the build system The Dockerfile includes a self-test that verifies Clang + lld can produce a valid ELF 32-bit i386 binary before the image is accepted. --- .devcontainer/Dockerfile | 43 +++++++++++++++++++++++++++++++++ .devcontainer/devcontainer.json | 24 ++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..4eee051 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,43 @@ +# ClaudeOS build environment +# +# Provides everything needed to build and test a bare-metal i386 kernel: +# - Clang + LLD for cross-compilation to i386-elf +# - GRUB 2 tools for generating bootable ISO and floppy images +# - QEMU (i386) for running integration tests +# - CMake + Ninja for the build system +# - xorriso and mtools used internally by grub-mkrescue / grub-mkimage + +FROM debian:bookworm-slim + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y --no-install-recommends \ + # Core build tools + clang \ + lld \ + cmake \ + ninja-build \ + make \ + git \ + # GRUB image generation + grub-pc-bin \ + grub-common \ + xorriso \ + mtools \ + # QEMU for running the OS + qemu-system-x86 \ + # Handy utilities + file \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +# Verify the cross-compilation toolchain can produce an i386-elf binary. +RUN echo 'void _start(void){for(;;)__asm__("hlt");}' > /tmp/test.c \ + && clang -target i386-elf -m32 -march=i386 -ffreestanding -fno-stack-protector \ + -nostdlib -fuse-ld=lld -Wl,-e,_start \ + /tmp/test.c -o /tmp/test.elf \ + && file /tmp/test.elf | grep -q "ELF 32-bit.*80386" \ + && echo "Toolchain OK" \ + && rm /tmp/test.c /tmp/test.elf + +WORKDIR /workspaces/claude-os diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..fbac6df --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,24 @@ +{ + "name": "ClaudeOS Dev", + "build": { + "dockerfile": "Dockerfile", + "context": ".." + }, + "customizations": { + "vscode": { + "extensions": [ + "ms-vscode.cmake-tools", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack" + ], + "settings": { + "cmake.generator": "Ninja", + "cmake.configureSettings": { + "CMAKE_TOOLCHAIN_FILE": "${workspaceFolder}/cmake/toolchain-i386-clang.cmake" + } + } + } + }, + "postCreateCommand": "echo 'ClaudeOS devcontainer ready!'", + "remoteUser": "root" +} -- 2.49.1 From 1cf39a70cf42042408d7bbf0a854e6a2c8e89a59 Mon Sep 17 00:00:00 2001 From: Sebastiaan de Schaetzen Date: Mon, 23 Feb 2026 08:12:51 +0100 Subject: [PATCH 4/8] Update instructions (human) --- HUMAN.md | 8 +++++--- README.md | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/HUMAN.md b/HUMAN.md index 1d4571d..bb29092 100644 --- a/HUMAN.md +++ b/HUMAN.md @@ -3,12 +3,14 @@ This document describes which files are written fully by humans, which had human ## Fully Human The following files were written completely by humans: -- HUMAN.md -- README.md +- `HUMAN.md` +- `README.md` - Everything in `vendor/`. While we don't actually know if it was written by humans, certainly the AI used for this project has not touched it. ## Partially Human -The following files were written partially by humans: +The following files were written partially by humans or was generated by an AI and was later edited: + +- `.devcontainer/Dockerfile` ## Fully AI Any file not in the above two categories were completely written by an AI without a human manually editing the file afterwards. \ No newline at end of file diff --git a/README.md b/README.md index 795d967..507042b 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ The source code uses the following directory structure: # Task list An AI implementing this operating system should implement features in this order. -After each feature, it should create a new Git commit. +After each feature, it should create a new Git commit and push the commit. The git commit should describe what is done and why certain choices were made. The title of the commit should always end with (AI), to indicate that it was written by an AI. -- 2.49.1 From e8fa7e1f2897c48029d5f8b729c06dfa8017b436 Mon Sep 17 00:00:00 2001 From: Sebastiaan de Schaetzen Date: Mon, 23 Feb 2026 08:13:18 +0100 Subject: [PATCH 5/8] Update devcontainer (AI+human) --- .devcontainer/Dockerfile | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 4eee051..678fe0b 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -7,37 +7,35 @@ # - CMake + Ninja for the build system # - xorriso and mtools used internally by grub-mkrescue / grub-mkimage -FROM debian:bookworm-slim +FROM alpine:3.23 -ENV DEBIAN_FRONTEND=noninteractive - -RUN apt-get update && apt-get install -y --no-install-recommends \ +RUN apk add --no-cache \ # Core build tools clang \ lld \ cmake \ - ninja-build \ + ninja \ make \ git \ # GRUB image generation - grub-pc-bin \ - grub-common \ + grub \ xorriso \ mtools \ # QEMU for running the OS - qemu-system-x86 \ + qemu-system-i386 \ # Handy utilities file \ - ca-certificates \ - && rm -rf /var/lib/apt/lists/* + ca-certificates # Verify the cross-compilation toolchain can produce an i386-elf binary. +# Compile and link separately so ld.lld is invoked directly, avoiding the +# host gcc linker driver (which rejects -m32 on non-x86 hosts such as aarch64). RUN echo 'void _start(void){for(;;)__asm__("hlt");}' > /tmp/test.c \ - && clang -target i386-elf -m32 -march=i386 -ffreestanding -fno-stack-protector \ - -nostdlib -fuse-ld=lld -Wl,-e,_start \ - /tmp/test.c -o /tmp/test.elf \ - && file /tmp/test.elf | grep -q "ELF 32-bit.*80386" \ + && clang -v -target i386-elf -march=i386 -ffreestanding -fno-stack-protector \ + -c /tmp/test.c -o /tmp/test.o \ + && ld.lld -m elf_i386 -e _start --nostdlib /tmp/test.o -o /tmp/test.elf \ + && file /tmp/test.elf | grep -q "ELF 32-bit.*386" \ && echo "Toolchain OK" \ - && rm /tmp/test.c /tmp/test.elf + && rm /tmp/test.c /tmp/test.o /tmp/test.elf WORKDIR /workspaces/claude-os -- 2.49.1 From 6d9ab26423bd39f58c92702a1d94b9b3b97a9985 Mon Sep 17 00:00:00 2001 From: Sebastiaan de Schaetzen Date: Mon, 23 Feb 2026 08:15:11 +0100 Subject: [PATCH 6/8] Update readme (human) --- HUMAN.md | 2 +- README.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/HUMAN.md b/HUMAN.md index bb29092..d0f7638 100644 --- a/HUMAN.md +++ b/HUMAN.md @@ -3,7 +3,7 @@ This document describes which files are written fully by humans, which had human ## Fully Human The following files were written completely by humans: -- `HUMAN.md` +- `HUMAN.md` (except for the checkmarks in the tasklist) - `README.md` - Everything in `vendor/`. While we don't actually know if it was written by humans, certainly the AI used for this project has not touched it. diff --git a/README.md b/README.md index 507042b..74c1163 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ An AI implementing this operating system should implement features in this order After each feature, it should create a new Git commit and push the commit. The git commit should describe what is done and why certain choices were made. The title of the commit should always end with (AI), to indicate that it was written by an AI. +Once a task is completed, it should be checked off. - [ ] Create directory structure - [ ] Create initial build system -- 2.49.1 From f42d65267bb35e250c654573bddda5dbb64c5a8d Mon Sep 17 00:00:00 2001 From: Sebastiaan de Schaetzen Date: Mon, 23 Feb 2026 08:30:20 +0100 Subject: [PATCH 7/8] Force devcontainer to use amd64 (human) --- .devcontainer/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 678fe0b..ff3e165 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -7,7 +7,7 @@ # - CMake + Ninja for the build system # - xorriso and mtools used internally by grub-mkrescue / grub-mkimage -FROM alpine:3.23 +FROM --platform=linux/amd64 alpine:3.23 RUN apk add --no-cache \ # Core build tools @@ -19,6 +19,7 @@ RUN apk add --no-cache \ git \ # GRUB image generation grub \ + grub-bios \ xorriso \ mtools \ # QEMU for running the OS -- 2.49.1 From cd0e1cafae79c4011514676f8cc9510a1c799e48 Mon Sep 17 00:00:00 2001 From: Sebastiaan de Schaetzen Date: Mon, 23 Feb 2026 08:30:43 +0100 Subject: [PATCH 8/8] There was an attempt... (AI) --- CMakeLists.txt | 9 ++++ cmake/toolchain-i386-clang.cmake | 59 ++++++++++++++++++++ src/CMakeLists.txt | 33 ++++++++++++ src/boot/boot.s | 92 ++++++++++++++++++++++++++++++++ src/debugport.c | 39 ++++++++++++++ src/include/debugport.h | 34 ++++++++++++ src/include/io.h | 41 ++++++++++++++ src/kernel.c | 45 ++++++++++++++++ src/linker.ld | 53 ++++++++++++++++++ 9 files changed, 405 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 cmake/toolchain-i386-clang.cmake create mode 100644 src/CMakeLists.txt create mode 100644 src/boot/boot.s create mode 100644 src/debugport.c create mode 100644 src/include/debugport.h create mode 100644 src/include/io.h create mode 100644 src/kernel.c create mode 100644 src/linker.ld diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..e717c9b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.20) + +project(ClaudeOS C ASM) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) + +# Kernel +add_subdirectory(src) diff --git a/cmake/toolchain-i386-clang.cmake b/cmake/toolchain-i386-clang.cmake new file mode 100644 index 0000000..f3dedb6 --- /dev/null +++ b/cmake/toolchain-i386-clang.cmake @@ -0,0 +1,59 @@ +# Cross-compilation toolchain for i386-elf using Clang + LLD. +# +# This targets a bare-metal i386 (80386) environment with no host OS +# libraries. lld is used as the linker because the macOS system ld does +# not support the ELF32 output format required for a GRUB-bootable kernel. + +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_PROCESSOR i386) + +# Use Clang for both C and assembly. +set(CMAKE_C_COMPILER clang) +set(CMAKE_ASM_COMPILER clang) + +# Prevent CMake from trying to run the cross-compiled test binaries. +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) + +# Target triple and architecture flags shared by all compilation steps. +set(CROSS_TARGET "i386-elf") +set(CROSS_ARCH_FLAGS "-target ${CROSS_TARGET} -m32 -march=i386 -mtune=i386") + +# C compiler flags: +# - ffreestanding : no hosted C runtime assumptions +# - fno-stack-protector : no __stack_chk_fail dependency +# - fno-builtin : avoid implicit replacements of standard functions +set(CMAKE_C_FLAGS_INIT + "${CROSS_ARCH_FLAGS} -ffreestanding -fno-stack-protector -fno-builtin -Wall -Wextra" +) + +# Assembler flags: only the flags the assembler front-end actually accepts. +# C-only flags like -ffreestanding, -fno-stack-protector, etc. must NOT be +# passed here — clang treats them as unused and errors out with -Werror. +# -Wno-unused-command-line-argument suppresses CMake's auto-added -MD/-MT/-MF +# dependency-tracking flags that clang ignores in pure assembler mode. +set(CMAKE_ASM_FLAGS_INIT + "-target ${CROSS_TARGET} -m32 -march=i386 -Wno-unused-command-line-argument" +) + +# Linking: +# On aarch64 Alpine the clang driver delegates link steps to the host gcc, +# which does not support -m32 and therefore fails. Bypass the driver entirely +# and call ld.lld directly. The CMake rule variables below replace the normal +# "compile-with-compiler-driver" link invocation. +# +# Rule variables use these placeholders: +# — output binary path +# — all compiled object files +# — extra flags from target_link_options / CMAKE_EXE_LINKER_FLAGS +# — libraries to link (empty for a freestanding kernel) +set(CMAKE_LINKER "ld.lld") +set(CMAKE_C_LINK_EXECUTABLE + "ld.lld -m elf_i386 -o ") +set(CMAKE_ASM_LINK_EXECUTABLE + "ld.lld -m elf_i386 -o ") + +# Keep CMake from accidentally picking up host system headers/libraries. +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..38c51d7 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,33 @@ +set(KERNEL_SOURCES + boot/boot.s + kernel.c + debugport.c +) + +add_executable(kernel ${KERNEL_SOURCES}) + +target_include_directories(kernel PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/include +) + +target_compile_options(kernel PRIVATE + # C-only flags — not valid for the assembler front-end. + $<$:-Werror> + $<$:-ffreestanding> + $<$:-fno-stack-protector> + $<$:-fno-exceptions> + # Suppress the harmless "argument unused" warnings that clang emits + # when CMake passes -MD/-MT/-MF to the assembler. + $<$:-Wno-unused-command-line-argument> +) + +set_target_properties(kernel PROPERTIES + OUTPUT_NAME "claudeos.elf" + SUFFIX "" +) + +target_link_options(kernel PRIVATE + # Pass the linker script directly to ld.lld (CMAKE_C_LINK_EXECUTABLE + # calls ld.lld directly, so these are raw ld flags, not clang driver flags). + -T ${CMAKE_CURRENT_SOURCE_DIR}/linker.ld +) diff --git a/src/boot/boot.s b/src/boot/boot.s new file mode 100644 index 0000000..78872cb --- /dev/null +++ b/src/boot/boot.s @@ -0,0 +1,92 @@ +/* + * boot.s — Multiboot-compliant bootstrap for ClaudeOS. + * + * This file is the very first code executed by GRUB after the kernel is + * loaded. Its responsibilities are: + * + * 1. Provide a valid Multiboot header so that GRUB recognises the image. + * 2. Zero the BSS segment (the C runtime requires it to start at zero). + * 3. Set up a small temporary stack. + * 4. Forward the Multiboot magic value and info-structure pointer to + * kernel_main() as its first and second arguments. + * 5. Halt the CPU if kernel_main() ever returns (it should not). + * + * Multiboot flags used: + * Bit 0 — 4 KiB-align all boot modules. + * Bit 1 — Include memory map in the Multiboot info structure. + */ + +/* ------------------------------------------------------------------------- + * Multiboot header + * ------------------------------------------------------------------------- */ +.set MULTIBOOT_MAGIC, 0x1BADB002 +.set MULTIBOOT_FLAGS, (1 << 0) | (1 << 1) +.set MULTIBOOT_CHECKSUM, -(MULTIBOOT_MAGIC + MULTIBOOT_FLAGS) + +.section .multiboot, "a" +.align 4 +.long MULTIBOOT_MAGIC +.long MULTIBOOT_FLAGS +.long MULTIBOOT_CHECKSUM + +/* ------------------------------------------------------------------------- + * Bootstrap stack — 16 KiB, 16-byte aligned (required by the i386 ABI). + * ------------------------------------------------------------------------- */ +.section .bss +.align 16 +stack_bottom: + .skip 16384 +stack_top: + +/* ------------------------------------------------------------------------- + * Kernel entry point + * ------------------------------------------------------------------------- */ +.section .text +.global _start +.type _start, @function +_start: + /* Set up the temporary kernel stack. */ + mov $stack_top, %esp + + /* Preserve the Multiboot magic before the BSS-clearing loop clobbers EAX. + * + * Registers on entry (Multiboot spec §3.2): + * eax — 0x2BADB002 (magic) + * ebx — multiboot_info_t* (physical address of info structure) + * + * 'rep stosb' only modifies EAX, EDI and ECX, so EBX survives intact. + * We park EAX in ESI (callee-saved, not used by the BSS loop). */ + mov %eax, %esi /* save Multiboot magic */ + + /* Zero the BSS segment. + * The C standard requires that all static storage is zero-initialised. + * The linker script exports _bss_start and _bss_end for this purpose. */ + mov $_bss_start, %edi + mov $_bss_end, %ecx + sub %edi, %ecx /* byte count */ + xor %eax, %eax + rep stosb + + /* Restore magic; EBX (info pointer) is still intact. */ + mov %esi, %eax + + /* + * Call kernel_main(uint32_t multiboot_magic, uint32_t multiboot_info). + * + * The Multiboot specification passes: + * eax — 0x2BADB002 (bootloader magic) + * ebx — physical address of the multiboot_info_t structure + * + * We push them in reverse order to match the C calling convention. + */ + push %ebx /* arg1: multiboot_info pointer */ + push %eax /* arg0: multiboot magic */ + call kernel_main + + /* kernel_main should never return; halt the machine if it does. */ +.L_halt: + cli + hlt + jmp .L_halt + +.size _start, . - _start diff --git a/src/debugport.c b/src/debugport.c new file mode 100644 index 0000000..65055b3 --- /dev/null +++ b/src/debugport.c @@ -0,0 +1,39 @@ +/** + * @file debugport.c + * @brief QEMU/Bochs debug port (I/O port 0xE9) implementation. + * + * Writing a byte to I/O port 0xE9 causes QEMU (when run with + * `-debugcon stdio`) to echo that byte verbatim to the host's stdout. + * This requires no peripheral initialisation and is available from the + * very first instruction after the bootloader hands control to the kernel. + */ + +#include +#include + +/** I/O port address of the QEMU/Bochs debug console. */ +#define DEBUG_PORT 0xE9U + +/** + * @brief Write a single character to the QEMU debug port. + * + * @param c Character to transmit. + */ +void debugport_putc(char c) +{ + outb((uint16_t)DEBUG_PORT, (uint8_t)c); +} + +/** + * @brief Write a NUL-terminated string to the QEMU debug port. + * + * Each character is transmitted individually via debugport_putc(). + * + * @param s Pointer to the NUL-terminated string. Must not be NULL. + */ +void debugport_puts(const char *s) +{ + while (*s) { + debugport_putc(*s++); + } +} diff --git a/src/include/debugport.h b/src/include/debugport.h new file mode 100644 index 0000000..ac67a1e --- /dev/null +++ b/src/include/debugport.h @@ -0,0 +1,34 @@ +/** + * @file debugport.h + * @brief QEMU/Bochs debug port (I/O port 0xE9) output interface. + * + * Port 0xE9 is a de-facto standard "debug port" supported by both Bochs and + * QEMU (when launched with `-debugcon stdio`). Any byte written to this port + * is immediately forwarded to the host's standard output, making it the + * simplest possible kernel logging mechanism — no interrupt handling, VGA, or + * serial initialisation is required. + * + * Usage in QEMU: + * qemu-system-i386 -debugcon stdio ... + */ + +#pragma once + +/** + * @brief Write a single character to the QEMU debug port. + * + * Issues a single OUT instruction to port 0xE9. + * + * @param c Character to transmit. + */ +void debugport_putc(char c); + +/** + * @brief Write a NUL-terminated string to the QEMU debug port. + * + * Iterates over every character in @p s and calls debugport_putc() for each + * one. The terminating NUL byte is not transmitted. + * + * @param s Pointer to the NUL-terminated string. Must not be NULL. + */ +void debugport_puts(const char *s); diff --git a/src/include/io.h b/src/include/io.h new file mode 100644 index 0000000..5b3f30a --- /dev/null +++ b/src/include/io.h @@ -0,0 +1,41 @@ +/** + * @file io.h + * @brief Low-level x86 port I/O helpers. + * + * Provides thin wrappers around the x86 IN/OUT instructions so that C code + * throughout the kernel can read from and write to I/O ports without + * resorting to inline assembly at every call site. + */ + +#pragma once + +#include + +/** + * @brief Write an 8-bit value to an I/O port. + * + * Issues an x86 OUT instruction. Execution halts until the peripheral + * acknowledges the write (the CPU inserts appropriate bus wait-states). + * + * @param port 16-bit I/O port address. + * @param value 8-bit value to send. + */ +static inline void outb(uint16_t port, uint8_t value) +{ + __asm__ volatile("outb %0, %1" : : "a"(value), "Nd"(port)); +} + +/** + * @brief Read an 8-bit value from an I/O port. + * + * Issues an x86 IN instruction. + * + * @param port 16-bit I/O port address. + * @return The 8-bit value returned by the peripheral. + */ +static inline uint8_t inb(uint16_t port) +{ + uint8_t value; + __asm__ volatile("inb %1, %0" : "=a"(value) : "Nd"(port)); + return value; +} diff --git a/src/kernel.c b/src/kernel.c new file mode 100644 index 0000000..c7925e4 --- /dev/null +++ b/src/kernel.c @@ -0,0 +1,45 @@ +/** + * @file kernel.c + * @brief Kernel entry point. + * + * Called by the Multiboot bootstrap (boot/boot.s) after the BSS has been + * zeroed and a temporary stack has been established. The bootloader passes + * the Multiboot magic value and a pointer to the Multiboot info structure. + */ + +#include +#include + +/** Magic value the bootloader stores in EAX before jumping to _start. */ +#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002U + +/** + * @brief Main kernel entry point. + * + * Validates the Multiboot magic so we know the bootloader handed us a proper + * info structure, then writes a greeting to the QEMU debug port so the host + * can confirm the kernel reached C code successfully. + * + * @param multiboot_magic 0x2BADB002 when booted by a Multiboot-compliant + * bootloader (e.g. GRUB). + * @param multiboot_info Physical address of the multiboot_info_t structure + * provided by the bootloader. + */ +void kernel_main(uint32_t multiboot_magic, uint32_t multiboot_info) +{ + (void)multiboot_info; + + /* Announce ourselves on the QEMU debug port. + * QEMU must be launched with -debugcon stdio for this to appear. */ + if (multiboot_magic == MULTIBOOT_BOOTLOADER_MAGIC) { + debugport_puts("Hello, World!\n"); + debugport_puts("ClaudeOS kernel booted successfully.\n"); + } else { + debugport_puts("ERROR: bad Multiboot magic — not booted by GRUB?\n"); + } + + /* Spin forever. Subsequent commits will add real work here. */ + for (;;) { + __asm__ volatile("hlt"); + } +} diff --git a/src/linker.ld b/src/linker.ld new file mode 100644 index 0000000..0713cd6 --- /dev/null +++ b/src/linker.ld @@ -0,0 +1,53 @@ +/* + * Linker script for the ClaudeOS kernel. + * + * The kernel is loaded by GRUB at physical address 0x00100000 (1 MiB). + * All kernel sections follow immediately after the multiboot header so that + * GRUB can locate it within the first 8 KiB of the ELF image as required by + * the Multiboot specification. + * + * Symbol _bss_start / _bss_end are exported so the bootstrap can zero the + * BSS segment before jumping to C code. _kernel_end marks the first byte + * after the kernel image, which the physical memory allocator will use as + * its starting address. + */ + +OUTPUT_FORMAT(elf32-i386) +OUTPUT_ARCH(i386) +ENTRY(_start) + +SECTIONS +{ + . = 0x00100000; + + /* The multiboot header must appear within the first 8 KiB. */ + .multiboot ALIGN(4) : + { + KEEP(*(.multiboot)) + } + + .text ALIGN(4096) : + { + *(.text*) + } + + .rodata ALIGN(4096) : + { + *(.rodata*) + } + + .data ALIGN(4096) : + { + *(.data*) + } + + .bss ALIGN(4096) : + { + _bss_start = .; + *(COMMON) + *(.bss*) + _bss_end = .; + } + + _kernel_end = .; +} -- 2.49.1