Attempt 2 #2

Open
seeseemelk wants to merge 56 commits from attempt-2 into master
2 changed files with 183 additions and 10 deletions
Showing only changes of commit d3ab5a5b55 - Show all commits

135
src/devicefs.h Normal file
View 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 */

View File

@@ -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");