Compare commits
3 Commits
3512e937ae
...
d064e67a8f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d064e67a8f | ||
|
|
c07ec030a7 | ||
|
|
c12d49dea0 |
@@ -57,10 +57,10 @@ Once a task is completed, it should be checked off.
|
||||
- [x] Implement the fork system call.
|
||||
- [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.
|
||||
- [ ] 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 an `ls` app. It should list the contents of the current working directory, via the environment variable.
|
||||
- [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.
|
||||
|
||||
@@ -19,6 +19,9 @@ add_executable(kernel
|
||||
cpio.c
|
||||
vfs.c
|
||||
initrd_fs.c
|
||||
devicefs.c
|
||||
ide.c
|
||||
mbr.c
|
||||
env.c
|
||||
keyboard.c
|
||||
interrupts.S
|
||||
|
||||
350
src/devicefs.c
Normal file
350
src/devicefs.c
Normal file
@@ -0,0 +1,350 @@
|
||||
/**
|
||||
* @file devicefs.c
|
||||
* @brief Device filesystem (devicefs) implementation.
|
||||
*
|
||||
* Provides a VFS driver mounted at /dev that exposes block and character
|
||||
* devices. Kernel drivers register devices via devicefs_register_block()
|
||||
* or devicefs_register_char(), and the devicefs assigns sequential numbers
|
||||
* per device class (e.g., hdd1, hdd2, cd1).
|
||||
*
|
||||
* The VFS interface supports:
|
||||
* - readdir: lists all registered devices
|
||||
* - finddir: looks up a device by name
|
||||
* - read/write: delegates to the device's block or char operations
|
||||
*/
|
||||
|
||||
#include "devicefs.h"
|
||||
#include "vfs.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);
|
||||
|
||||
/** Device table. */
|
||||
static devicefs_device_t devices[DEVICEFS_MAX_DEVICES];
|
||||
|
||||
/** Number of active devices. */
|
||||
static uint32_t device_count = 0;
|
||||
|
||||
/**
|
||||
* Find a free slot in the device table.
|
||||
* @return Index of free slot, or -1 if full.
|
||||
*/
|
||||
static int find_free_slot(void) {
|
||||
for (int i = 0; i < DEVICEFS_MAX_DEVICES; i++) {
|
||||
if (!devices[i].active) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* VFS operations for /dev
|
||||
* ================================================================ */
|
||||
|
||||
/**
|
||||
* Read from a device file.
|
||||
*
|
||||
* For block devices, translates byte offset/size to sector reads.
|
||||
* For character devices, delegates directly to the char read op.
|
||||
*/
|
||||
static int32_t devfs_read(vfs_node_t *node, uint32_t offset,
|
||||
uint32_t size, void *buf) {
|
||||
if (!node || !node->fs_data) return -1;
|
||||
|
||||
devicefs_device_t *dev = (devicefs_device_t *)node->fs_data;
|
||||
|
||||
if (dev->type == DEVICEFS_BLOCK && dev->block_ops) {
|
||||
uint32_t sec_size = 512;
|
||||
if (dev->block_ops->sector_size) {
|
||||
sec_size = dev->block_ops->sector_size(dev->dev_data);
|
||||
}
|
||||
if (sec_size == 0) return -1;
|
||||
|
||||
uint32_t start_lba = offset / sec_size;
|
||||
uint32_t end_byte = offset + size;
|
||||
uint32_t end_lba = (end_byte + sec_size - 1) / sec_size;
|
||||
uint32_t num_sectors = end_lba - start_lba;
|
||||
|
||||
/* For simplicity, require aligned reads for now */
|
||||
if (offset % sec_size != 0 || size % sec_size != 0) {
|
||||
/* Unaligned read: read full sectors, copy partial */
|
||||
/* TODO: implement unaligned block reads with temp buffer */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (dev->block_ops->read_sectors) {
|
||||
int ret = dev->block_ops->read_sectors(dev->dev_data,
|
||||
start_lba,
|
||||
num_sectors,
|
||||
buf);
|
||||
return (ret == 0) ? (int32_t)size : -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (dev->type == DEVICEFS_CHAR && dev->char_ops) {
|
||||
if (dev->char_ops->read) {
|
||||
return dev->char_ops->read(dev->dev_data, size, buf);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write to a device file.
|
||||
*
|
||||
* For block devices, translates byte offset/size to sector writes.
|
||||
* For character devices, delegates directly to the char write op.
|
||||
*/
|
||||
static int32_t devfs_write(vfs_node_t *node, uint32_t offset,
|
||||
uint32_t size, const void *buf) {
|
||||
if (!node || !node->fs_data) return -1;
|
||||
|
||||
devicefs_device_t *dev = (devicefs_device_t *)node->fs_data;
|
||||
|
||||
if (dev->type == DEVICEFS_BLOCK && dev->block_ops) {
|
||||
uint32_t sec_size = 512;
|
||||
if (dev->block_ops->sector_size) {
|
||||
sec_size = dev->block_ops->sector_size(dev->dev_data);
|
||||
}
|
||||
if (sec_size == 0) return -1;
|
||||
|
||||
if (offset % sec_size != 0 || size % sec_size != 0) {
|
||||
return -1; /* Require aligned writes */
|
||||
}
|
||||
|
||||
uint32_t start_lba = offset / sec_size;
|
||||
uint32_t num_sectors = size / sec_size;
|
||||
|
||||
if (dev->block_ops->write_sectors) {
|
||||
int ret = dev->block_ops->write_sectors(dev->dev_data,
|
||||
start_lba,
|
||||
num_sectors,
|
||||
buf);
|
||||
return (ret == 0) ? (int32_t)size : -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (dev->type == DEVICEFS_CHAR && dev->char_ops) {
|
||||
if (dev->char_ops->write) {
|
||||
return dev->char_ops->write(dev->dev_data, size, buf);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a directory entry from /dev.
|
||||
* Lists all registered devices.
|
||||
*/
|
||||
static int devfs_readdir(vfs_node_t *dir, uint32_t idx, vfs_dirent_t *out) {
|
||||
(void)dir;
|
||||
|
||||
uint32_t count = 0;
|
||||
for (int i = 0; i < DEVICEFS_MAX_DEVICES; i++) {
|
||||
if (!devices[i].active) continue;
|
||||
if (count == idx) {
|
||||
memset(out, 0, sizeof(vfs_dirent_t));
|
||||
strncpy(out->name, devices[i].name, VFS_MAX_NAME - 1);
|
||||
out->inode = (uint32_t)i;
|
||||
out->type = (devices[i].type == DEVICEFS_BLOCK) ?
|
||||
VFS_BLOCKDEV : VFS_CHARDEV;
|
||||
return 0;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
return -1; /* No more entries */
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a device by name within /dev.
|
||||
*/
|
||||
static int devfs_finddir(vfs_node_t *dir, const char *name, vfs_node_t *out) {
|
||||
(void)dir;
|
||||
|
||||
for (int i = 0; i < DEVICEFS_MAX_DEVICES; i++) {
|
||||
if (!devices[i].active) continue;
|
||||
if (strcmp(devices[i].name, name) == 0) {
|
||||
memset(out, 0, sizeof(vfs_node_t));
|
||||
strncpy(out->name, devices[i].name, VFS_MAX_NAME - 1);
|
||||
out->type = (devices[i].type == DEVICEFS_BLOCK) ?
|
||||
VFS_BLOCKDEV : VFS_CHARDEV;
|
||||
out->inode = (uint32_t)i;
|
||||
out->fs_data = &devices[i];
|
||||
|
||||
/* For block devices, compute size from sector count */
|
||||
if (devices[i].type == DEVICEFS_BLOCK && devices[i].block_ops) {
|
||||
uint32_t sec_size = 512;
|
||||
uint32_t sec_count = 0;
|
||||
if (devices[i].block_ops->sector_size) {
|
||||
sec_size = devices[i].block_ops->sector_size(devices[i].dev_data);
|
||||
}
|
||||
if (devices[i].block_ops->sector_count) {
|
||||
sec_count = devices[i].block_ops->sector_count(devices[i].dev_data);
|
||||
}
|
||||
out->size = sec_count * sec_size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Filesystem operations for /dev. */
|
||||
static vfs_fs_ops_t devfs_ops = {
|
||||
.open = NULL,
|
||||
.close = NULL,
|
||||
.read = devfs_read,
|
||||
.write = devfs_write,
|
||||
.readdir = devfs_readdir,
|
||||
.finddir = devfs_finddir,
|
||||
};
|
||||
|
||||
/* ================================================================
|
||||
* Public API
|
||||
* ================================================================ */
|
||||
|
||||
uint32_t devicefs_next_number(const char *class_name) {
|
||||
uint32_t max_num = 0;
|
||||
for (int i = 0; i < DEVICEFS_MAX_DEVICES; i++) {
|
||||
if (!devices[i].active) continue;
|
||||
if (strcmp(devices[i].class_name, class_name) == 0) {
|
||||
if (devices[i].number >= max_num) {
|
||||
max_num = devices[i].number + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Numbers start at 1 */
|
||||
return (max_num == 0) ? 1 : max_num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a uint32 as a decimal string into buf.
|
||||
* Returns pointer to the start of the number within buf.
|
||||
*/
|
||||
static char *uint_to_str(uint32_t val, char *buf, int buf_size) {
|
||||
buf[buf_size - 1] = '\0';
|
||||
int pos = buf_size - 2;
|
||||
if (val == 0) {
|
||||
buf[pos] = '0';
|
||||
return &buf[pos];
|
||||
}
|
||||
while (val > 0 && pos >= 0) {
|
||||
buf[pos--] = (char)('0' + (val % 10));
|
||||
val /= 10;
|
||||
}
|
||||
return &buf[pos + 1];
|
||||
}
|
||||
|
||||
devicefs_device_t *devicefs_register_block(const char *class_name,
|
||||
devicefs_block_ops_t *ops,
|
||||
void *dev_data) {
|
||||
int slot = find_free_slot();
|
||||
if (slot < 0) {
|
||||
offset_print(" DEVICEFS: no free device slots\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
devicefs_device_t *dev = &devices[slot];
|
||||
memset(dev, 0, sizeof(devicefs_device_t));
|
||||
|
||||
/* Copy class name */
|
||||
strncpy(dev->class_name, class_name, DEVICEFS_MAX_CLASS_NAME - 1);
|
||||
|
||||
/* Assign sequential number */
|
||||
dev->number = devicefs_next_number(class_name);
|
||||
|
||||
/* Build full device name: class_name + number */
|
||||
char num_buf[12];
|
||||
char *num_str = uint_to_str(dev->number, num_buf, sizeof(num_buf));
|
||||
strncpy(dev->name, class_name, DEVICEFS_MAX_DEV_NAME - 12);
|
||||
/* Append number string */
|
||||
uint32_t nlen = strlen(dev->name);
|
||||
uint32_t slen = strlen(num_str);
|
||||
if (nlen + slen < DEVICEFS_MAX_DEV_NAME) {
|
||||
memcpy(dev->name + nlen, num_str, slen + 1);
|
||||
}
|
||||
|
||||
dev->type = DEVICEFS_BLOCK;
|
||||
dev->block_ops = ops;
|
||||
dev->dev_data = dev_data;
|
||||
dev->active = 1;
|
||||
device_count++;
|
||||
|
||||
offset_print(" DEVICEFS: registered block device /dev/");
|
||||
offset_print(dev->name);
|
||||
offset_print("\n");
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
devicefs_device_t *devicefs_register_char(const char *class_name,
|
||||
devicefs_char_ops_t *ops,
|
||||
void *dev_data) {
|
||||
int slot = find_free_slot();
|
||||
if (slot < 0) {
|
||||
offset_print(" DEVICEFS: no free device slots\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
devicefs_device_t *dev = &devices[slot];
|
||||
memset(dev, 0, sizeof(devicefs_device_t));
|
||||
|
||||
strncpy(dev->class_name, class_name, DEVICEFS_MAX_CLASS_NAME - 1);
|
||||
dev->number = devicefs_next_number(class_name);
|
||||
|
||||
char num_buf[12];
|
||||
char *num_str = uint_to_str(dev->number, num_buf, sizeof(num_buf));
|
||||
strncpy(dev->name, class_name, DEVICEFS_MAX_DEV_NAME - 12);
|
||||
/* Append number string */
|
||||
uint32_t nlen2 = strlen(dev->name);
|
||||
uint32_t slen2 = strlen(num_str);
|
||||
if (nlen2 + slen2 < DEVICEFS_MAX_DEV_NAME) {
|
||||
memcpy(dev->name + nlen2, num_str, slen2 + 1);
|
||||
}
|
||||
|
||||
dev->type = DEVICEFS_CHAR;
|
||||
dev->char_ops = ops;
|
||||
dev->dev_data = dev_data;
|
||||
dev->active = 1;
|
||||
device_count++;
|
||||
|
||||
offset_print(" DEVICEFS: registered char device /dev/");
|
||||
offset_print(dev->name);
|
||||
offset_print("\n");
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
devicefs_device_t *devicefs_find(const char *name) {
|
||||
for (int i = 0; i < DEVICEFS_MAX_DEVICES; i++) {
|
||||
if (!devices[i].active) continue;
|
||||
if (strcmp(devices[i].name, name) == 0) {
|
||||
return &devices[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int init_devicefs(void) {
|
||||
memset(devices, 0, sizeof(devices));
|
||||
device_count = 0;
|
||||
|
||||
int ret = vfs_mount("/dev", &devfs_ops, NULL);
|
||||
if (ret != 0) {
|
||||
offset_print(" DEVICEFS: failed to mount at /dev\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
offset_print(" DEVICEFS: mounted at /dev\n");
|
||||
return 0;
|
||||
}
|
||||
405
src/ide.c
Normal file
405
src/ide.c
Normal file
@@ -0,0 +1,405 @@
|
||||
/**
|
||||
* @file ide.c
|
||||
* @brief IDE/ATA disk driver implementation.
|
||||
*
|
||||
* Probes the primary and secondary IDE channels for ATA hard drives and
|
||||
* ATAPI CD/DVD drives using PIO-mode IDENTIFY commands. Detected devices
|
||||
* are registered with the devicefs subsystem as block devices:
|
||||
* - ATA drives → "hdd" class (hdd1, hdd2, ...)
|
||||
* - ATAPI drives → "cd" class (cd1, cd2, ...)
|
||||
*
|
||||
* Supports PIO-mode sector reads and writes using 28-bit LBA addressing,
|
||||
* which covers drives up to 128 GiB.
|
||||
*/
|
||||
|
||||
#include "ide.h"
|
||||
#include "port_io.h"
|
||||
#include "devicefs.h"
|
||||
#include "driver.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);
|
||||
|
||||
/** All detected IDE devices. */
|
||||
static ide_device_t ide_devices[IDE_MAX_DEVICES];
|
||||
|
||||
/* ================================================================
|
||||
* Low-level IDE I/O helpers
|
||||
* ================================================================ */
|
||||
|
||||
/**
|
||||
* Wait for the BSY flag to clear on the given channel.
|
||||
* Returns the final status byte, or 0xFF on timeout.
|
||||
*
|
||||
* @param io_base Channel I/O base port.
|
||||
* @return Status byte.
|
||||
*/
|
||||
static uint8_t ide_wait(uint16_t io_base) {
|
||||
uint8_t status;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for BSY to clear and DRQ to set (data ready).
|
||||
* Returns 0 on success, -1 on timeout or error.
|
||||
*
|
||||
* @param io_base Channel I/O base port.
|
||||
* @return 0 on success, -1 on error/timeout.
|
||||
*/
|
||||
static int ide_wait_drq(uint16_t io_base) {
|
||||
uint8_t status;
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read 256 16-bit words (512 bytes) from the data register.
|
||||
*
|
||||
* @param io_base Channel I/O base port.
|
||||
* @param buf Destination buffer (must be at least 512 bytes).
|
||||
*/
|
||||
static void ide_read_buffer(uint16_t io_base, uint16_t *buf) {
|
||||
for (int i = 0; i < 256; i++) {
|
||||
buf[i] = inw(io_base + IDE_REG_DATA);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write 256 16-bit words (512 bytes) to the data register.
|
||||
*
|
||||
* @param io_base Channel I/O base port.
|
||||
* @param buf Source buffer (must be at least 512 bytes).
|
||||
*/
|
||||
static void ide_write_buffer(uint16_t io_base, const uint16_t *buf) {
|
||||
for (int i = 0; i < 256; i++) {
|
||||
outw(io_base + IDE_REG_DATA, buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a software reset on an IDE channel.
|
||||
*
|
||||
* @param ctrl_base Channel control port.
|
||||
*/
|
||||
static void ide_soft_reset(uint16_t ctrl_base) {
|
||||
outb(ctrl_base, 0x04); /* Set SRST bit */
|
||||
/* Wait ~5 µs (several I/O reads) */
|
||||
for (int i = 0; i < 4; i++) inb(ctrl_base);
|
||||
outb(ctrl_base, 0x00); /* Clear SRST bit */
|
||||
/* Wait for BSY to clear */
|
||||
for (int i = 0; i < 4; i++) inb(ctrl_base);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a drive on a channel (master=0, slave=1).
|
||||
*
|
||||
* @param io_base Channel I/O base port.
|
||||
* @param drive 0 for master, 1 for slave.
|
||||
*/
|
||||
static void ide_select_drive(uint16_t io_base, uint8_t drive) {
|
||||
outb(io_base + IDE_REG_DRIVE_HEAD, 0xA0 | (drive << 4));
|
||||
/* Wait ~400 ns by reading status 4 times */
|
||||
for (int i = 0; i < 4; i++) inb(io_base + IDE_REG_STATUS);
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* IDENTIFY command
|
||||
* ================================================================ */
|
||||
|
||||
/**
|
||||
* Send IDENTIFY (or IDENTIFY PACKET) to a drive and read the result.
|
||||
*
|
||||
* @param dev Pointer to the device descriptor to fill.
|
||||
* @return 0 on success, -1 if no device or error.
|
||||
*/
|
||||
static int ide_identify(ide_device_t *dev) {
|
||||
uint16_t io = dev->io_base;
|
||||
|
||||
/* 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);
|
||||
outb(io + IDE_REG_LBA_MID, 0);
|
||||
outb(io + IDE_REG_LBA_HI, 0);
|
||||
|
||||
/* Send IDENTIFY command */
|
||||
outb(io + IDE_REG_COMMAND, IDE_CMD_IDENTIFY);
|
||||
|
||||
/* Read status — if 0 or 0xFF, no device */
|
||||
uint8_t status = inb(io + IDE_REG_STATUS);
|
||||
if (status == 0 || status == 0xFF) return -1;
|
||||
|
||||
/* Wait for BSY to clear */
|
||||
status = ide_wait(io);
|
||||
if (status == 0xFF) return -1;
|
||||
|
||||
/* Check if this is an ATAPI device (LBA_MID/HI will be non-zero) */
|
||||
uint8_t lba_mid = inb(io + IDE_REG_LBA_MID);
|
||||
uint8_t lba_hi = inb(io + IDE_REG_LBA_HI);
|
||||
|
||||
if (lba_mid == 0x14 && lba_hi == 0xEB) {
|
||||
/* ATAPI device — re-identify with IDENTIFY PACKET DEVICE */
|
||||
dev->type = IDE_TYPE_ATAPI;
|
||||
outb(io + IDE_REG_COMMAND, IDE_CMD_IDENTIFY_PKT);
|
||||
status = ide_wait(io);
|
||||
if (status == 0xFF) return -1;
|
||||
} else if (lba_mid == 0 && lba_hi == 0) {
|
||||
dev->type = IDE_TYPE_ATA;
|
||||
} else {
|
||||
/* Unknown device type */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Wait for DRQ */
|
||||
if (ide_wait_drq(io) != 0) return -1;
|
||||
|
||||
/* Read 256 words of identification data */
|
||||
uint16_t identify_buf[256];
|
||||
ide_read_buffer(io, identify_buf);
|
||||
|
||||
/* Parse model string (words 27-46, each word is big-endian) */
|
||||
for (int i = 0; i < 20; i++) {
|
||||
dev->model[i * 2] = (char)(identify_buf[27 + i] >> 8);
|
||||
dev->model[i * 2 + 1] = (char)(identify_buf[27 + i] & 0xFF);
|
||||
}
|
||||
dev->model[40] = '\0';
|
||||
/* Trim trailing spaces */
|
||||
for (int i = 39; i >= 0; i--) {
|
||||
if (dev->model[i] == ' ') dev->model[i] = '\0';
|
||||
else break;
|
||||
}
|
||||
|
||||
/* Parse sector count (28-bit LBA: words 60-61) */
|
||||
if (dev->type == IDE_TYPE_ATA) {
|
||||
dev->sector_count = (uint32_t)identify_buf[60] |
|
||||
((uint32_t)identify_buf[61] << 16);
|
||||
dev->sector_size = 512;
|
||||
} else {
|
||||
/* ATAPI: sector count from READ CAPACITY, default to 0 */
|
||||
dev->sector_count = 0;
|
||||
dev->sector_size = 2048; /* Standard CD sector size */
|
||||
}
|
||||
|
||||
dev->present = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* Block device operations (for devicefs)
|
||||
* ================================================================ */
|
||||
|
||||
/**
|
||||
* Read sectors from an ATA drive using PIO.
|
||||
*/
|
||||
static int ide_block_read(void *dev_data, uint32_t lba,
|
||||
uint32_t count, void *buf) {
|
||||
ide_device_t *dev = (ide_device_t *)dev_data;
|
||||
if (!dev || dev->type != IDE_TYPE_ATA) return -1;
|
||||
|
||||
uint16_t io = dev->io_base;
|
||||
uint8_t *dest = (uint8_t *)buf;
|
||||
|
||||
for (uint32_t s = 0; s < count; s++) {
|
||||
uint32_t cur_lba = lba + s;
|
||||
|
||||
/* Select drive with LBA mode and top 4 LBA bits */
|
||||
outb(io + IDE_REG_DRIVE_HEAD,
|
||||
0xE0 | (dev->drive << 4) | ((cur_lba >> 24) & 0x0F));
|
||||
|
||||
/* Set sector count = 1 and LBA */
|
||||
outb(io + IDE_REG_SECCOUNT, 1);
|
||||
outb(io + IDE_REG_LBA_LO, cur_lba & 0xFF);
|
||||
outb(io + IDE_REG_LBA_MID, (cur_lba >> 8) & 0xFF);
|
||||
outb(io + IDE_REG_LBA_HI, (cur_lba >> 16) & 0xFF);
|
||||
|
||||
/* Send READ SECTORS command */
|
||||
outb(io + IDE_REG_COMMAND, IDE_CMD_READ_PIO);
|
||||
|
||||
/* Wait for data */
|
||||
if (ide_wait_drq(io) != 0) return -1;
|
||||
|
||||
/* Read 256 words (512 bytes) */
|
||||
ide_read_buffer(io, (uint16_t *)(dest + s * 512));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write sectors to an ATA drive using PIO.
|
||||
*/
|
||||
static int ide_block_write(void *dev_data, uint32_t lba,
|
||||
uint32_t count, const void *buf) {
|
||||
ide_device_t *dev = (ide_device_t *)dev_data;
|
||||
if (!dev || dev->type != IDE_TYPE_ATA) return -1;
|
||||
|
||||
uint16_t io = dev->io_base;
|
||||
const uint8_t *src = (const uint8_t *)buf;
|
||||
|
||||
for (uint32_t s = 0; s < count; s++) {
|
||||
uint32_t cur_lba = lba + s;
|
||||
|
||||
outb(io + IDE_REG_DRIVE_HEAD,
|
||||
0xE0 | (dev->drive << 4) | ((cur_lba >> 24) & 0x0F));
|
||||
|
||||
outb(io + IDE_REG_SECCOUNT, 1);
|
||||
outb(io + IDE_REG_LBA_LO, cur_lba & 0xFF);
|
||||
outb(io + IDE_REG_LBA_MID, (cur_lba >> 8) & 0xFF);
|
||||
outb(io + IDE_REG_LBA_HI, (cur_lba >> 16) & 0xFF);
|
||||
|
||||
outb(io + IDE_REG_COMMAND, IDE_CMD_WRITE_PIO);
|
||||
|
||||
if (ide_wait_drq(io) != 0) return -1;
|
||||
|
||||
ide_write_buffer(io, (const uint16_t *)(src + s * 512));
|
||||
|
||||
/* Flush cache: wait for BSY to clear after write */
|
||||
ide_wait(io);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return sector size for a device.
|
||||
*/
|
||||
static uint32_t ide_block_sector_size(void *dev_data) {
|
||||
ide_device_t *dev = (ide_device_t *)dev_data;
|
||||
return dev ? dev->sector_size : 512;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return total sector count for a device.
|
||||
*/
|
||||
static uint32_t ide_block_sector_count(void *dev_data) {
|
||||
ide_device_t *dev = (ide_device_t *)dev_data;
|
||||
return dev ? dev->sector_count : 0;
|
||||
}
|
||||
|
||||
/** Block operations for ATA/ATAPI devices. */
|
||||
static devicefs_block_ops_t ide_block_ops = {
|
||||
.read_sectors = ide_block_read,
|
||||
.write_sectors = ide_block_write,
|
||||
.sector_size = ide_block_sector_size,
|
||||
.sector_count = ide_block_sector_count,
|
||||
};
|
||||
|
||||
/* ================================================================
|
||||
* Driver probe and init
|
||||
* ================================================================ */
|
||||
|
||||
/**
|
||||
* Probe: always return OK since IDE ports are standard.
|
||||
*/
|
||||
static driver_probe_result_t ide_probe(void) {
|
||||
return DRIVER_PROBE_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the IDE driver: scan channels and register devices.
|
||||
*/
|
||||
static int ide_driver_init(void) {
|
||||
memset(ide_devices, 0, sizeof(ide_devices));
|
||||
|
||||
/* Channel definitions: primary (0x1F0, 0x3F6), secondary (0x170, 0x376) */
|
||||
static const uint16_t io_bases[2] = { IDE_PRIMARY_IO, IDE_SECONDARY_IO };
|
||||
static const uint16_t ctrl_bases[2] = { IDE_PRIMARY_CTRL, IDE_SECONDARY_CTRL };
|
||||
|
||||
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]);
|
||||
|
||||
for (int drv = 0; drv < 2; drv++) {
|
||||
int idx = ch * 2 + drv;
|
||||
ide_devices[idx].channel = (uint8_t)ch;
|
||||
ide_devices[idx].drive = (uint8_t)drv;
|
||||
ide_devices[idx].io_base = io_bases[ch];
|
||||
ide_devices[idx].ctrl_base = ctrl_bases[ch];
|
||||
ide_devices[idx].present = 0;
|
||||
|
||||
if (ide_identify(&ide_devices[idx]) == 0) {
|
||||
found++;
|
||||
|
||||
const char *class_name = (ide_devices[idx].type == IDE_TYPE_ATA)
|
||||
? "hdd" : "cd";
|
||||
const char *type_str = (ide_devices[idx].type == IDE_TYPE_ATA)
|
||||
? "ATA" : "ATAPI";
|
||||
|
||||
offset_print(" IDE: ");
|
||||
offset_print(type_str);
|
||||
offset_print(" device on ");
|
||||
offset_print(ch == 0 ? "primary" : "secondary");
|
||||
offset_print(drv == 0 ? " master" : " slave");
|
||||
offset_print(": ");
|
||||
offset_print(ide_devices[idx].model);
|
||||
offset_print(" (");
|
||||
print_hex(ide_devices[idx].sector_count);
|
||||
offset_print(" sectors)\n");
|
||||
|
||||
/* Register with devicefs */
|
||||
devicefs_register_block(class_name, &ide_block_ops,
|
||||
&ide_devices[idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found == 0) {
|
||||
offset_print(" IDE: no devices found\n");
|
||||
} else {
|
||||
offset_print(" IDE: ");
|
||||
print_hex((uint32_t)found);
|
||||
offset_print(" device(s) found\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ide_init(void) {
|
||||
return ide_driver_init();
|
||||
}
|
||||
|
||||
ide_device_t *ide_get_device(int index) {
|
||||
if (index < 0 || index >= IDE_MAX_DEVICES) return NULL;
|
||||
if (!ide_devices[index].present) return NULL;
|
||||
return &ide_devices[index];
|
||||
}
|
||||
|
||||
/** IDE driver descriptor. */
|
||||
static const driver_t ide_driver = {
|
||||
.name = "ide",
|
||||
.probe = ide_probe,
|
||||
.init = ide_driver_init,
|
||||
};
|
||||
|
||||
REGISTER_DRIVER(ide_driver);
|
||||
88
src/ide.h
Normal file
88
src/ide.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* @file ide.h
|
||||
* @brief IDE/ATA disk driver.
|
||||
*
|
||||
* Enumerates IDE devices on the primary and secondary channels, identifies
|
||||
* ATA hard drives and ATAPI CD-ROMs, and registers them with the devicefs
|
||||
* subsystem as block devices (hddN / cdN).
|
||||
*/
|
||||
|
||||
#ifndef IDE_H
|
||||
#define IDE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/** Maximum number of IDE devices (2 channels × 2 drives). */
|
||||
#define IDE_MAX_DEVICES 4
|
||||
|
||||
/** IDE channel I/O port bases. */
|
||||
#define IDE_PRIMARY_IO 0x1F0
|
||||
#define IDE_PRIMARY_CTRL 0x3F6
|
||||
#define IDE_SECONDARY_IO 0x170
|
||||
#define IDE_SECONDARY_CTRL 0x376
|
||||
|
||||
/** IDE register offsets from I/O base. */
|
||||
#define IDE_REG_DATA 0x00
|
||||
#define IDE_REG_ERROR 0x01
|
||||
#define IDE_REG_FEATURES 0x01
|
||||
#define IDE_REG_SECCOUNT 0x02
|
||||
#define IDE_REG_LBA_LO 0x03
|
||||
#define IDE_REG_LBA_MID 0x04
|
||||
#define IDE_REG_LBA_HI 0x05
|
||||
#define IDE_REG_DRIVE_HEAD 0x06
|
||||
#define IDE_REG_STATUS 0x07
|
||||
#define IDE_REG_COMMAND 0x07
|
||||
|
||||
/** IDE status register bits. */
|
||||
#define IDE_STATUS_ERR 0x01 /**< Error occurred. */
|
||||
#define IDE_STATUS_DRQ 0x08 /**< Data request ready. */
|
||||
#define IDE_STATUS_SRV 0x10 /**< Overlapped mode service request. */
|
||||
#define IDE_STATUS_DF 0x20 /**< Drive fault. */
|
||||
#define IDE_STATUS_DRDY 0x40 /**< Drive ready. */
|
||||
#define IDE_STATUS_BSY 0x80 /**< Drive busy. */
|
||||
|
||||
/** IDE commands. */
|
||||
#define IDE_CMD_IDENTIFY 0xEC /**< ATA IDENTIFY DEVICE. */
|
||||
#define IDE_CMD_IDENTIFY_PKT 0xA1 /**< ATAPI IDENTIFY PACKET DEVICE. */
|
||||
#define IDE_CMD_READ_PIO 0x20 /**< Read sectors (PIO, 28-bit LBA). */
|
||||
#define IDE_CMD_WRITE_PIO 0x30 /**< Write sectors (PIO, 28-bit LBA). */
|
||||
|
||||
/** IDE device types. */
|
||||
#define IDE_TYPE_NONE 0 /**< No device present. */
|
||||
#define IDE_TYPE_ATA 1 /**< ATA hard disk. */
|
||||
#define IDE_TYPE_ATAPI 2 /**< ATAPI CD/DVD drive. */
|
||||
|
||||
/**
|
||||
* IDE device descriptor.
|
||||
*/
|
||||
typedef struct ide_device {
|
||||
uint8_t present; /**< 1 if device is present. */
|
||||
uint8_t type; /**< IDE_TYPE_ATA or IDE_TYPE_ATAPI. */
|
||||
uint8_t channel; /**< 0 = primary, 1 = secondary. */
|
||||
uint8_t drive; /**< 0 = master, 1 = slave. */
|
||||
uint16_t io_base; /**< I/O base port for this channel. */
|
||||
uint16_t ctrl_base; /**< Control port for this channel. */
|
||||
uint32_t sector_count; /**< Total sectors (28-bit LBA max). */
|
||||
uint32_t sector_size; /**< Sector size in bytes (usually 512). */
|
||||
char model[41]; /**< Model string from IDENTIFY. */
|
||||
} ide_device_t;
|
||||
|
||||
/**
|
||||
* Initialize the IDE driver.
|
||||
*
|
||||
* Scans primary and secondary channels for ATA/ATAPI devices,
|
||||
* reads their IDENTIFY data, and registers them with devicefs.
|
||||
*
|
||||
* @return Number of devices found.
|
||||
*/
|
||||
int ide_init(void);
|
||||
|
||||
/**
|
||||
* Get an IDE device by index (0–3).
|
||||
*
|
||||
* @param index Device index.
|
||||
* @return Pointer to the device descriptor, or NULL if invalid/not present.
|
||||
*/
|
||||
ide_device_t *ide_get_device(int index);
|
||||
|
||||
#endif /* IDE_H */
|
||||
10
src/kernel.c
10
src/kernel.c
@@ -17,6 +17,8 @@
|
||||
#include "cpio.h"
|
||||
#include "vfs.h"
|
||||
#include "initrd_fs.h"
|
||||
#include "devicefs.h"
|
||||
#include "mbr.h"
|
||||
#include "keyboard.h"
|
||||
#include "framebuffer.h"
|
||||
|
||||
@@ -247,6 +249,10 @@ void kernel_main(uint32_t magic, uint32_t addr) {
|
||||
}
|
||||
}
|
||||
|
||||
init_devicefs();
|
||||
EARLY_PRINT("DEV ");
|
||||
offset_print("Devicefs initialized\n");
|
||||
|
||||
init_tss();
|
||||
EARLY_PRINT("TSS ");
|
||||
offset_print("TSS initialized\n");
|
||||
@@ -281,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 */
|
||||
@@ -15,6 +15,18 @@ static inline uint8_t inb(uint16_t port)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void outw(uint16_t port, uint16_t val)
|
||||
{
|
||||
asm volatile ( "outw %w0, %w1" : : "a"(val), "Nd"(port) );
|
||||
}
|
||||
|
||||
static inline uint16_t inw(uint16_t port)
|
||||
{
|
||||
uint16_t ret;
|
||||
asm volatile ( "inw %w1, %w0" : "=a"(ret) : "Nd"(port) );
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void io_wait(void)
|
||||
{
|
||||
/* Port 0x80 is used for 'checkpoints' during POST. */
|
||||
|
||||
Reference in New Issue
Block a user