#include #include #include #include #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"); }