Files
claude-os/src/kernel.c
2026-02-24 07:43:45 +00:00

506 lines
16 KiB
C

#include <multiboot2.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "gdt.h"
#include "idt.h"
#include "pic.h"
#include "port_io.h"
#include "pmm.h"
#include "paging.h"
#include "kmalloc.h"
#include "driver.h"
#include "vga.h"
#include "tss.h"
#include "syscall.h"
#include "process.h"
#include "cpio.h"
#include "vfs.h"
#include "initrd_fs.h"
#include "devicefs.h"
#include "sysfs.h"
#include "mbr.h"
#include "fat32.h"
#include "keyboard.h"
#include "ethernet.h"
#include "ipv4.h"
#include "arp.h"
#include "dhcp.h"
#include "udp.h"
#include "tcp.h"
#include "framebuffer.h"
/* Global framebuffer info, parsed from multiboot2 tags. */
framebuffer_info_t fb_info;
/**
* Initialize COM1 serial port for debug output.
* Baud rate: 115200, 8N1.
*/
static void serial_init(void) {
outb(0x3F8 + 1, 0x00); /* Disable interrupts */
outb(0x3F8 + 3, 0x80); /* Enable DLAB */
outb(0x3F8 + 0, 0x01); /* Divisor low: 115200 baud */
outb(0x3F8 + 1, 0x00); /* Divisor high */
outb(0x3F8 + 3, 0x03); /* 8 bits, no parity, 1 stop */
outb(0x3F8 + 2, 0xC7); /* Enable FIFO */
outb(0x3F8 + 4, 0x03); /* RTS/DSR set */
}
static void serial_putc(char c) {
/* Wait for transmit buffer empty */
while (!(inb(0x3F8 + 5) & 0x20));
outb(0x3F8, c);
}
void offset_print(const char *str)
{
while (*str) {
outb(0xE9, *str); /* debugcon */
serial_putc(*str); /* COM1 serial */
str++;
}
}
void print_hex(uint32_t val)
{
const char *hex = "0123456789ABCDEF";
outb(0xE9, '0'); serial_putc('0');
outb(0xE9, 'x'); serial_putc('x');
for (int i = 28; i >= 0; i -= 4) {
char c = hex[(val >> i) & 0xF];
outb(0xE9, c);
serial_putc(c);
}
outb(0xE9, '\n'); serial_putc('\n');
}
/* ================================================================
* VFS sysfs namespace — handles mount/umount via /sys/vfs
* ================================================================
*
* /sys/vfs/mount — write "device mountpoint" to mount a FAT32 device
* /sys/vfs/mounts — read to list active mounts
*/
/** Last mount status message for reading back. */
static char vfs_sysfs_status[256] = "no mounts\n";
static int vfs_sysfs_list(void *ctx, const char *path, uint32_t idx,
sysfs_entry_t *out) {
(void)ctx;
if (path[0] != '\0') return -1; /* No subdirectories */
static const char *entries[] = { "mount", "mounts" };
if (idx >= 2) return -1;
memset(out, 0, sizeof(sysfs_entry_t));
strncpy(out->name, entries[idx], SYSFS_MAX_NAME - 1);
out->is_dir = 0;
return 0;
}
static int vfs_sysfs_read(void *ctx, const char *path, char *buf,
uint32_t buf_size) {
(void)ctx;
if (strcmp(path, "mount") == 0 || strcmp(path, "mounts") == 0) {
uint32_t len = strlen(vfs_sysfs_status);
if (len + 1 > buf_size) len = buf_size - 1;
memcpy(buf, vfs_sysfs_status, len);
buf[len] = '\0';
return (int)len;
}
return -1;
}
static int vfs_sysfs_write(void *ctx, const char *path, const char *buf,
uint32_t size) {
(void)ctx;
if (strcmp(path, "mount") != 0) return -1;
/* Parse: "device_name mount_path\n" */
char device[64];
char mount_path[128];
int di = 0, mi = 0;
uint32_t i = 0;
/* Parse device name */
while (i < size && buf[i] != ' ' && buf[i] != '\n' && di < 63) {
device[di++] = buf[i++];
}
device[di] = '\0';
/* Skip spaces */
while (i < size && buf[i] == ' ') i++;
/* Parse mount path */
while (i < size && buf[i] != ' ' && buf[i] != '\n' && mi < 127) {
mount_path[mi++] = buf[i++];
}
mount_path[mi] = '\0';
if (di == 0 || mi == 0) {
strncpy(vfs_sysfs_status, "error: usage: device mountpoint\n",
sizeof(vfs_sysfs_status) - 1);
return -1;
}
offset_print(" VFS mount request: ");
offset_print(device);
offset_print(" -> ");
offset_print(mount_path);
offset_print("\n");
/* Find the device */
devicefs_device_t *dev = devicefs_find(device);
if (!dev) {
strncpy(vfs_sysfs_status, "error: device not found\n",
sizeof(vfs_sysfs_status) - 1);
offset_print(" VFS mount: device not found\n");
return -1;
}
/* Mount as FAT32 */
int ret = fat32_mount_device(dev, mount_path);
if (ret != 0) {
strncpy(vfs_sysfs_status, "error: FAT32 mount failed\n",
sizeof(vfs_sysfs_status) - 1);
offset_print(" VFS mount: FAT32 mount failed\n");
return -1;
}
/* Build success message */
strncpy(vfs_sysfs_status, "mounted ", sizeof(vfs_sysfs_status) - 1);
uint32_t slen = strlen(vfs_sysfs_status);
strncpy(vfs_sysfs_status + slen, device,
sizeof(vfs_sysfs_status) - 1 - slen);
slen = strlen(vfs_sysfs_status);
strncpy(vfs_sysfs_status + slen, " at ",
sizeof(vfs_sysfs_status) - 1 - slen);
slen = strlen(vfs_sysfs_status);
strncpy(vfs_sysfs_status + slen, mount_path,
sizeof(vfs_sysfs_status) - 1 - slen);
slen = strlen(vfs_sysfs_status);
if (slen < sizeof(vfs_sysfs_status) - 1) {
vfs_sysfs_status[slen] = '\n';
vfs_sysfs_status[slen + 1] = '\0';
}
return (int)size;
}
static sysfs_ops_t vfs_sysfs_ops = {
.list = vfs_sysfs_list,
.read = vfs_sysfs_read,
.write = vfs_sysfs_write,
};
void kernel_main(uint32_t magic, uint32_t addr) {
/* Initialize serial port first so all debug output goes to COM1 too */
serial_init();
/* Early VGA: write directly to the text buffer at 0xB8000 for boot
* progress that is visible even without the VGA driver initialized.
* Attribute 0x1F = white on blue. */
volatile uint16_t *early_vga = (volatile uint16_t *)0xB8000;
int early_pos = 0;
/* Helper macro: write a short string to early VGA */
#define EARLY_PRINT(s) do { \
const char *_p = (s); \
while (*_p) { \
early_vga[early_pos++] = (uint16_t)(unsigned char)*_p | (0x1F << 8); \
_p++; \
} \
} while(0)
/* Clear screen to blue */
for (int i = 0; i < 80 * 25; i++)
early_vga[i] = (uint16_t)' ' | (0x1F << 8);
EARLY_PRINT("ClaudeOS early boot");
early_pos = 80; /* Move to line 2 */
if (magic != MULTIBOOT2_BOOTLOADER_MAGIC) {
EARLY_PRINT("ERROR: Bad magic=0x");
/* Print magic in hex */
const char *hex = "0123456789ABCDEF";
for (int i = 28; i >= 0; i -= 4)
early_vga[early_pos++] = (uint16_t)(unsigned char)hex[(magic >> i) & 0xF] | (0x4F << 8);
early_pos = 80 * 3;
EARLY_PRINT("Expected 0x36D76289 (Multiboot2)");
early_pos = 80 * 4;
EARLY_PRINT("Got MB1 magic? Checking grub.cfg...");
offset_print("Invalid magic number: ");
print_hex(magic);
/* Hang with interrupts disabled so user can see the message */
for (;;) __asm__ volatile("hlt");
}
EARLY_PRINT("Magic OK ");
offset_print("Booting...\n");
init_gdt();
EARLY_PRINT("GDT ");
offset_print("GDT initialized\n");
init_idt();
EARLY_PRINT("IDT ");
offset_print("IDT initialized\n");
init_pic();
/* Unmask timer IRQ (IRQ0) explicitly */
pic_clear_mask(0);
EARLY_PRINT("PIC ");
offset_print("PIC initialized\n");
init_pmm(addr);
EARLY_PRINT("PMM ");
offset_print("PMM initialized\n");
/* Scan Multiboot2 tags for the initrd module and framebuffer info */
uint32_t initrd_start = 0, initrd_end = 0;
memset(&fb_info, 0, sizeof(fb_info));
{
struct multiboot_tag *tag;
for (tag = (struct multiboot_tag *)(addr + 8);
tag->type != MULTIBOOT_TAG_TYPE_END;
tag = (struct multiboot_tag *)((uint8_t *)tag + ((tag->size + 7) & ~7u))) {
if (tag->type == MULTIBOOT_TAG_TYPE_MODULE) {
struct multiboot_tag_module *mod = (struct multiboot_tag_module *)tag;
initrd_start = mod->mod_start;
initrd_end = mod->mod_end;
offset_print("Initrd module at ");
print_hex(initrd_start);
offset_print(" to ");
print_hex(initrd_end);
}
if (tag->type == MULTIBOOT_TAG_TYPE_FRAMEBUFFER) {
struct multiboot_tag_framebuffer *fbt =
(struct multiboot_tag_framebuffer *)tag;
fb_info.addr = (uint32_t)fbt->common.framebuffer_addr;
fb_info.pitch = fbt->common.framebuffer_pitch;
fb_info.width = fbt->common.framebuffer_width;
fb_info.height = fbt->common.framebuffer_height;
fb_info.bpp = fbt->common.framebuffer_bpp;
fb_info.type = fbt->common.framebuffer_type;
if (fb_info.type == FB_TYPE_RGB) {
fb_info.red_pos = fbt->framebuffer_red_field_position;
fb_info.red_size = fbt->framebuffer_red_mask_size;
fb_info.green_pos = fbt->framebuffer_green_field_position;
fb_info.green_size = fbt->framebuffer_green_mask_size;
fb_info.blue_pos = fbt->framebuffer_blue_field_position;
fb_info.blue_size = fbt->framebuffer_blue_mask_size;
}
offset_print("Framebuffer: type=");
print_hex(fb_info.type);
offset_print(" addr=");
print_hex(fb_info.addr);
offset_print(" ");
print_hex(fb_info.width);
offset_print(" x ");
print_hex(fb_info.height);
offset_print(" bpp=");
print_hex(fb_info.bpp);
}
}
}
init_paging();
EARLY_PRINT("PAGING ");
offset_print("Paging initialized\n");
/* If GRUB provided a graphical framebuffer, identity-map it so
* the VGA driver can write pixels to it after paging is enabled. */
if (fb_info.addr != 0 && fb_info.type == FB_TYPE_RGB) {
uint32_t fb_size = fb_info.pitch * fb_info.height;
uint32_t fb_base = fb_info.addr & ~0xFFFu; /* page-align down */
uint32_t fb_end = (fb_info.addr + fb_size + 0xFFF) & ~0xFFFu;
offset_print(" Mapping framebuffer ");
print_hex(fb_base);
offset_print(" to ");
print_hex(fb_end);
for (uint32_t pa = fb_base; pa < fb_end; pa += 4096) {
paging_map_page(pa, pa, PAGE_PRESENT | PAGE_WRITE | PAGE_WRITETHROUGH);
}
}
/* Test paging: allocate a page and write to it */
void *test_page = paging_alloc_page();
if (test_page) {
offset_print("Allocated virtual page at: ");
print_hex((uint32_t)test_page);
*((volatile uint32_t *)test_page) = 0xDEADBEEF;
offset_print("Virtual page write/read OK\n");
paging_free_page(test_page);
} else {
offset_print("FAILED to allocate virtual page\n");
}
init_kmalloc();
EARLY_PRINT("HEAP ");
offset_print("Memory allocator initialized\n");
/* Initialize CPIO ramdisk if module was loaded */
if (initrd_start != 0) {
cpio_init((const void *)initrd_start, initrd_end - initrd_start);
EARLY_PRINT("CPIO ");
offset_print("CPIO ramdisk initialized\n");
} else {
offset_print("No initrd module found\n");
}
init_vfs();
EARLY_PRINT("VFS ");
offset_print("VFS initialized\n");
if (initrd_start != 0) {
init_initrd_fs();
EARLY_PRINT("INITRD ");
offset_print("Initrd filesystem mounted\n");
/* Test VFS: read a file from the initrd */
int fd = vfs_open("/initrd/README", 0);
if (fd >= 0) {
char buf[64];
int32_t n = vfs_read(fd, buf, sizeof(buf) - 1);
if (n > 0) {
buf[n] = '\0';
offset_print("VFS read /initrd/README: ");
offset_print(buf);
}
vfs_close(fd);
}
}
init_devicefs();
EARLY_PRINT("DEV ");
offset_print("Devicefs initialized\n");
init_sysfs();
EARLY_PRINT("SYS ");
offset_print("Sysfs initialized\n");
/* Register VFS mount namespace in sysfs */
sysfs_register("vfs", &vfs_sysfs_ops, NULL);
offset_print("VFS sysfs namespace registered\n");
init_tss();
EARLY_PRINT("TSS ");
offset_print("TSS initialized\n");
init_syscalls();
EARLY_PRINT("SYSCALL ");
offset_print("Syscalls initialized\n");
keyboard_init();
EARLY_PRINT("KBD ");
offset_print("Keyboard initialized\n");
init_process();
EARLY_PRINT("PROC ");
offset_print("Process subsystem initialized\n");
/* If the early VGA canary at 0xB8000 was visible, the display is
* definitely in text mode, regardless of what the GRUB framebuffer
* tag says. Force text mode so vga_init doesn't try to use a
* pixel framebuffer that isn't actually being displayed. */
if (fb_info.type == FB_TYPE_RGB) {
offset_print(" Overriding fb type from RGB to EGA_TEXT (early VGA visible)\n");
fb_info.type = FB_TYPE_EGA_TEXT;
fb_info.addr = 0x000B8000;
fb_info.width = 80;
fb_info.height = 25;
fb_info.bpp = 16;
fb_info.pitch = 80 * 2;
}
ethernet_init();
offset_print("Ethernet subsystem initialized\n");
ipv4_init();
offset_print("IPv4 stack initialized\n");
arp_init();
offset_print("ARP subsystem initialized\n");
dhcp_init();
offset_print("DHCP subsystem initialized\n");
udp_init();
offset_print("UDP stack initialized\n");
tcp_init();
offset_print("TCP stack initialized\n");
init_drivers();
EARLY_PRINT("DRV ");
offset_print("Drivers initialized\n");
/* Scan for MBR partitions on detected hard drives */
init_mbr();
offset_print("MBR scan complete\n");
/* At this point the VGA driver has been initialized and taken over
* the display. The early VGA text is no longer visible. */
/* Show memory statistics and boot progress on VGA */
vga_show_mem_stats();
vga_puts("Boot complete.\n\n");
/* Test kmalloc/kfree */
uint32_t *test_alloc = (uint32_t *)kmalloc(64);
if (test_alloc) {
*test_alloc = 42;
offset_print("kmalloc(64) = ");
print_hex((uint32_t)test_alloc);
kfree(test_alloc);
offset_print("kfree OK\n");
} else {
offset_print("FAILED to kmalloc\n");
}
/* Load the initial program from the initrd and run it */
cpio_entry_t app_entry;
const char *init_app = "sh";
if (cpio_find(init_app, &app_entry) == 0) {
offset_print("Found ");
offset_print(init_app);
offset_print(" in initrd (");
print_hex(app_entry.datasize);
offset_print(" bytes)\n");
int32_t pid = process_create(init_app,
app_entry.data,
app_entry.datasize);
if (pid > 0) {
offset_print("Created init process, pid=");
print_hex((uint32_t)pid);
/* Enable interrupts before entering user mode */
asm volatile("sti");
offset_print("Interrupts enabled\n");
/* Enter user mode - does not return */
process_run_first();
} else {
offset_print("FAILED to create init process\n");
}
} else {
offset_print(init_app);
offset_print(" not found in initrd\n");
}
/* Enable interrupts */
asm volatile("sti");
offset_print("Interrupts enabled\n");
offset_print("Hello, world\n");
}