8 Commits

Author SHA1 Message Date
cd0e1cafae There was an attempt... (AI) 2026-02-23 08:30:43 +01:00
f42d65267b Force devcontainer to use amd64 (human) 2026-02-23 08:30:31 +01:00
6d9ab26423 Update readme (human) 2026-02-23 08:15:11 +01:00
e8fa7e1f28 Update devcontainer (AI+human) 2026-02-23 08:13:18 +01:00
1cf39a70cf Update instructions (human) 2026-02-23 08:12:51 +01:00
bbd7d3725b 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.
2026-02-23 07:13:49 +01:00
f3ef92be4f 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.
2026-02-23 07:09:04 +01:00
34e24c9979 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.
2026-02-23 07:08:34 +01:00
27 changed files with 394 additions and 831 deletions

8
.gitignore vendored
View File

@@ -1,9 +1,3 @@
build/
release/
*.img
*.iso
debug_grub/
*_output.txt
snippet.*
qemu.log
iso_output.txt
*.img

View File

@@ -1,40 +1,9 @@
cmake_minimum_required(VERSION 3.16)
cmake_minimum_required(VERSION 3.20)
project(ClaudeOS C ASM)
set(CMAKE_C_STANDARD 99)
# We are building a kernel, so we don't want standard libraries
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffreestanding -m32 -fno-pie -fno-pic -fno-builtin -fno-stack-protector -mno-sse -mno-mmx -g -O2 -Wall -Wextra")
set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -m32 -fno-pie -fno-pic")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -m32 -nostdlib -no-pie")
# Define build output directory
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
# Kernel
add_subdirectory(src)
# Create output directories
file(MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/release)
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/isodir/boot/grub)
# Create grub.cfg for ISO
file(WRITE ${CMAKE_BINARY_DIR}/isodir/boot/grub/grub.cfg "set timeout=0\nset default=0\nsearch --set=root --file /boot/kernel.bin\nmenuentry \"ClaudeOS\" { multiboot /boot/kernel.bin }")
# ISO Generation
add_custom_target(iso ALL
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/kernel ${CMAKE_BINARY_DIR}/isodir/boot/kernel.bin
COMMAND grub-mkrescue -o ${CMAKE_SOURCE_DIR}/release/claude-os.iso ${CMAKE_BINARY_DIR}/isodir
DEPENDS kernel
COMMENT "Generating bootable ISO image"
)
# Test target
add_custom_target(test_images
COMMAND sh ${CMAKE_SOURCE_DIR}/test_images.sh
DEPENDS iso
COMMENT "Testing generated images in QEMU"
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)

7
PROMPT.md Normal file
View File

@@ -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.

View File

@@ -37,13 +37,11 @@ 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.
- [x] Create directory structure
- [x] Create initial build system
- [x] Setup a simple kernel that writes `Hello, world` to Qemu debug port
- [x] Update the build system to create both ISO and Floppy images. Verify these work using a test script. The standard CMake build target should automatically generate both images. (Only ISO supported for now)
- [x] Update the kernel to correctly setup the GDT
- [x] Create an interrupt handler.
- [ ] Implement a PIC handler
- [ ] Create directory structure
- [ ] Create initial build system
- [ ] Setup a simple kernel that writes `Hello, world` to Qemu debug port
- [ ] Update the build system to create both ISO and Floppy images. Verify these work using a test script.
- [ ] Update the kernel to correctly setup the GDT
- [ ] Create a physical memory allocator and mapper. The kernel should live in the upper last gigabyte of virtual memory. It should support different zones (e.g.: `SUB_16M`, `DEFAULT`, ...) These zones describe the region of memory that memory should be allocated in. If it is not possible to allocate in that region (because it is full, or has 0 capacity to begin with), it should fallback to another zone.
- [ ] Create a paging subsystem. It should allow drivers to allocate and deallocate pages at will.
- [ ] Create a memory allocator. This should provide the kernel with `malloc` and `free`. Internally, it should use the paging subsystem to ensure that the address it returns have actual RAM paged to them.

0
apps/.gitkeep Normal file
View File

View File

@@ -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:
# <TARGET> — output binary path
# <OBJECTS> — all compiled object files
# <LINK_FLAGS> — extra flags from target_link_options / CMAKE_EXE_LINKER_FLAGS
# <LINK_LIBRARIES> — libraries to link (empty for a freestanding kernel)
set(CMAKE_LINKER "ld.lld")
set(CMAKE_C_LINK_EXECUTABLE
"ld.lld -m elf_i386 <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")
set(CMAKE_ASM_LINK_EXECUTABLE
"ld.lld -m elf_i386 <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")
# 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)

0
docs/.gitkeep Normal file
View File

0
libs/.gitkeep Normal file
View File

0
src/.gitkeep Normal file
View File

View File

@@ -1,18 +1,33 @@
cmake_minimum_required(VERSION 3.16)
add_executable(kernel
boot.S
gdt_flush.S
gdt.c
idt.c
interrupts.S
set(KERNEL_SOURCES
boot/boot.s
kernel.c
debugport.c
)
# Use our custom linker script
target_link_options(kernel PRIVATE -T ${CMAKE_CURRENT_SOURCE_DIR}/linker.ld)
add_executable(kernel ${KERNEL_SOURCES})
target_include_directories(kernel PRIVATE
${CMAKE_SOURCE_DIR}/vendor
${CMAKE_SOURCE_DIR}/include # If created later
${CMAKE_CURRENT_SOURCE_DIR}/include
)
target_compile_options(kernel PRIVATE
# C-only flags — not valid for the assembler front-end.
$<$<COMPILE_LANGUAGE:C>:-Werror>
$<$<COMPILE_LANGUAGE:C>:-ffreestanding>
$<$<COMPILE_LANGUAGE:C>:-fno-stack-protector>
$<$<COMPILE_LANGUAGE:C>:-fno-exceptions>
# Suppress the harmless "argument unused" warnings that clang emits
# when CMake passes -MD/-MT/-MF to the assembler.
$<$<COMPILE_LANGUAGE:ASM>:-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
)

View File

@@ -1,65 +0,0 @@
#define ASM_FILE
#include <multiboot2.h>
/* Multiboot 1 header constants */
.set ALIGN, 1<<0 /* align loaded modules on page boundaries */
.set MEMINFO, 1<<1 /* provide memory map */
.set FLAGS, ALIGN | MEMINFO /* this is the Multiboot 'flag' field */
.set MAGIC, 0x1BADB002 /* 'magic number' lets bootloader find the header */
.set CHECKSUM, -(MAGIC + FLAGS) /* checksum of above, to prove we are multiboot */
.section .multiboot
.align 4
multiboot1_header:
.long MAGIC
.long FLAGS
.long CHECKSUM
.align 8
multiboot_header:
/* magic */
.long MULTIBOOT2_HEADER_MAGIC
/* architecture: 0 (protected mode i386) */
.long MULTIBOOT_ARCHITECTURE_I386
/* header length */
.long multiboot_header_end - multiboot_header
/* checksum */
.long -(MULTIBOOT2_HEADER_MAGIC + MULTIBOOT_ARCHITECTURE_I386 + (multiboot_header_end - multiboot_header))
/* Tags here */
/* End tag */
.short MULTIBOOT_HEADER_TAG_END
.short 0
.long 8
multiboot_header_end:
.section .bss
.align 16
stack_bottom:
.skip 16384 # 16 KiB
stack_top:
.section .text
.global _start
.type _start, @function
_start:
/* Set up stack */
mov $stack_top, %esp
/* Reset EFLAGS */
push $0
popf
/* Push magic and multiboot info pointer onto stack for kernel_main */
/* Multiboot2 puts magic in EAX, pointer in EBX */
push %ebx
push %eax
call kernel_main
cli
1: hlt
jmp 1b
.size _start, . - _start

92
src/boot/boot.s Normal file
View File

@@ -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

39
src/debugport.c Normal file
View File

@@ -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 <debugport.h>
#include <io.h>
/** 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++);
}
}

View File

@@ -1,68 +0,0 @@
#include "gdt.h"
#include <stdint.h>
/* GDT Pointer Structure */
struct gdt_ptr gp;
/* GDT entries */
struct gdt_entry gdt[5];
extern void gdt_flush(uint32_t);
/* Setup a descriptor in the Global Descriptor Table */
void gdt_set_gate(int32_t num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran)
{
/* Setup the descriptor base address */
gdt[num].base_low = (base & 0xFFFF);
gdt[num].base_middle = (base >> 16) & 0xFF;
gdt[num].base_high = (base >> 24) & 0xFF;
/* Setup the descriptor limits */
gdt[num].limit_low = (limit & 0xFFFF);
gdt[num].granularity = ((limit >> 16) & 0x0F);
/* Finally, set up the granularity and access flags */
gdt[num].granularity |= (gran & 0xF0);
gdt[num].access = access;
}
/* Should be called by main. This will setup the special GDT
* pointer, set up the first 3 entries in our GDT, and then
* call gdt_flush() in our assembler file in order to tell
* the processor where the new GDT is and update the
* internal registers */
void init_gdt()
{
/* Setup the GDT pointer and limit */
gp.limit = (sizeof(struct gdt_entry) * 5) - 1;
gp.base = (uint32_t)&gdt;
/* Our NULL descriptor */
gdt_set_gate(0, 0, 0, 0, 0);
/* The second entry is our Code Segment. The base address
* is 0, the limit is 4GBytes, it uses 4KByte granularity,
* uses 32-bit opcodes, and is a Code Segment descriptor.
* Please check the table above in the tutorial in order
* to see exactly what each value means */
/* 0x9A = 1001 1010
P=1, DPL=00, S=1 (Code/Data), Type=1010 (Code, Exec/Read) */
/* 0xCF = 1100 1111
G=1 (4k), DB=1 (32bit), L=0, AVL=0, LimitHigh=0xF */
gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF);
/* The third entry is our Data Segment. It's EXACTLY the
* same as our code segment, but the descriptor type in
* this entry's access byte says it's a Data Segment */
/* 0x92 = 1001 0010
P=1, DPL=00, S=1, Type=0010 (Data, Read/Write) */
gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF);
/* User mode code segment */
gdt_set_gate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF);
/* User mode data segment */
gdt_set_gate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF);
/* Flush out the old GDT and install the new changes! */
gdt_flush((uint32_t)&gp);
}

View File

@@ -1,25 +0,0 @@
#ifndef GDT_H
#define GDT_H
#include <stdint.h>
/* GDT Entry Structure */
struct gdt_entry {
uint16_t limit_low;
uint16_t base_low;
uint8_t base_middle;
uint8_t access;
uint8_t granularity;
uint8_t base_high;
} __attribute__((packed));
/* GDT Pointer Structure */
struct gdt_ptr {
uint16_t limit;
uint32_t base;
} __attribute__((packed, aligned(1)));
/* Initialize GDT */
void init_gdt(void);
#endif // GDT_H

View File

@@ -1,18 +0,0 @@
.section .text
.global gdt_flush
.type gdt_flush, @function
gdt_flush:
mov 4(%esp), %eax
lgdt (%eax)
mov $0x10, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
mov %ax, %ss
ljmp $0x08, $flush
flush:
ret

View File

@@ -1,46 +0,0 @@
#include "idt.h"
#include <stdint.h>
#include <stddef.h> /* for memset */
idt_entry_t idt_entries[256];
idt_ptr_t idt_ptr;
extern void idt_load();
static void idt_set_gate(uint8_t num, uint32_t base, uint16_t sel, uint8_t flags)
{
idt_entries[num].base_lo = base & 0xFFFF;
idt_entries[num].base_hi = (base >> 16) & 0xFFFF;
idt_entries[num].sel = sel;
idt_entries[num].always0 = 0;
// We must uncomment the OR below when we get to using user-mode.
// It sets the interrupt gate's privilege level to 3.
idt_entries[num].flags = flags /* | 0x60 */;
}
/* Note: memset implementation because we don't link with libc */
static void *memset(void *s, int c, size_t n)
{
unsigned char *p = s;
while(n--)
*p++ = (unsigned char)c;
return s;
}
void init_idt()
{
idt_ptr.limit = sizeof(idt_entry_t) * 256 - 1;
idt_ptr.base = (uint32_t)&idt_entries;
memset(&idt_entries, 0, sizeof(idt_entry_t) * 256);
/* Determine valid flags:
* 0x8E = 1000 1110
* P=1, DPL=00, S=0 (System), Type=1110 (32-bit Interrupt Gate) */
/* For now, just load the IDT without any entries to verify loading works without crashing.
Later we will set up ISRs. */
idt_load((uint32_t)&idt_ptr);
}

View File

@@ -1,33 +0,0 @@
#ifndef IDT_H
#define IDT_H
#include <stdint.h>
/* A struct describing an interrupt gate. */
struct idt_entry_struct {
uint16_t base_lo; // The lower 16 bits of the address to jump to when this interrupt fires.
uint16_t sel; // Kernel segment selector.
uint8_t always0; // This must always be zero.
uint8_t flags; // More flags. See documentation.
uint16_t base_hi; // The upper 16 bits of the address to jump to.
} __attribute__((packed));
typedef struct idt_entry_struct idt_entry_t;
/* A struct describing a pointer to an array of interrupt handlers.
* This is in a format suitable for giving to 'lidt'. */
struct idt_ptr_struct {
uint16_t limit;
uint32_t base; // The address of the first element in our idt_entry_t array.
} __attribute__((packed));
typedef struct idt_ptr_struct idt_ptr_t;
// These extern directives let us access the addresses of our ASM ISR handlers.
extern void isr0();
extern void isr1();
// ... we will add more later ...
void init_idt();
#endif

34
src/include/debugport.h Normal file
View File

@@ -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);

41
src/include/io.h Normal file
View File

@@ -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 <stdint.h>
/**
* @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;
}

View File

@@ -1,7 +0,0 @@
.section .text
.global idt_load
.type idt_load, @function
idt_load:
mov 4(%esp), %eax /* Turn the argument into the EAX register */
lidt (%eax) /* Load the IDT pointer */
ret

0
src/it/.gitkeep Normal file
View File

View File

@@ -1,51 +1,45 @@
#include <multiboot2.h>
/**
* @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 <stdint.h>
#include <stddef.h>
#include "gdt.h"
#include "idt.h"
#include <debugport.h>
static inline void outb(uint16_t port, uint8_t val)
/** 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)
{
asm volatile ( "outb %b0, %w1" : : "a"(val), "Nd"(port) );
}
(void)multiboot_info;
void offset_print(const char *str)
{
while (*str) {
outb(0xE9, *str);
str++;
}
}
#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002
void print_hex(uint32_t val)
{
const char *hex = "0123456789ABCDEF";
outb(0xE9, '0');
outb(0xE9, 'x');
for (int i = 28; i >= 0; i -= 4) {
outb(0xE9, hex[(val >> i) & 0xF]);
}
outb(0xE9, '\n');
}
void kernel_main(uint32_t magic, uint32_t addr) {
(void)addr; // Unused for now
if (magic != MULTIBOOT2_BOOTLOADER_MAGIC && magic != MULTIBOOT_BOOTLOADER_MAGIC) {
offset_print("Invalid magic number: ");
print_hex(magic);
return;
/* 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");
}
offset_print("Booting...\n");
init_gdt();
offset_print("GDT initialized\n");
init_idt();
offset_print("IDT initialized\n");
offset_print("Hello, world\n");
/* Spin forever. Subsequent commits will add real work here. */
for (;;) {
__asm__ volatile("hlt");
}
}

View File

@@ -1,28 +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
{
. = 1M;
. = 0x00100000;
.text BLOCK(4K) : ALIGN(4K)
{
KEEP(*(.multiboot))
*(.text)
}
/* The multiboot header must appear within the first 8 KiB. */
.multiboot ALIGN(4) :
{
KEEP(*(.multiboot))
}
.rodata BLOCK(4K) : ALIGN(4K)
{
*(.rodata)
}
.text ALIGN(4096) :
{
*(.text*)
}
.data BLOCK(4K) : ALIGN(4K)
{
*(.data)
}
.rodata ALIGN(4096) :
{
*(.rodata*)
}
.bss BLOCK(4K) : ALIGN(4K)
{
*(COMMON)
*(.bss)
}
.data ALIGN(4096) :
{
*(.data*)
}
.bss ALIGN(4096) :
{
_bss_start = .;
*(COMMON)
*(.bss*)
_bss_end = .;
}
_kernel_end = .;
}

View File

@@ -1,25 +0,0 @@
#!/bin/sh
set -e
# Build directory
BUILD_DIR=build
BIN_DIR=$BUILD_DIR/bin
RELEASE_DIR=release
# Check if images exist
if [ ! -f "$RELEASE_DIR/claude-os.iso" ]; then
echo "Error: claude-os.iso not found!"
exit 1
fi
echo "Testing ISO image..."
timeout 5s qemu-system-i386 -cdrom "$RELEASE_DIR/claude-os.iso" -debugcon file:iso_output.txt -display none -no-reboot || true
if grep -q "Hello, world" iso_output.txt; then
echo "ISO Test Passed!"
else
echo "ISO Test Failed!"
cat iso_output.txt
exit 1
fi
echo "All tests passed!"

0
vendor/.gitkeep vendored Normal file
View File

417
vendor/multiboot2.h vendored
View File

@@ -1,417 +0,0 @@
/* multiboot2.h - Multiboot 2 header file. */
/* Copyright (C) 1999,2003,2007,2008,2009,2010 Free Software Foundation, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY
* DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
* IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef MULTIBOOT_HEADER
#define MULTIBOOT_HEADER 1
/* How many bytes from the start of the file we search for the header. */
#define MULTIBOOT_SEARCH 32768
#define MULTIBOOT_HEADER_ALIGN 8
/* The magic field should contain this. */
#define MULTIBOOT2_HEADER_MAGIC 0xe85250d6
/* This should be in %eax. */
#define MULTIBOOT2_BOOTLOADER_MAGIC 0x36d76289
/* Alignment of multiboot modules. */
#define MULTIBOOT_MOD_ALIGN 0x00001000
/* Alignment of the multiboot info structure. */
#define MULTIBOOT_INFO_ALIGN 0x00000008
/* Flags set in the 'flags' member of the multiboot header. */
#define MULTIBOOT_TAG_ALIGN 8
#define MULTIBOOT_TAG_TYPE_END 0
#define MULTIBOOT_TAG_TYPE_CMDLINE 1
#define MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME 2
#define MULTIBOOT_TAG_TYPE_MODULE 3
#define MULTIBOOT_TAG_TYPE_BASIC_MEMINFO 4
#define MULTIBOOT_TAG_TYPE_BOOTDEV 5
#define MULTIBOOT_TAG_TYPE_MMAP 6
#define MULTIBOOT_TAG_TYPE_VBE 7
#define MULTIBOOT_TAG_TYPE_FRAMEBUFFER 8
#define MULTIBOOT_TAG_TYPE_ELF_SECTIONS 9
#define MULTIBOOT_TAG_TYPE_APM 10
#define MULTIBOOT_TAG_TYPE_EFI32 11
#define MULTIBOOT_TAG_TYPE_EFI64 12
#define MULTIBOOT_TAG_TYPE_SMBIOS 13
#define MULTIBOOT_TAG_TYPE_ACPI_OLD 14
#define MULTIBOOT_TAG_TYPE_ACPI_NEW 15
#define MULTIBOOT_TAG_TYPE_NETWORK 16
#define MULTIBOOT_TAG_TYPE_EFI_MMAP 17
#define MULTIBOOT_TAG_TYPE_EFI_BS 18
#define MULTIBOOT_TAG_TYPE_EFI32_IH 19
#define MULTIBOOT_TAG_TYPE_EFI64_IH 20
#define MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR 21
#define MULTIBOOT_HEADER_TAG_END 0
#define MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST 1
#define MULTIBOOT_HEADER_TAG_ADDRESS 2
#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS 3
#define MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS 4
#define MULTIBOOT_HEADER_TAG_FRAMEBUFFER 5
#define MULTIBOOT_HEADER_TAG_MODULE_ALIGN 6
#define MULTIBOOT_HEADER_TAG_EFI_BS 7
#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI32 8
#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64 9
#define MULTIBOOT_HEADER_TAG_RELOCATABLE 10
#define MULTIBOOT_ARCHITECTURE_I386 0
#define MULTIBOOT_ARCHITECTURE_MIPS32 4
#define MULTIBOOT_HEADER_TAG_OPTIONAL 1
#define MULTIBOOT_LOAD_PREFERENCE_NONE 0
#define MULTIBOOT_LOAD_PREFERENCE_LOW 1
#define MULTIBOOT_LOAD_PREFERENCE_HIGH 2
#define MULTIBOOT_CONSOLE_FLAGS_CONSOLE_REQUIRED 1
#define MULTIBOOT_CONSOLE_FLAGS_EGA_TEXT_SUPPORTED 2
#ifndef ASM_FILE
typedef unsigned char multiboot_uint8_t;
typedef unsigned short multiboot_uint16_t;
typedef unsigned int multiboot_uint32_t;
typedef unsigned long long multiboot_uint64_t;
struct multiboot_header
{
/* Must be MULTIBOOT_MAGIC - see above. */
multiboot_uint32_t magic;
/* ISA */
multiboot_uint32_t architecture;
/* Total header length. */
multiboot_uint32_t header_length;
/* The above fields plus this one must equal 0 mod 2^32. */
multiboot_uint32_t checksum;
};
struct multiboot_header_tag
{
multiboot_uint16_t type;
multiboot_uint16_t flags;
multiboot_uint32_t size;
};
struct multiboot_header_tag_information_request
{
multiboot_uint16_t type;
multiboot_uint16_t flags;
multiboot_uint32_t size;
multiboot_uint32_t requests[0];
};
struct multiboot_header_tag_address
{
multiboot_uint16_t type;
multiboot_uint16_t flags;
multiboot_uint32_t size;
multiboot_uint32_t header_addr;
multiboot_uint32_t load_addr;
multiboot_uint32_t load_end_addr;
multiboot_uint32_t bss_end_addr;
};
struct multiboot_header_tag_entry_address
{
multiboot_uint16_t type;
multiboot_uint16_t flags;
multiboot_uint32_t size;
multiboot_uint32_t entry_addr;
};
struct multiboot_header_tag_console_flags
{
multiboot_uint16_t type;
multiboot_uint16_t flags;
multiboot_uint32_t size;
multiboot_uint32_t console_flags;
};
struct multiboot_header_tag_framebuffer
{
multiboot_uint16_t type;
multiboot_uint16_t flags;
multiboot_uint32_t size;
multiboot_uint32_t width;
multiboot_uint32_t height;
multiboot_uint32_t depth;
};
struct multiboot_header_tag_module_align
{
multiboot_uint16_t type;
multiboot_uint16_t flags;
multiboot_uint32_t size;
};
struct multiboot_header_tag_relocatable
{
multiboot_uint16_t type;
multiboot_uint16_t flags;
multiboot_uint32_t size;
multiboot_uint32_t min_addr;
multiboot_uint32_t max_addr;
multiboot_uint32_t align;
multiboot_uint32_t preference;
};
struct multiboot_color
{
multiboot_uint8_t red;
multiboot_uint8_t green;
multiboot_uint8_t blue;
};
struct multiboot_mmap_entry
{
multiboot_uint64_t addr;
multiboot_uint64_t len;
#define MULTIBOOT_MEMORY_AVAILABLE 1
#define MULTIBOOT_MEMORY_RESERVED 2
#define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3
#define MULTIBOOT_MEMORY_NVS 4
#define MULTIBOOT_MEMORY_BADRAM 5
multiboot_uint32_t type;
multiboot_uint32_t zero;
};
typedef struct multiboot_mmap_entry multiboot_memory_map_t;
struct multiboot_tag
{
multiboot_uint32_t type;
multiboot_uint32_t size;
};
struct multiboot_tag_string
{
multiboot_uint32_t type;
multiboot_uint32_t size;
char string[0];
};
struct multiboot_tag_module
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint32_t mod_start;
multiboot_uint32_t mod_end;
char cmdline[0];
};
struct multiboot_tag_basic_meminfo
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint32_t mem_lower;
multiboot_uint32_t mem_upper;
};
struct multiboot_tag_bootdev
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint32_t biosdev;
multiboot_uint32_t slice;
multiboot_uint32_t part;
};
struct multiboot_tag_mmap
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint32_t entry_size;
multiboot_uint32_t entry_version;
struct multiboot_mmap_entry entries[0];
};
struct multiboot_vbe_info_block
{
multiboot_uint8_t external_specification[512];
};
struct multiboot_vbe_mode_info_block
{
multiboot_uint8_t external_specification[256];
};
struct multiboot_tag_vbe
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint16_t vbe_mode;
multiboot_uint16_t vbe_interface_seg;
multiboot_uint16_t vbe_interface_off;
multiboot_uint16_t vbe_interface_len;
struct multiboot_vbe_info_block vbe_control_info;
struct multiboot_vbe_mode_info_block vbe_mode_info;
};
struct multiboot_tag_framebuffer_common
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint64_t framebuffer_addr;
multiboot_uint32_t framebuffer_pitch;
multiboot_uint32_t framebuffer_width;
multiboot_uint32_t framebuffer_height;
multiboot_uint8_t framebuffer_bpp;
#define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0
#define MULTIBOOT_FRAMEBUFFER_TYPE_RGB 1
#define MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT 2
multiboot_uint8_t framebuffer_type;
multiboot_uint16_t reserved;
};
struct multiboot_tag_framebuffer
{
struct multiboot_tag_framebuffer_common common;
union
{
struct
{
multiboot_uint16_t framebuffer_palette_num_colors;
struct multiboot_color framebuffer_palette[0];
};
struct
{
multiboot_uint8_t framebuffer_red_field_position;
multiboot_uint8_t framebuffer_red_mask_size;
multiboot_uint8_t framebuffer_green_field_position;
multiboot_uint8_t framebuffer_green_mask_size;
multiboot_uint8_t framebuffer_blue_field_position;
multiboot_uint8_t framebuffer_blue_mask_size;
};
};
};
struct multiboot_tag_elf_sections
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint32_t num;
multiboot_uint32_t entsize;
multiboot_uint32_t shndx;
char sections[0];
};
struct multiboot_tag_apm
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint16_t version;
multiboot_uint16_t cseg;
multiboot_uint32_t offset;
multiboot_uint16_t cseg_16;
multiboot_uint16_t dseg;
multiboot_uint16_t flags;
multiboot_uint16_t cseg_len;
multiboot_uint16_t cseg_16_len;
multiboot_uint16_t dseg_len;
};
struct multiboot_tag_efi32
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint32_t pointer;
};
struct multiboot_tag_efi64
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint64_t pointer;
};
struct multiboot_tag_smbios
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint8_t major;
multiboot_uint8_t minor;
multiboot_uint8_t reserved[6];
multiboot_uint8_t tables[0];
};
struct multiboot_tag_old_acpi
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint8_t rsdp[0];
};
struct multiboot_tag_new_acpi
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint8_t rsdp[0];
};
struct multiboot_tag_network
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint8_t dhcpack[0];
};
struct multiboot_tag_efi_mmap
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint32_t descr_size;
multiboot_uint32_t descr_vers;
multiboot_uint8_t efi_mmap[0];
};
struct multiboot_tag_efi32_ih
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint32_t pointer;
};
struct multiboot_tag_efi64_ih
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint64_t pointer;
};
struct multiboot_tag_load_base_addr
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint32_t load_base_addr;
};
#endif /* ! ASM_FILE */
#endif /* ! MULTIBOOT_HEADER */