Files
claude-os/src/mbr.c
AI d064e67a8f Add MBR partition driver
- Scan sector 0 of all hdd devices for MBR signature (0xAA55)
- Parse 4 partition entries, register non-empty ones as sub-devices
- Sub-devices named hddNmbrY (e.g., hdd1mbr1) via devicefs
- LBA translation: sub-device reads/writes offset by partition start LBA
- Partition type constants for FAT12/16/32, Linux, NTFS, etc.
- IDE improvements: floating bus detection (0xFF), channel presence check
- Tested: detects FAT32 LBA partition (type=0x0C) on test disk
- Check off devicefs, IDE, MBR in README
2026-02-23 14:08:10 +00:00

271 lines
8.4 KiB
C

/**
* @file mbr.c
* @brief MBR partition table driver implementation.
*
* Reads sector 0 from hard drives registered with the devicefs, parses
* the MBR partition table, and registers each partition as a sub-device.
* Partition devices are named using the parent device name + "mbr" class
* prefix, with the number assigned by the devicefs (e.g., hdd1mbr1).
*
* Each partition sub-device translates LBA offsets relative to its own
* start into absolute LBA addresses on the parent device.
*/
#include "mbr.h"
#include "devicefs.h"
#include <string.h>
/* Debug print helpers defined in kernel.c */
extern void offset_print(const char *str);
extern void print_hex(uint32_t val);
/** Maximum number of MBR partition sub-devices. */
#define MBR_MAX_SUBS 16
/**
* Per-partition context for sub-device read/write operations.
*/
typedef struct mbr_sub_device {
devicefs_device_t *parent; /**< Parent block device. */
uint32_t lba_start; /**< Partition start LBA on parent. */
uint32_t sector_count; /**< Partition size in sectors. */
uint8_t type; /**< MBR partition type code. */
int active; /**< 1 if in use. */
} mbr_sub_device_t;
/** Pool of sub-device contexts. */
static mbr_sub_device_t sub_devices[MBR_MAX_SUBS];
/**
* Allocate a sub-device context.
* @return Pointer to a free sub-device, or NULL if exhausted.
*/
static mbr_sub_device_t *alloc_sub(void) {
for (int i = 0; i < MBR_MAX_SUBS; i++) {
if (!sub_devices[i].active) {
sub_devices[i].active = 1;
return &sub_devices[i];
}
}
return NULL;
}
/* ================================================================
* Block device ops for MBR partition sub-devices
* ================================================================ */
/**
* Read sectors from a partition (translates LBA to parent device).
*/
static int mbr_sub_read(void *dev_data, uint32_t lba,
uint32_t count, void *buf) {
mbr_sub_device_t *sub = (mbr_sub_device_t *)dev_data;
if (!sub || !sub->parent) return -1;
/* Bounds check */
if (lba + count > sub->sector_count) return -1;
/* Translate to absolute LBA on parent device */
uint32_t abs_lba = sub->lba_start + lba;
/* Read from parent device */
if (!sub->parent->block_ops || !sub->parent->block_ops->read_sectors) {
return -1;
}
return sub->parent->block_ops->read_sectors(sub->parent->dev_data,
abs_lba, count, buf);
}
/**
* Write sectors to a partition (translates LBA to parent device).
*/
static int mbr_sub_write(void *dev_data, uint32_t lba,
uint32_t count, const void *buf) {
mbr_sub_device_t *sub = (mbr_sub_device_t *)dev_data;
if (!sub || !sub->parent) return -1;
if (lba + count > sub->sector_count) return -1;
uint32_t abs_lba = sub->lba_start + lba;
if (!sub->parent->block_ops || !sub->parent->block_ops->write_sectors) {
return -1;
}
return sub->parent->block_ops->write_sectors(sub->parent->dev_data,
abs_lba, count, buf);
}
/**
* Get sector size (same as parent device).
*/
static uint32_t mbr_sub_sector_size(void *dev_data) {
mbr_sub_device_t *sub = (mbr_sub_device_t *)dev_data;
if (!sub || !sub->parent || !sub->parent->block_ops) return 512;
if (sub->parent->block_ops->sector_size) {
return sub->parent->block_ops->sector_size(sub->parent->dev_data);
}
return 512;
}
/**
* Get total sector count of the partition.
*/
static uint32_t mbr_sub_sector_count(void *dev_data) {
mbr_sub_device_t *sub = (mbr_sub_device_t *)dev_data;
return sub ? sub->sector_count : 0;
}
/** Block operations for MBR partition sub-devices. */
static devicefs_block_ops_t mbr_sub_ops = {
.read_sectors = mbr_sub_read,
.write_sectors = mbr_sub_write,
.sector_size = mbr_sub_sector_size,
.sector_count = mbr_sub_sector_count,
};
/* ================================================================
* MBR scanning
* ================================================================ */
int mbr_scan(const char *parent_name) {
devicefs_device_t *parent = devicefs_find(parent_name);
if (!parent) {
offset_print(" MBR: device not found: ");
offset_print(parent_name);
offset_print("\n");
return -1;
}
if (parent->type != DEVICEFS_BLOCK || !parent->block_ops) {
offset_print(" MBR: not a block device: ");
offset_print(parent_name);
offset_print("\n");
return -1;
}
if (!parent->block_ops->read_sectors) {
offset_print(" MBR: device has no read_sectors op\n");
return -1;
}
/* Read sector 0 (MBR) */
uint8_t sector[512];
if (parent->block_ops->read_sectors(parent->dev_data, 0, 1, sector) != 0) {
offset_print(" MBR: failed to read sector 0 of ");
offset_print(parent_name);
offset_print("\n");
return -1;
}
/* Check MBR signature */
uint16_t sig = (uint16_t)sector[510] | ((uint16_t)sector[511] << 8);
if (sig != MBR_SIGNATURE) {
offset_print(" MBR: no valid signature on ");
offset_print(parent_name);
offset_print(" (sig=");
print_hex(sig);
offset_print(")\n");
return 0; /* Not an error, just no MBR */
}
/* Build the class name for partition sub-devices: parentName + "mbr" */
char class_name[DEVICEFS_MAX_DEV_NAME];
memset(class_name, 0, sizeof(class_name));
strncpy(class_name, parent_name, DEVICEFS_MAX_DEV_NAME - 4);
/* Append "mbr" */
uint32_t clen = strlen(class_name);
if (clen + 3 < DEVICEFS_MAX_DEV_NAME) {
class_name[clen] = 'm';
class_name[clen + 1] = 'b';
class_name[clen + 2] = 'r';
class_name[clen + 3] = '\0';
}
/* Parse the 4 partition table entries (at offset 446) */
int found = 0;
for (int i = 0; i < MBR_MAX_PARTITIONS; i++) {
mbr_partition_entry_t *entry =
(mbr_partition_entry_t *)(sector + 446 + i * 16);
/* Skip empty partitions */
if (entry->type == MBR_TYPE_EMPTY) continue;
if (entry->sector_count == 0) continue;
/* Skip extended partitions for now */
if (entry->type == MBR_TYPE_EXTENDED) {
offset_print(" MBR: skipping extended partition on ");
offset_print(parent_name);
offset_print("\n");
continue;
}
mbr_sub_device_t *sub = alloc_sub();
if (!sub) {
offset_print(" MBR: no free sub-device slots\n");
break;
}
sub->parent = parent;
sub->lba_start = entry->lba_start;
sub->sector_count = entry->sector_count;
sub->type = entry->type;
/* Register with devicefs */
devicefs_device_t *dev = devicefs_register_block(class_name,
&mbr_sub_ops,
sub);
if (dev) {
offset_print(" MBR: ");
offset_print(dev->name);
offset_print(" type=");
print_hex(entry->type);
offset_print(" LBA=");
print_hex(entry->lba_start);
offset_print(" size=");
print_hex(entry->sector_count);
offset_print(" sectors\n");
found++;
}
}
if (found == 0) {
offset_print(" MBR: no partitions on ");
offset_print(parent_name);
offset_print("\n");
}
return found;
}
int init_mbr(void) {
memset(sub_devices, 0, sizeof(sub_devices));
/* Scan all "hdd" class devices for MBR partitions */
int total_partitions = 0;
for (uint32_t i = 1; i <= 16; i++) {
/* Build device name: hdd1, hdd2, ... */
char name[DEVICEFS_MAX_DEV_NAME];
memset(name, 0, sizeof(name));
name[0] = 'h'; name[1] = 'd'; name[2] = 'd';
/* Append number */
if (i < 10) {
name[3] = (char)('0' + i);
} else {
name[3] = (char)('0' + i / 10);
name[4] = (char)('0' + i % 10);
}
devicefs_device_t *dev = devicefs_find(name);
if (!dev) continue;
int n = mbr_scan(name);
if (n > 0) total_partitions += n;
}
offset_print(" MBR: ");
print_hex((uint32_t)total_partitions);
offset_print(" partition(s) found\n");
return 0;
}