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 */
|
||||
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");
|
||||
|
||||
Reference in New Issue
Block a user