diff --git a/src/devicefs.h b/src/devicefs.h new file mode 100644 index 0000000..adc1062 --- /dev/null +++ b/src/devicefs.h @@ -0,0 +1,135 @@ +/** + * @file devicefs.h + * @brief Device filesystem (devicefs) subsystem. + * + * Provides a VFS interface at /dev for exposing block and character devices. + * Drivers register devices through the devicefs API, and each device is + * assigned a sequential number by device class (e.g., hdd1, hdd2, cd1). + * + * The devicefs owns device naming — drivers specify a class name (e.g., "hdd") + * and the devicefs appends a sequential number. + */ + +#ifndef DEVICEFS_H +#define DEVICEFS_H + +#include +#include + +/** Maximum number of registered devices. */ +#define DEVICEFS_MAX_DEVICES 32 + +/** Maximum length of a device class name (e.g., "hdd", "cd", "floppy"). */ +#define DEVICEFS_MAX_CLASS_NAME 16 + +/** Maximum length of a full device name (class + number, e.g., "hdd1"). */ +#define DEVICEFS_MAX_DEV_NAME 32 + +/** Device types. */ +#define DEVICEFS_BLOCK 0x01 /**< Block device (e.g., hard drives, CDs). */ +#define DEVICEFS_CHAR 0x02 /**< Character device (e.g., serial ports). */ + +/** + * Block device operations. + * + * Block devices transfer data in fixed-size sectors. + */ +typedef struct devicefs_block_ops { + /** Read `count` sectors starting at `lba` into `buf`. Returns 0 on success. */ + int (*read_sectors)(void *dev_data, uint32_t lba, uint32_t count, void *buf); + + /** Write `count` sectors from `buf` starting at `lba`. Returns 0 on success. */ + int (*write_sectors)(void *dev_data, uint32_t lba, uint32_t count, const void *buf); + + /** Get the sector size in bytes. */ + uint32_t (*sector_size)(void *dev_data); + + /** Get total number of sectors. */ + uint32_t (*sector_count)(void *dev_data); +} devicefs_block_ops_t; + +/** + * Character device operations. + * + * Character devices transfer data as byte streams. + */ +typedef struct devicefs_char_ops { + /** Read up to `size` bytes into `buf`. Returns bytes read, or -1. */ + int32_t (*read)(void *dev_data, uint32_t size, void *buf); + + /** Write `size` bytes from `buf`. Returns bytes written, or -1. */ + int32_t (*write)(void *dev_data, uint32_t size, const void *buf); +} devicefs_char_ops_t; + +/** + * Registered device entry. + */ +typedef struct devicefs_device { + char name[DEVICEFS_MAX_DEV_NAME]; /**< Full device name (e.g., "hdd1"). */ + char class_name[DEVICEFS_MAX_CLASS_NAME]; /**< Device class (e.g., "hdd"). */ + uint8_t type; /**< DEVICEFS_BLOCK or DEVICEFS_CHAR. */ + uint32_t number; /**< Assigned device number within class. */ + int active; /**< 1 if registered, 0 if free. */ + + /** Device-specific operations (union of block/char). */ + union { + devicefs_block_ops_t *block_ops; + devicefs_char_ops_t *char_ops; + }; + + /** Opaque driver-specific data passed to operation callbacks. */ + void *dev_data; +} devicefs_device_t; + +/** + * Initialize the devicefs subsystem and mount at /dev. + * + * @return 0 on success, -1 on failure. + */ +int init_devicefs(void); + +/** + * Register a new block device. + * + * The devicefs assigns a sequential number within the class. For example, + * registering class "hdd" twice yields "hdd1" and "hdd2". + * + * @param class_name Device class name (e.g., "hdd", "cd"). + * @param ops Block device operations. + * @param dev_data Opaque data passed to operation callbacks. + * @return Pointer to the device entry, or NULL on failure. + */ +devicefs_device_t *devicefs_register_block(const char *class_name, + devicefs_block_ops_t *ops, + void *dev_data); + +/** + * Register a new character device. + * + * @param class_name Device class name (e.g., "tty", "serial"). + * @param ops Character device operations. + * @param dev_data Opaque data passed to operation callbacks. + * @return Pointer to the device entry, or NULL on failure. + */ +devicefs_device_t *devicefs_register_char(const char *class_name, + devicefs_char_ops_t *ops, + void *dev_data); + +/** + * Find a device by its full name (e.g., "hdd1"). + * + * @param name Device name. + * @return Pointer to the device entry, or NULL if not found. + */ +devicefs_device_t *devicefs_find(const char *name); + +/** + * Get the next device number for a given class. + * This is called internally but may be useful for drivers. + * + * @param class_name Device class name. + * @return Next sequential number (starting from 1). + */ +uint32_t devicefs_next_number(const char *class_name); + +#endif /* DEVICEFS_H */ diff --git a/src/kernel.c b/src/kernel.c index 18ca0bb..ec7b180 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -69,36 +69,65 @@ void kernel_main(uint32_t magic, uint32_t addr) { /* Initialize serial port first so all debug output goes to COM1 too */ serial_init(); - /* Early canary: write directly to VGA text buffer at 0xB8000. - * If the display is in text mode, this will show a bright magenta 'C' - * in the top-left corner before any subsystem is initialized. */ - { - volatile uint16_t *vga = (volatile uint16_t *)0xB8000; - vga[0] = (uint16_t)'C' | (0x5F << 8); /* magenta on magenta = visible block */ - vga[1] = (uint16_t)'O' | (0x5F << 8); - vga[2] = (uint16_t)'S' | (0x5F << 8); - } + /* 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); - return; + + /* 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 */ @@ -152,6 +181,7 @@ void kernel_main(uint32_t magic, uint32_t addr) { } init_paging(); + EARLY_PRINT("PAGING "); offset_print("Paging initialized\n"); /* If GRUB provided a graphical framebuffer, identity-map it so @@ -182,21 +212,25 @@ void kernel_main(uint32_t magic, uint32_t addr) { } 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 */ @@ -214,6 +248,7 @@ void kernel_main(uint32_t magic, uint32_t addr) { } init_tss(); + EARLY_PRINT("TSS "); offset_print("TSS initialized\n"); init_syscalls(); @@ -228,6 +263,9 @@ void kernel_main(uint32_t magic, uint32_t addr) { init_drivers(); offset_print("Drivers initialized\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");