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
This commit is contained in:
@@ -58,9 +58,9 @@ Once a task is completed, it should be checked off.
|
||||
- [x] Implement environment variables. Apps should be able to modify this, and it should be copied to new forks of an app.
|
||||
- [x] Create a basic shell program `sh`. This shell must be able to start the hello-world app. It must include `cd` as a built-in to change the current working directory.
|
||||
- [x] Create an `ls` app. It should list the contents of the current working directory, via the environment variable.
|
||||
- [ ] Create a devicefs vfs driver. The devicefs subsystem should allow drivers to create new block devices devices.
|
||||
- [ ] Create an IDE driver. It should enumerate all IDE devices, and expose them to the vfs driver as `hddN` or `cdN`, where N is a number that starts at 1 and increases for each new device. The `N` value should be calucated by the devicefs subsystem, not the IDE driver.
|
||||
- [ ] Create an MBR driver. It should be able to automatically detect when new hard drives are detected, and automatically scan them for MBR partitions. Each MBR partition found should be listed as `hddNmbrY`, where Y is a number determined by the devicefs subsystem.
|
||||
- [x] Create a devicefs vfs driver. The devicefs subsystem should allow drivers to create new block devices devices.
|
||||
- [x] Create an IDE driver. It should enumerate all IDE devices, and expose them to the vfs driver as `hddN` or `cdN`, where N is a number that starts at 1 and increases for each new device. The `N` value should be calucated by the devicefs subsystem, not the IDE driver.
|
||||
- [x] Create an MBR driver. It should be able to automatically detect when new hard drives are detected, and automatically scan them for MBR partitions. Each MBR partition found should be listed as `hddNmbrY`, where Y is a number determined by the devicefs subsystem.
|
||||
- [ ] Create a `sysfs` vfs driver. It should allow drivers to expose text/config files to the VFS. Each driver can request a namespace in the sysfs. E.g.: the IDE driver could request `ide`. During this registration, the drive must provide a struct containing function callbacks. The callbacks must contain the function `list`, `read`, and `write`. These are executed when the user lists a directory, reads a file, or writes a file. It is expected that the contents of these files are extremely small and can simply be stored on the stack. It should be very easy for a driver to expose new information.
|
||||
- [ ] Create a FAT32 driver. It should allow reading and writing to and from a block device.
|
||||
- [ ] Create the `mount` app. It should allow on to mount a block device using the fat32 driver. Internally, it should use sysfs (which should be mounted automatically by the kernel to `/sys`) to setup a new mount.
|
||||
|
||||
@@ -21,6 +21,7 @@ add_executable(kernel
|
||||
initrd_fs.c
|
||||
devicefs.c
|
||||
ide.c
|
||||
mbr.c
|
||||
env.c
|
||||
keyboard.c
|
||||
interrupts.S
|
||||
|
||||
23
src/ide.c
23
src/ide.c
@@ -38,9 +38,10 @@ static ide_device_t ide_devices[IDE_MAX_DEVICES];
|
||||
*/
|
||||
static uint8_t ide_wait(uint16_t io_base) {
|
||||
uint8_t status;
|
||||
int timeout = 100000;
|
||||
int timeout = 500000;
|
||||
do {
|
||||
status = inb(io_base + IDE_REG_STATUS);
|
||||
if (status == 0xFF) return 0xFF; /* Floating bus */
|
||||
if (--timeout == 0) return 0xFF;
|
||||
} while (status & IDE_STATUS_BSY);
|
||||
return status;
|
||||
@@ -55,9 +56,10 @@ static uint8_t ide_wait(uint16_t io_base) {
|
||||
*/
|
||||
static int ide_wait_drq(uint16_t io_base) {
|
||||
uint8_t status;
|
||||
int timeout = 100000;
|
||||
int timeout = 500000;
|
||||
do {
|
||||
status = inb(io_base + IDE_REG_STATUS);
|
||||
if (status == 0xFF) return -1;
|
||||
if (status & (IDE_STATUS_ERR | IDE_STATUS_DF)) return -1;
|
||||
if (--timeout == 0) return -1;
|
||||
} while ((status & (IDE_STATUS_BSY | IDE_STATUS_DRQ)) != IDE_STATUS_DRQ);
|
||||
@@ -130,6 +132,10 @@ static int ide_identify(ide_device_t *dev) {
|
||||
/* Select the drive */
|
||||
ide_select_drive(io, dev->drive);
|
||||
|
||||
/* Check for floating bus (no device) — read status, if 0xFF, no drive */
|
||||
uint8_t check = inb(io + IDE_REG_STATUS);
|
||||
if (check == 0xFF) return -1;
|
||||
|
||||
/* Clear sector count and LBA registers */
|
||||
outb(io + IDE_REG_SECCOUNT, 0);
|
||||
outb(io + IDE_REG_LBA_LO, 0);
|
||||
@@ -139,9 +145,9 @@ static int ide_identify(ide_device_t *dev) {
|
||||
/* Send IDENTIFY command */
|
||||
outb(io + IDE_REG_COMMAND, IDE_CMD_IDENTIFY);
|
||||
|
||||
/* Read status — if 0, no device */
|
||||
/* Read status — if 0 or 0xFF, no device */
|
||||
uint8_t status = inb(io + IDE_REG_STATUS);
|
||||
if (status == 0) return -1;
|
||||
if (status == 0 || status == 0xFF) return -1;
|
||||
|
||||
/* Wait for BSY to clear */
|
||||
status = ide_wait(io);
|
||||
@@ -322,6 +328,15 @@ static int ide_driver_init(void) {
|
||||
int found = 0;
|
||||
|
||||
for (int ch = 0; ch < 2; ch++) {
|
||||
/* Check if channel exists by reading status — 0xFF = floating bus */
|
||||
uint8_t ch_status = inb(io_bases[ch] + IDE_REG_STATUS);
|
||||
if (ch_status == 0xFF) {
|
||||
offset_print(" IDE: ");
|
||||
offset_print(ch == 0 ? "primary" : "secondary");
|
||||
offset_print(" channel not present\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Software reset the channel */
|
||||
ide_soft_reset(ctrl_bases[ch]);
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "vfs.h"
|
||||
#include "initrd_fs.h"
|
||||
#include "devicefs.h"
|
||||
#include "mbr.h"
|
||||
#include "keyboard.h"
|
||||
#include "framebuffer.h"
|
||||
|
||||
@@ -286,6 +287,10 @@ void kernel_main(uint32_t magic, uint32_t addr) {
|
||||
EARLY_PRINT("DRV ");
|
||||
offset_print("Drivers initialized\n");
|
||||
|
||||
/* Scan for MBR partitions on detected hard drives */
|
||||
init_mbr();
|
||||
offset_print("MBR scan complete\n");
|
||||
|
||||
/* At this point the VGA driver has been initialized and taken over
|
||||
* the display. The early VGA text is no longer visible. */
|
||||
|
||||
|
||||
270
src/mbr.c
Normal file
270
src/mbr.c
Normal file
@@ -0,0 +1,270 @@
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
67
src/mbr.h
Normal file
67
src/mbr.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* @file mbr.h
|
||||
* @brief MBR (Master Boot Record) partition table driver.
|
||||
*
|
||||
* Scans block devices for MBR partition tables and registers each
|
||||
* discovered partition as a new block device in the devicefs.
|
||||
* For example, hdd1 with two partitions becomes hdd1mbr1 and hdd1mbr2.
|
||||
*
|
||||
* The partition number (Y in hddNmbrY) is assigned by the devicefs
|
||||
* subsystem using the device class "hddNmbr".
|
||||
*/
|
||||
|
||||
#ifndef MBR_H
|
||||
#define MBR_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/** MBR signature bytes (offset 510-511). */
|
||||
#define MBR_SIGNATURE 0xAA55
|
||||
|
||||
/** Maximum partitions in a standard MBR. */
|
||||
#define MBR_MAX_PARTITIONS 4
|
||||
|
||||
/** MBR partition table entry (16 bytes each). */
|
||||
typedef struct __attribute__((packed)) mbr_partition_entry {
|
||||
uint8_t status; /**< 0x80 = bootable, 0x00 = not bootable. */
|
||||
uint8_t chs_first[3]; /**< CHS address of first sector. */
|
||||
uint8_t type; /**< Partition type code. */
|
||||
uint8_t chs_last[3]; /**< CHS address of last sector. */
|
||||
uint32_t lba_start; /**< LBA of first sector. */
|
||||
uint32_t sector_count; /**< Number of sectors. */
|
||||
} mbr_partition_entry_t;
|
||||
|
||||
/** Well-known MBR partition types. */
|
||||
#define MBR_TYPE_EMPTY 0x00
|
||||
#define MBR_TYPE_FAT12 0x01
|
||||
#define MBR_TYPE_FAT16_SM 0x04 /**< FAT16 < 32 MiB. */
|
||||
#define MBR_TYPE_EXTENDED 0x05
|
||||
#define MBR_TYPE_FAT16_LG 0x06 /**< FAT16 >= 32 MiB. */
|
||||
#define MBR_TYPE_FAT32 0x0B
|
||||
#define MBR_TYPE_FAT32_LBA 0x0C
|
||||
#define MBR_TYPE_FAT16_LBA 0x0E
|
||||
#define MBR_TYPE_LINUX 0x83
|
||||
#define MBR_TYPE_LINUX_SWAP 0x82
|
||||
|
||||
/**
|
||||
* Scan a block device for MBR partitions.
|
||||
*
|
||||
* Reads sector 0 of the given device, validates the MBR signature,
|
||||
* and registers each non-empty partition entry with the devicefs
|
||||
* as a sub-device.
|
||||
*
|
||||
* @param parent_name Name of the parent device (e.g., "hdd1").
|
||||
* @return Number of partitions found, or -1 on error.
|
||||
*/
|
||||
int mbr_scan(const char *parent_name);
|
||||
|
||||
/**
|
||||
* Initialize the MBR subsystem.
|
||||
*
|
||||
* Scans all currently registered "hdd" class devices for MBR partitions.
|
||||
*
|
||||
* @return 0 on success.
|
||||
*/
|
||||
int init_mbr(void);
|
||||
|
||||
#endif /* MBR_H */
|
||||
Reference in New Issue
Block a user