Attempt 2 #2
135
src/devicefs.h
Normal file
135
src/devicefs.h
Normal file
@@ -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 <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/** 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 */
|
||||||
58
src/kernel.c
58
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 */
|
/* Initialize serial port first so all debug output goes to COM1 too */
|
||||||
serial_init();
|
serial_init();
|
||||||
|
|
||||||
/* Early canary: write directly to VGA text buffer at 0xB8000.
|
/* Early VGA: write directly to the text buffer at 0xB8000 for boot
|
||||||
* If the display is in text mode, this will show a bright magenta 'C'
|
* progress that is visible even without the VGA driver initialized.
|
||||||
* in the top-left corner before any subsystem is initialized. */
|
* Attribute 0x1F = white on blue. */
|
||||||
{
|
volatile uint16_t *early_vga = (volatile uint16_t *)0xB8000;
|
||||||
volatile uint16_t *vga = (volatile uint16_t *)0xB8000;
|
int early_pos = 0;
|
||||||
vga[0] = (uint16_t)'C' | (0x5F << 8); /* magenta on magenta = visible block */
|
|
||||||
vga[1] = (uint16_t)'O' | (0x5F << 8);
|
/* Helper macro: write a short string to early VGA */
|
||||||
vga[2] = (uint16_t)'S' | (0x5F << 8);
|
#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) {
|
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: ");
|
offset_print("Invalid magic number: ");
|
||||||
print_hex(magic);
|
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");
|
offset_print("Booting...\n");
|
||||||
|
|
||||||
init_gdt();
|
init_gdt();
|
||||||
|
EARLY_PRINT("GDT ");
|
||||||
offset_print("GDT initialized\n");
|
offset_print("GDT initialized\n");
|
||||||
|
|
||||||
init_idt();
|
init_idt();
|
||||||
|
EARLY_PRINT("IDT ");
|
||||||
offset_print("IDT initialized\n");
|
offset_print("IDT initialized\n");
|
||||||
|
|
||||||
init_pic();
|
init_pic();
|
||||||
/* Unmask timer IRQ (IRQ0) explicitly */
|
/* Unmask timer IRQ (IRQ0) explicitly */
|
||||||
pic_clear_mask(0);
|
pic_clear_mask(0);
|
||||||
|
EARLY_PRINT("PIC ");
|
||||||
offset_print("PIC initialized\n");
|
offset_print("PIC initialized\n");
|
||||||
|
|
||||||
init_pmm(addr);
|
init_pmm(addr);
|
||||||
|
EARLY_PRINT("PMM ");
|
||||||
offset_print("PMM initialized\n");
|
offset_print("PMM initialized\n");
|
||||||
|
|
||||||
/* Scan Multiboot2 tags for the initrd module and framebuffer info */
|
/* 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();
|
init_paging();
|
||||||
|
EARLY_PRINT("PAGING ");
|
||||||
offset_print("Paging initialized\n");
|
offset_print("Paging initialized\n");
|
||||||
|
|
||||||
/* If GRUB provided a graphical framebuffer, identity-map it so
|
/* 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();
|
init_kmalloc();
|
||||||
|
EARLY_PRINT("HEAP ");
|
||||||
offset_print("Memory allocator initialized\n");
|
offset_print("Memory allocator initialized\n");
|
||||||
|
|
||||||
/* Initialize CPIO ramdisk if module was loaded */
|
/* Initialize CPIO ramdisk if module was loaded */
|
||||||
if (initrd_start != 0) {
|
if (initrd_start != 0) {
|
||||||
cpio_init((const void *)initrd_start, initrd_end - initrd_start);
|
cpio_init((const void *)initrd_start, initrd_end - initrd_start);
|
||||||
|
EARLY_PRINT("CPIO ");
|
||||||
offset_print("CPIO ramdisk initialized\n");
|
offset_print("CPIO ramdisk initialized\n");
|
||||||
} else {
|
} else {
|
||||||
offset_print("No initrd module found\n");
|
offset_print("No initrd module found\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
init_vfs();
|
init_vfs();
|
||||||
|
EARLY_PRINT("VFS ");
|
||||||
offset_print("VFS initialized\n");
|
offset_print("VFS initialized\n");
|
||||||
|
|
||||||
if (initrd_start != 0) {
|
if (initrd_start != 0) {
|
||||||
init_initrd_fs();
|
init_initrd_fs();
|
||||||
|
EARLY_PRINT("INITRD ");
|
||||||
offset_print("Initrd filesystem mounted\n");
|
offset_print("Initrd filesystem mounted\n");
|
||||||
|
|
||||||
/* Test VFS: read a file from the initrd */
|
/* Test VFS: read a file from the initrd */
|
||||||
@@ -214,6 +248,7 @@ void kernel_main(uint32_t magic, uint32_t addr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init_tss();
|
init_tss();
|
||||||
|
EARLY_PRINT("TSS ");
|
||||||
offset_print("TSS initialized\n");
|
offset_print("TSS initialized\n");
|
||||||
|
|
||||||
init_syscalls();
|
init_syscalls();
|
||||||
@@ -228,6 +263,9 @@ void kernel_main(uint32_t magic, uint32_t addr) {
|
|||||||
init_drivers();
|
init_drivers();
|
||||||
offset_print("Drivers initialized\n");
|
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 */
|
/* Show memory statistics and boot progress on VGA */
|
||||||
vga_show_mem_stats();
|
vga_show_mem_stats();
|
||||||
vga_puts("Boot complete.\n\n");
|
vga_puts("Boot complete.\n\n");
|
||||||
|
|||||||
Reference in New Issue
Block a user