mirror of
https://github.com/JannisHeydemann/BoredOS.git
synced 2026-05-30 10:26:59 +00:00
CHECKP: shitty VFS
This commit is contained in:
509
src/dev/ahci.c
Normal file
509
src/dev/ahci.c
Normal file
@@ -0,0 +1,509 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include "ahci.h"
|
||||
#include "pci.h"
|
||||
#include "disk.h"
|
||||
#include "memory_manager.h"
|
||||
#include "paging.h"
|
||||
#include "io.h"
|
||||
#include <stddef.h>
|
||||
|
||||
extern void serial_write(const char *str);
|
||||
extern void serial_write_num(uint64_t num);
|
||||
extern void serial_write_hex(uint32_t val);
|
||||
|
||||
// ============================================================================
|
||||
// AHCI Driver State
|
||||
// ============================================================================
|
||||
|
||||
static HBA_MEM *abar = NULL; // MMIO-mapped AHCI Base Address
|
||||
static bool ahci_initialized = false;
|
||||
static int active_port_count = 0;
|
||||
|
||||
#define MAX_AHCI_PORTS 32
|
||||
|
||||
typedef struct {
|
||||
bool active;
|
||||
int port_num;
|
||||
HBA_PORT *port;
|
||||
HBA_CMD_HEADER *cmd_list; // 1KB, 1KB aligned
|
||||
void *fis_base; // 256B, 256B aligned
|
||||
HBA_CMD_TBL *cmd_tbl; // Command table for slot 0
|
||||
} ahci_port_state_t;
|
||||
|
||||
static ahci_port_state_t ports[MAX_AHCI_PORTS];
|
||||
|
||||
// ============================================================================
|
||||
// String Helpers
|
||||
// ============================================================================
|
||||
|
||||
static void ahci_strcpy(char *d, const char *s) {
|
||||
while ((*d++ = *s++));
|
||||
}
|
||||
|
||||
// Kernel virtual to physical address conversion
|
||||
extern uint64_t v2p(uint64_t vaddr);
|
||||
|
||||
// ============================================================================
|
||||
// Port Setup
|
||||
// ============================================================================
|
||||
|
||||
static void ahci_stop_cmd(HBA_PORT *port) {
|
||||
// Clear ST (Start)
|
||||
port->cmd &= ~HBA_PORT_CMD_ST;
|
||||
|
||||
// Clear FRE (FIS Receive Enable)
|
||||
port->cmd &= ~HBA_PORT_CMD_FRE;
|
||||
|
||||
// Wait until FR and CR clear
|
||||
int timeout = 500000;
|
||||
while (timeout-- > 0) {
|
||||
if (port->cmd & HBA_PORT_CMD_FR) continue;
|
||||
if (port->cmd & HBA_PORT_CMD_CR) continue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ahci_start_cmd(HBA_PORT *port) {
|
||||
// Wait until CR clears
|
||||
while (port->cmd & HBA_PORT_CMD_CR);
|
||||
|
||||
// Set FRE and ST
|
||||
port->cmd |= HBA_PORT_CMD_FRE;
|
||||
port->cmd |= HBA_PORT_CMD_ST;
|
||||
}
|
||||
|
||||
static int ahci_check_port_type(HBA_PORT *port) {
|
||||
uint32_t ssts = port->ssts;
|
||||
uint8_t ipm = (ssts >> 8) & 0x0F;
|
||||
uint8_t det = ssts & 0x0F;
|
||||
|
||||
if (det != 3) return -1; // No device detected
|
||||
if (ipm != 1) return -1; // Not in active state
|
||||
|
||||
switch (port->sig) {
|
||||
case SATA_SIG_ATA: return 0; // SATA drive
|
||||
case SATA_SIG_ATAPI: return 1; // SATAPI drive
|
||||
case SATA_SIG_SEMB: return 2; // SEMB
|
||||
case SATA_SIG_PM: return 3; // Port multiplier
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void ahci_port_rebase(ahci_port_state_t *ps) {
|
||||
HBA_PORT *port = ps->port;
|
||||
|
||||
ahci_stop_cmd(port);
|
||||
|
||||
// Allocate command list (1KB, 1024-byte aligned)
|
||||
ps->cmd_list = (HBA_CMD_HEADER*)kmalloc_aligned(1024, 1024);
|
||||
if (!ps->cmd_list) return;
|
||||
mem_memset(ps->cmd_list, 0, 1024);
|
||||
|
||||
uint64_t clb_phys = v2p((uint64_t)ps->cmd_list);
|
||||
port->clb = (uint32_t)(clb_phys & 0xFFFFFFFF);
|
||||
port->clbu = (uint32_t)(clb_phys >> 32);
|
||||
|
||||
// Allocate FIS receive area (256 bytes, 256-byte aligned)
|
||||
ps->fis_base = kmalloc_aligned(256, 256);
|
||||
if (!ps->fis_base) return;
|
||||
mem_memset(ps->fis_base, 0, 256);
|
||||
|
||||
uint64_t fb_phys = v2p((uint64_t)ps->fis_base);
|
||||
port->fb = (uint32_t)(fb_phys & 0xFFFFFFFF);
|
||||
port->fbu = (uint32_t)(fb_phys >> 32);
|
||||
|
||||
// Allocate command table for slot 0 (256-byte aligned, room for 8 PRDT entries)
|
||||
int cmd_tbl_size = sizeof(HBA_CMD_TBL) + 8 * sizeof(HBA_PRDT_ENTRY);
|
||||
ps->cmd_tbl = (HBA_CMD_TBL*)kmalloc_aligned(cmd_tbl_size, 256);
|
||||
if (!ps->cmd_tbl) return;
|
||||
mem_memset(ps->cmd_tbl, 0, cmd_tbl_size);
|
||||
|
||||
// Set command header 0 to point to our command table
|
||||
uint64_t ctba_phys = v2p((uint64_t)ps->cmd_tbl);
|
||||
ps->cmd_list[0].ctba = (uint32_t)(ctba_phys & 0xFFFFFFFF);
|
||||
ps->cmd_list[0].ctbau = (uint32_t)(ctba_phys >> 32);
|
||||
ps->cmd_list[0].prdtl = 1; // 1 PRDT entry default
|
||||
|
||||
// Clear error and interrupt status
|
||||
port->serr = 0xFFFFFFFF;
|
||||
port->is = 0xFFFFFFFF;
|
||||
|
||||
ahci_start_cmd(port);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Sector I/O
|
||||
// ============================================================================
|
||||
|
||||
static int ahci_find_free_slot(HBA_PORT *port) {
|
||||
uint32_t slots = (port->sact | port->ci);
|
||||
for (int i = 0; i < 32; i++) {
|
||||
if (!(slots & (1 << i))) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ahci_read_sectors(int port_num, uint64_t lba, uint32_t count, uint8_t *buffer) {
|
||||
if (!ahci_initialized || port_num < 0 || port_num >= MAX_AHCI_PORTS) return -1;
|
||||
ahci_port_state_t *ps = &ports[port_num];
|
||||
if (!ps->active) return -1;
|
||||
|
||||
HBA_PORT *port = ps->port;
|
||||
|
||||
// Clear any pending interrupts/errors
|
||||
port->is = 0xFFFFFFFF;
|
||||
|
||||
int slot = ahci_find_free_slot(port);
|
||||
if (slot < 0) return -1;
|
||||
|
||||
HBA_CMD_HEADER *cmd_hdr = &ps->cmd_list[slot];
|
||||
cmd_hdr->cfl = sizeof(FIS_REG_H2D) / sizeof(uint32_t);
|
||||
cmd_hdr->w = 0; // Read
|
||||
cmd_hdr->prdtl = 1;
|
||||
|
||||
HBA_CMD_TBL *cmd_tbl = ps->cmd_tbl;
|
||||
mem_memset(cmd_tbl, 0, sizeof(HBA_CMD_TBL) + sizeof(HBA_PRDT_ENTRY));
|
||||
|
||||
// Setup PRDT
|
||||
uint64_t buf_phys = v2p((uint64_t)buffer);
|
||||
cmd_tbl->prdt[0].dba = (uint32_t)(buf_phys & 0xFFFFFFFF);
|
||||
cmd_tbl->prdt[0].dbau = (uint32_t)(buf_phys >> 32);
|
||||
cmd_tbl->prdt[0].dbc = (count * 512) - 1; // 0-based byte count
|
||||
cmd_tbl->prdt[0].i = 1;
|
||||
|
||||
// Setup Command FIS
|
||||
FIS_REG_H2D *fis = (FIS_REG_H2D*)&cmd_tbl->cfis;
|
||||
fis->fis_type = FIS_TYPE_REG_H2D;
|
||||
fis->c = 1; // Command
|
||||
fis->command = ATA_CMD_READ_DMA_EX;
|
||||
|
||||
fis->lba0 = (uint8_t)(lba);
|
||||
fis->lba1 = (uint8_t)(lba >> 8);
|
||||
fis->lba2 = (uint8_t)(lba >> 16);
|
||||
fis->device = 1 << 6; // LBA mode
|
||||
fis->lba3 = (uint8_t)(lba >> 24);
|
||||
fis->lba4 = (uint8_t)(lba >> 32);
|
||||
fis->lba5 = (uint8_t)(lba >> 40);
|
||||
|
||||
fis->countl = (uint8_t)(count);
|
||||
fis->counth = (uint8_t)(count >> 8);
|
||||
|
||||
// Issue command
|
||||
port->ci = (1 << slot);
|
||||
|
||||
// Wait for completion
|
||||
int timeout = 1000000;
|
||||
while (timeout-- > 0) {
|
||||
if (!(port->ci & (1 << slot))) break;
|
||||
if (port->is & (1 << 30)) { // Task File Error
|
||||
serial_write("[AHCI] Read error on port ");
|
||||
serial_write_num(port_num);
|
||||
serial_write("\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (timeout <= 0) {
|
||||
serial_write("[AHCI] Read timeout on port ");
|
||||
serial_write_num(port_num);
|
||||
serial_write("\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ahci_write_sectors(int port_num, uint64_t lba, uint32_t count, const uint8_t *buffer) {
|
||||
if (!ahci_initialized || port_num < 0 || port_num >= MAX_AHCI_PORTS) return -1;
|
||||
ahci_port_state_t *ps = &ports[port_num];
|
||||
if (!ps->active) return -1;
|
||||
|
||||
HBA_PORT *port = ps->port;
|
||||
|
||||
port->is = 0xFFFFFFFF;
|
||||
|
||||
int slot = ahci_find_free_slot(port);
|
||||
if (slot < 0) return -1;
|
||||
|
||||
HBA_CMD_HEADER *cmd_hdr = &ps->cmd_list[slot];
|
||||
cmd_hdr->cfl = sizeof(FIS_REG_H2D) / sizeof(uint32_t);
|
||||
cmd_hdr->w = 1; // Write
|
||||
cmd_hdr->prdtl = 1;
|
||||
|
||||
HBA_CMD_TBL *cmd_tbl = ps->cmd_tbl;
|
||||
mem_memset(cmd_tbl, 0, sizeof(HBA_CMD_TBL) + sizeof(HBA_PRDT_ENTRY));
|
||||
|
||||
uint64_t buf_phys = v2p((uint64_t)buffer);
|
||||
cmd_tbl->prdt[0].dba = (uint32_t)(buf_phys & 0xFFFFFFFF);
|
||||
cmd_tbl->prdt[0].dbau = (uint32_t)(buf_phys >> 32);
|
||||
cmd_tbl->prdt[0].dbc = (count * 512) - 1;
|
||||
cmd_tbl->prdt[0].i = 1;
|
||||
|
||||
FIS_REG_H2D *fis = (FIS_REG_H2D*)&cmd_tbl->cfis;
|
||||
fis->fis_type = FIS_TYPE_REG_H2D;
|
||||
fis->c = 1;
|
||||
fis->command = ATA_CMD_WRITE_DMA_EX;
|
||||
|
||||
fis->lba0 = (uint8_t)(lba);
|
||||
fis->lba1 = (uint8_t)(lba >> 8);
|
||||
fis->lba2 = (uint8_t)(lba >> 16);
|
||||
fis->device = 1 << 6;
|
||||
fis->lba3 = (uint8_t)(lba >> 24);
|
||||
fis->lba4 = (uint8_t)(lba >> 32);
|
||||
fis->lba5 = (uint8_t)(lba >> 40);
|
||||
|
||||
fis->countl = (uint8_t)(count);
|
||||
fis->counth = (uint8_t)(count >> 8);
|
||||
|
||||
port->ci = (1 << slot);
|
||||
|
||||
int timeout = 1000000;
|
||||
while (timeout-- > 0) {
|
||||
if (!(port->ci & (1 << slot))) break;
|
||||
if (port->is & (1 << 30)) {
|
||||
serial_write("[AHCI] Write error on port ");
|
||||
serial_write_num(port_num);
|
||||
serial_write("\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (timeout <= 0) {
|
||||
serial_write("[AHCI] Write timeout on port ");
|
||||
serial_write_num(port_num);
|
||||
serial_write("\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// AHCI Disk Integration — wrap AHCI into Disk read/write_sector
|
||||
// ============================================================================
|
||||
|
||||
typedef struct {
|
||||
int ahci_port;
|
||||
} AHCIDriverData;
|
||||
|
||||
static int ahci_disk_read_sector(Disk *disk, uint32_t sector, uint8_t *buffer) {
|
||||
AHCIDriverData *data = (AHCIDriverData*)disk->driver_data;
|
||||
|
||||
// For partitions, add offset and use parent's port
|
||||
if (disk->is_partition && disk->parent) {
|
||||
AHCIDriverData *pdata = (AHCIDriverData*)disk->parent->driver_data;
|
||||
return ahci_read_sectors(pdata->ahci_port,
|
||||
(uint64_t)sector + disk->partition_lba_offset, 1, buffer);
|
||||
}
|
||||
|
||||
return ahci_read_sectors(data->ahci_port, (uint64_t)sector, 1, buffer);
|
||||
}
|
||||
|
||||
static int ahci_disk_write_sector(Disk *disk, uint32_t sector, const uint8_t *buffer) {
|
||||
AHCIDriverData *data = (AHCIDriverData*)disk->driver_data;
|
||||
|
||||
if (disk->is_partition && disk->parent) {
|
||||
AHCIDriverData *pdata = (AHCIDriverData*)disk->parent->driver_data;
|
||||
return ahci_write_sectors(pdata->ahci_port,
|
||||
(uint64_t)sector + disk->partition_lba_offset, 1, buffer);
|
||||
}
|
||||
|
||||
return ahci_write_sectors(data->ahci_port, (uint64_t)sector, 1, buffer);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Initialization
|
||||
// ============================================================================
|
||||
|
||||
int ahci_get_port_count(void) {
|
||||
return active_port_count;
|
||||
}
|
||||
|
||||
bool ahci_port_is_active(int port_num) {
|
||||
if (port_num < 0 || port_num >= MAX_AHCI_PORTS) return false;
|
||||
return ports[port_num].active;
|
||||
}
|
||||
|
||||
void ahci_init(void) {
|
||||
serial_write("[AHCI] Scanning PCI for AHCI controller...\n");
|
||||
|
||||
// Find AHCI controller (Class 0x01, Subclass 0x06)
|
||||
pci_device_t pci_dev;
|
||||
if (!pci_find_device_by_class(PCI_CLASS_MASS_STORAGE, PCI_SUBCLASS_SATA, &pci_dev)) {
|
||||
serial_write("[AHCI] No AHCI controller found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
serial_write("[AHCI] Found AHCI controller (");
|
||||
serial_write("vendor=0x");
|
||||
serial_write_hex(pci_dev.vendor_id);
|
||||
serial_write(", device=0x");
|
||||
serial_write_hex(pci_dev.device_id);
|
||||
serial_write(")\n");
|
||||
|
||||
// Enable Bus Mastering and MMIO
|
||||
pci_enable_bus_mastering(&pci_dev);
|
||||
pci_enable_mmio(&pci_dev);
|
||||
|
||||
// Read ABAR (BAR5)
|
||||
uint32_t abar_raw = pci_get_bar(&pci_dev, 5);
|
||||
uint64_t abar_phys = abar_raw & 0xFFFFF000; // Mask out lower bits
|
||||
|
||||
if (abar_phys == 0) {
|
||||
serial_write("[AHCI] Invalid ABAR address\n");
|
||||
return;
|
||||
}
|
||||
|
||||
serial_write("[AHCI] ABAR physical address: 0x");
|
||||
serial_write_hex((uint32_t)abar_phys);
|
||||
serial_write("\n");
|
||||
|
||||
// Map ABAR region into kernel virtual address space
|
||||
// Identity-map several pages to cover the HBA memory (at least 0x1100 bytes)
|
||||
uint64_t abar_virt = abar_phys; // Use identity mapping
|
||||
for (uint64_t offset = 0; offset < 0x2000; offset += 4096) {
|
||||
paging_map_page(paging_get_pml4_phys(), abar_virt + offset,
|
||||
abar_phys + offset,
|
||||
PT_PRESENT | PT_RW | PT_CACHE_DISABLE);
|
||||
}
|
||||
|
||||
abar = (HBA_MEM*)abar_virt;
|
||||
|
||||
// Enable AHCI mode
|
||||
abar->ghc |= (1 << 31); // AE (AHCI Enable)
|
||||
|
||||
serial_write("[AHCI] Version: ");
|
||||
serial_write_num(abar->vs >> 16);
|
||||
serial_write(".");
|
||||
serial_write_num(abar->vs & 0xFFFF);
|
||||
serial_write("\n");
|
||||
|
||||
// Probe ports
|
||||
uint32_t pi = abar->pi;
|
||||
active_port_count = 0;
|
||||
|
||||
for (int i = 0; i < 32; i++) {
|
||||
ports[i].active = false;
|
||||
|
||||
if (!(pi & (1 << i))) continue;
|
||||
|
||||
HBA_PORT *port = &abar->ports[i];
|
||||
int type = ahci_check_port_type(port);
|
||||
|
||||
if (type == 0) { // SATA drive
|
||||
serial_write("[AHCI] Port ");
|
||||
serial_write_num(i);
|
||||
serial_write(": SATA drive detected\n");
|
||||
|
||||
ports[i].port_num = i;
|
||||
ports[i].port = port;
|
||||
ahci_port_rebase(&ports[i]);
|
||||
ports[i].active = true;
|
||||
active_port_count++;
|
||||
|
||||
// Register as a block device
|
||||
Disk *disk = (Disk*)kmalloc(sizeof(Disk));
|
||||
if (disk) {
|
||||
AHCIDriverData *drv = (AHCIDriverData*)kmalloc(sizeof(AHCIDriverData));
|
||||
drv->ahci_port = i;
|
||||
|
||||
disk->devname[0] = 0; // Auto-assign
|
||||
disk->type = DISK_TYPE_SATA;
|
||||
ahci_strcpy(disk->label, "SATA Drive");
|
||||
disk->read_sector = ahci_disk_read_sector;
|
||||
disk->write_sector = ahci_disk_write_sector;
|
||||
disk->driver_data = drv;
|
||||
disk->partition_lba_offset = 0;
|
||||
disk->total_sectors = 0;
|
||||
disk->parent = NULL;
|
||||
disk->is_partition = false;
|
||||
disk->is_fat32 = false;
|
||||
|
||||
disk_register(disk);
|
||||
|
||||
// Let disk_manager parse partitions — we call a scan function
|
||||
extern void disk_manager_scan_partitions(Disk *disk);
|
||||
// Inline MBR parse for this disk
|
||||
extern void serial_write(const char *str);
|
||||
serial_write("[AHCI] Probing partitions on /dev/");
|
||||
serial_write(disk->devname);
|
||||
serial_write("...\n");
|
||||
|
||||
// Read MBR sector 0
|
||||
uint8_t *mbr_buf = (uint8_t*)kmalloc(512);
|
||||
if (mbr_buf) {
|
||||
if (ahci_disk_read_sector(disk, 0, mbr_buf) == 0) {
|
||||
if (mbr_buf[510] == 0x55 && mbr_buf[511] == 0xAA) {
|
||||
// Parse MBR partition table
|
||||
typedef struct {
|
||||
uint8_t status;
|
||||
uint8_t chs_first[3];
|
||||
uint8_t type;
|
||||
uint8_t chs_last[3];
|
||||
uint32_t lba_start;
|
||||
uint32_t sector_count;
|
||||
} __attribute__((packed)) MBR_Part;
|
||||
|
||||
MBR_Part *parts = (MBR_Part*)&mbr_buf[446];
|
||||
int pn = 1;
|
||||
for (int p = 0; p < 4; p++) {
|
||||
if (parts[p].type == 0x00 || parts[p].sector_count == 0)
|
||||
continue;
|
||||
|
||||
bool fat32 = false;
|
||||
if (parts[p].type == 0x0B || parts[p].type == 0x0C) {
|
||||
// Verify BPB
|
||||
uint8_t *pbuf = (uint8_t*)kmalloc(512);
|
||||
if (pbuf) {
|
||||
if (ahci_disk_read_sector(disk, parts[p].lba_start, pbuf) == 0) {
|
||||
if (pbuf[510] == 0x55 && pbuf[511] == 0xAA) {
|
||||
uint16_t bps = *(uint16_t*)&pbuf[11];
|
||||
uint16_t spf16 = *(uint16_t*)&pbuf[22];
|
||||
uint32_t spf32 = *(uint32_t*)&pbuf[36];
|
||||
if (bps == 512 && spf16 == 0 && spf32 > 0)
|
||||
fat32 = true;
|
||||
}
|
||||
}
|
||||
kfree(pbuf);
|
||||
}
|
||||
}
|
||||
|
||||
disk_register_partition(disk, parts[p].lba_start,
|
||||
parts[p].sector_count, fat32, pn);
|
||||
pn++;
|
||||
}
|
||||
|
||||
// Fallback: raw FAT32
|
||||
if (pn == 1) {
|
||||
uint16_t bps = *(uint16_t*)&mbr_buf[11];
|
||||
uint16_t spf16 = *(uint16_t*)&mbr_buf[22];
|
||||
uint32_t spf32 = *(uint32_t*)&mbr_buf[36];
|
||||
if (bps == 512 && spf16 == 0 && spf32 > 0) {
|
||||
disk->is_fat32 = true;
|
||||
disk->partition_lba_offset = 0;
|
||||
serial_write("[AHCI] Raw FAT32 volume detected\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
kfree(mbr_buf);
|
||||
}
|
||||
}
|
||||
} else if (type == 1) {
|
||||
serial_write("[AHCI] Port ");
|
||||
serial_write_num(i);
|
||||
serial_write(": SATAPI drive (ignored)\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (active_port_count > 0) {
|
||||
ahci_initialized = true;
|
||||
serial_write("[AHCI] Initialization complete: ");
|
||||
serial_write_num(active_port_count);
|
||||
serial_write(" SATA port(s) active\n");
|
||||
} else {
|
||||
serial_write("[AHCI] No active SATA ports found\n");
|
||||
}
|
||||
}
|
||||
174
src/dev/ahci.h
Normal file
174
src/dev/ahci.h
Normal file
@@ -0,0 +1,174 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#ifndef AHCI_H
|
||||
#define AHCI_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// ============================================================================
|
||||
// FIS (Frame Information Structure) Types
|
||||
// ============================================================================
|
||||
|
||||
typedef enum {
|
||||
FIS_TYPE_REG_H2D = 0x27, // Register FIS — Host to Device
|
||||
FIS_TYPE_REG_D2H = 0x34, // Register FIS — Device to Host
|
||||
FIS_TYPE_DMA_ACT = 0x39, // DMA Activate FIS
|
||||
FIS_TYPE_DMA_SETUP = 0x41, // DMA Setup FIS
|
||||
FIS_TYPE_DATA = 0x46, // Data FIS
|
||||
FIS_TYPE_BIST = 0x58, // BIST Activate FIS
|
||||
FIS_TYPE_PIO_SETUP = 0x5F, // PIO Setup FIS
|
||||
FIS_TYPE_DEV_BITS = 0xA1, // Set Device Bits FIS
|
||||
} FIS_TYPE;
|
||||
|
||||
// ============================================================================
|
||||
// HBA Register Structures (MMIO-mapped from ABAR)
|
||||
// ============================================================================
|
||||
|
||||
// Port Registers (one set per port, at ABAR + 0x100 + portno*0x80)
|
||||
typedef volatile struct {
|
||||
uint32_t clb; // 0x00: Command List Base Address (lower 32 bits)
|
||||
uint32_t clbu; // 0x04: Command List Base Address (upper 32 bits)
|
||||
uint32_t fb; // 0x08: FIS Base Address (lower 32 bits)
|
||||
uint32_t fbu; // 0x0C: FIS Base Address (upper 32 bits)
|
||||
uint32_t is; // 0x10: Interrupt Status
|
||||
uint32_t ie; // 0x14: Interrupt Enable
|
||||
uint32_t cmd; // 0x18: Command and Status
|
||||
uint32_t rsv0; // 0x1C: Reserved
|
||||
uint32_t tfd; // 0x20: Task File Data
|
||||
uint32_t sig; // 0x24: Signature
|
||||
uint32_t ssts; // 0x28: SATA Status (SStatus)
|
||||
uint32_t sctl; // 0x2C: SATA Control (SControl)
|
||||
uint32_t serr; // 0x30: SATA Error (SError)
|
||||
uint32_t sact; // 0x34: SATA Active (SCR3)
|
||||
uint32_t ci; // 0x38: Command Issue
|
||||
uint32_t sntf; // 0x3C: SATA Notification (SCR4)
|
||||
uint32_t fbs; // 0x40: FIS-based Switch Control
|
||||
uint32_t rsv1[11]; // 0x44~0x6F
|
||||
uint32_t vendor[4]; // 0x70~0x7F
|
||||
} HBA_PORT;
|
||||
|
||||
// Global HBA Memory Registers (at ABAR)
|
||||
typedef volatile struct {
|
||||
uint32_t cap; // 0x00: Host Capability
|
||||
uint32_t ghc; // 0x04: Global Host Control
|
||||
uint32_t is; // 0x08: Interrupt Status
|
||||
uint32_t pi; // 0x0C: Port Implemented
|
||||
uint32_t vs; // 0x10: Version
|
||||
uint32_t ccc_ctl; // 0x14: Command Completion Coalescing Control
|
||||
uint32_t ccc_pts; // 0x18: Command Completion Coalescing Ports
|
||||
uint32_t em_loc; // 0x1C: Enclosure Management Location
|
||||
uint32_t em_ctl; // 0x20: Enclosure Management Control
|
||||
uint32_t cap2; // 0x24: Host Capabilities Extended
|
||||
uint32_t bohc; // 0x28: BIOS/OS Handoff Control and Status
|
||||
uint8_t rsv[0xA0 - 0x2C];
|
||||
uint8_t vendor[0x100 - 0xA0];
|
||||
HBA_PORT ports[]; // Port 0 at offset 0x100 (flexible array member)
|
||||
} HBA_MEM;
|
||||
|
||||
// ============================================================================
|
||||
// Command List / Table Structures (DMA)
|
||||
// ============================================================================
|
||||
|
||||
// Command Header (32 bytes each, 32 entries per port = 1KB)
|
||||
typedef struct {
|
||||
uint8_t cfl:5; // Command FIS Length (in DWORDs)
|
||||
uint8_t a:1; // ATAPI
|
||||
uint8_t w:1; // Write (1=H2D, 0=D2H)
|
||||
uint8_t p:1; // Prefetchable
|
||||
|
||||
uint8_t r:1; // Reset
|
||||
uint8_t b:1; // BIST
|
||||
uint8_t c:1; // Clear Busy upon R_OK
|
||||
uint8_t rsv0:1;
|
||||
uint8_t pmp:4; // Port Multiplier Port
|
||||
|
||||
uint16_t prdtl; // Physical Region Descriptor Table Length (entries)
|
||||
|
||||
volatile uint32_t prdbc; // PRD Byte Count transferred
|
||||
|
||||
uint32_t ctba; // Command Table Descriptor Base Address (lower 32)
|
||||
uint32_t ctbau; // Command Table Descriptor Base Address (upper 32)
|
||||
|
||||
uint32_t rsv1[4]; // Reserved
|
||||
} __attribute__((packed)) HBA_CMD_HEADER;
|
||||
|
||||
// Physical Region Descriptor Table Entry
|
||||
typedef struct {
|
||||
uint32_t dba; // Data Base Address (lower 32)
|
||||
uint32_t dbau; // Data Base Address (upper 32)
|
||||
uint32_t rsv0; // Reserved
|
||||
uint32_t dbc:22; // Byte Count (0-based, max 4MB)
|
||||
uint32_t rsv1:9; // Reserved
|
||||
uint32_t i:1; // Interrupt on Completion
|
||||
} __attribute__((packed)) HBA_PRDT_ENTRY;
|
||||
|
||||
// Host-to-Device Register FIS
|
||||
typedef struct {
|
||||
uint8_t fis_type; // FIS_TYPE_REG_H2D
|
||||
uint8_t pmport:4; // Port Multiplier
|
||||
uint8_t rsv0:3; // Reserved
|
||||
uint8_t c:1; // 1=Command, 0=Control
|
||||
uint8_t command; // Command register
|
||||
uint8_t featurel; // Feature register (7:0)
|
||||
uint8_t lba0; // LBA (7:0)
|
||||
uint8_t lba1; // LBA (15:8)
|
||||
uint8_t lba2; // LBA (23:16)
|
||||
uint8_t device; // Device register
|
||||
uint8_t lba3; // LBA (31:24)
|
||||
uint8_t lba4; // LBA (39:32)
|
||||
uint8_t lba5; // LBA (47:40)
|
||||
uint8_t featureh; // Feature register (15:8)
|
||||
uint8_t countl; // Count (7:0)
|
||||
uint8_t counth; // Count (15:8)
|
||||
uint8_t icc; // Isochronous Command Completion
|
||||
uint8_t control; // Control register
|
||||
uint8_t rsv1[4]; // Reserved
|
||||
} __attribute__((packed)) FIS_REG_H2D;
|
||||
|
||||
// Command Table (256-byte aligned)
|
||||
typedef struct {
|
||||
uint8_t cfis[64]; // Command FIS
|
||||
uint8_t acmd[16]; // ATAPI Command
|
||||
uint8_t rsv[48]; // Reserved
|
||||
HBA_PRDT_ENTRY prdt[]; // PRDT entries (variable, at least 1)
|
||||
} __attribute__((packed)) HBA_CMD_TBL;
|
||||
|
||||
// ============================================================================
|
||||
// Port Signature Values
|
||||
// ============================================================================
|
||||
|
||||
#define SATA_SIG_ATA 0x00000101 // SATA drive
|
||||
#define SATA_SIG_ATAPI 0xEB140101 // SATAPI drive
|
||||
#define SATA_SIG_SEMB 0xC33C0101 // Enclosure management bridge
|
||||
#define SATA_SIG_PM 0x96690101 // Port multiplier
|
||||
|
||||
// ============================================================================
|
||||
// Port Command Bits
|
||||
// ============================================================================
|
||||
|
||||
#define HBA_PORT_CMD_ST 0x0001 // Start
|
||||
#define HBA_PORT_CMD_FRE 0x0010 // FIS Receive Enable
|
||||
#define HBA_PORT_CMD_FR 0x4000 // FIS Receive Running
|
||||
#define HBA_PORT_CMD_CR 0x8000 // Command List Running
|
||||
|
||||
// ============================================================================
|
||||
// ATA Commands
|
||||
// ============================================================================
|
||||
|
||||
#define ATA_CMD_READ_DMA_EX 0x25
|
||||
#define ATA_CMD_WRITE_DMA_EX 0x35
|
||||
#define ATA_CMD_IDENTIFY 0xEC
|
||||
|
||||
// ============================================================================
|
||||
// Public API
|
||||
// ============================================================================
|
||||
|
||||
void ahci_init(void);
|
||||
int ahci_read_sectors(int port_num, uint64_t lba, uint32_t count, uint8_t *buffer);
|
||||
int ahci_write_sectors(int port_num, uint64_t lba, uint32_t count, const uint8_t *buffer);
|
||||
int ahci_get_port_count(void);
|
||||
bool ahci_port_is_active(int port_num);
|
||||
|
||||
#endif
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
#define SECTOR_SIZE 512
|
||||
#define MAX_DISKS 16
|
||||
|
||||
typedef enum {
|
||||
DISK_TYPE_RAM,
|
||||
@@ -17,26 +18,45 @@ typedef enum {
|
||||
} DiskType;
|
||||
|
||||
typedef struct Disk {
|
||||
char letter;
|
||||
char devname[16]; // Device name: "sda", "sdb", "sda1", etc.
|
||||
DiskType type;
|
||||
bool is_fat32;
|
||||
char name[32];
|
||||
uint32_t partition_lba_offset; // LBA offset of FAT32 partition (0 for raw)
|
||||
|
||||
char label[32]; // Human-readable label
|
||||
uint32_t partition_lba_offset; // LBA offset of partition (0 for whole disk)
|
||||
uint32_t total_sectors; // Total sectors on this device/partition
|
||||
|
||||
// Function pointers for driver operations
|
||||
int (*read_sector)(struct Disk *disk, uint32_t sector, uint8_t *buffer);
|
||||
int (*write_sector)(struct Disk *disk, uint32_t sector, const uint8_t *buffer);
|
||||
|
||||
|
||||
// Private driver data
|
||||
void *driver_data;
|
||||
void *driver_data;
|
||||
|
||||
// Parent disk (for partitions — points to the whole-disk Disk)
|
||||
struct Disk *parent;
|
||||
bool is_partition;
|
||||
bool registered;
|
||||
} Disk;
|
||||
|
||||
// Initialization and scanning
|
||||
void disk_manager_init(void);
|
||||
void disk_manager_scan(void); // Scans for new disks
|
||||
Disk* disk_get_by_letter(char letter);
|
||||
char disk_get_next_free_letter(void);
|
||||
void disk_manager_scan(void);
|
||||
|
||||
// Device registration
|
||||
void disk_register(Disk *disk);
|
||||
void disk_register_partition(Disk *parent, uint32_t lba_offset, uint32_t sector_count,
|
||||
bool is_fat32, int part_num);
|
||||
|
||||
// Lookup
|
||||
Disk* disk_get_by_name(const char *devname);
|
||||
int disk_get_count(void);
|
||||
Disk* disk_get_by_index(int index);
|
||||
|
||||
// Auto-naming helpers
|
||||
const char* disk_get_next_dev_name(void); // Returns "sda", "sdb", etc.
|
||||
|
||||
// Backward compat (deprecated — wraps disk_get_by_name)
|
||||
Disk* disk_get_by_letter(char letter);
|
||||
char disk_get_next_free_letter(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -5,15 +5,39 @@
|
||||
#include "pci.h"
|
||||
#include "memory_manager.h"
|
||||
#include "io.h"
|
||||
#include "wm.h"
|
||||
#include "wm.h"
|
||||
#include "ahci.h"
|
||||
#include "../fs/vfs.h"
|
||||
#include "../fs/fat32.h"
|
||||
#include <stddef.h>
|
||||
|
||||
#define MAX_DISKS 26
|
||||
|
||||
static Disk *disks[MAX_DISKS];
|
||||
static int disk_count = 0;
|
||||
static int next_drive_letter_idx = 0; // For backward compat
|
||||
static int next_sd_index = 0; // For sda, sdb, sdc...
|
||||
|
||||
// === ATA Definitions ===
|
||||
extern void serial_write(const char *str);
|
||||
extern void serial_write_num(uint64_t num);
|
||||
|
||||
// === String Helpers ===
|
||||
|
||||
static void dm_strcpy(char *dest, const char *src) {
|
||||
while (*src) *dest++ = *src++;
|
||||
*dest = 0;
|
||||
}
|
||||
|
||||
static int dm_strcmp(const char *a, const char *b) {
|
||||
while (*a && *a == *b) { a++; b++; }
|
||||
return (unsigned char)*a - (unsigned char)*b;
|
||||
}
|
||||
|
||||
static int dm_strlen(const char *s) {
|
||||
int n = 0;
|
||||
while (s[n]) n++;
|
||||
return n;
|
||||
}
|
||||
|
||||
// === ATA Definitions (Legacy IDE PIO — kept as fallback) ===
|
||||
|
||||
#define ATA_PRIMARY_IO 0x1F0
|
||||
#define ATA_PRIMARY_CTRL 0x3F6
|
||||
@@ -35,40 +59,21 @@ static int disk_count = 0;
|
||||
#define ATA_CMD_WRITE_PIO 0x30
|
||||
#define ATA_CMD_IDENTIFY 0xEC
|
||||
|
||||
#define ATA_SR_BSY 0x80 // Busy
|
||||
#define ATA_SR_DRDY 0x40 // Drive ready
|
||||
#define ATA_SR_DF 0x20 // Drive write fault
|
||||
#define ATA_SR_DSC 0x10 // Drive seek complete
|
||||
#define ATA_SR_DRQ 0x08 // Data request ready
|
||||
#define ATA_SR_CORR 0x04 // Corrected data
|
||||
#define ATA_SR_IDX 0x02 // Index
|
||||
#define ATA_SR_ERR 0x01 // Error
|
||||
#define ATA_SR_BSY 0x80
|
||||
#define ATA_SR_DRDY 0x40
|
||||
#define ATA_SR_DF 0x20
|
||||
#define ATA_SR_DSC 0x10
|
||||
#define ATA_SR_DRQ 0x08
|
||||
#define ATA_SR_CORR 0x04
|
||||
#define ATA_SR_IDX 0x02
|
||||
#define ATA_SR_ERR 0x01
|
||||
|
||||
typedef struct {
|
||||
uint16_t port_base;
|
||||
bool slave;
|
||||
} ATADriverData;
|
||||
|
||||
// === Helpers ===
|
||||
|
||||
static void dm_strcpy(char *dest, const char *src) {
|
||||
while (*src) *dest++ = *src++;
|
||||
*dest = 0;
|
||||
}
|
||||
|
||||
void disk_register(Disk *disk);
|
||||
|
||||
|
||||
static int ramdisk_read(Disk *disk, uint32_t sector, uint8_t *buffer) {
|
||||
(void)disk; (void)sector; (void)buffer;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ramdisk_write(Disk *disk, uint32_t sector, const uint8_t *buffer) {
|
||||
(void)disk; (void)sector; (void)buffer;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// === ATA PIO Driver ===
|
||||
|
||||
static void ata_wait_bsy(uint16_t port_base) {
|
||||
while (inb(port_base + ATA_REG_STATUS) & ATA_SR_BSY);
|
||||
@@ -78,58 +83,57 @@ static void ata_wait_drq(uint16_t port_base) {
|
||||
while (!(inb(port_base + ATA_REG_STATUS) & ATA_SR_DRQ));
|
||||
}
|
||||
|
||||
// Returns 1 if drive exists, 0 otherwise
|
||||
static int ata_identify(uint16_t port_base, bool slave) {
|
||||
// Select Drive
|
||||
outb(port_base + ATA_REG_HDDEVSEL, slave ? 0xB0 : 0xA0);
|
||||
// Zero out sector count and LBA registers
|
||||
outb(port_base + ATA_REG_SEC_COUNT0, 0);
|
||||
outb(port_base + ATA_REG_LBA0, 0);
|
||||
outb(port_base + ATA_REG_LBA1, 0);
|
||||
outb(port_base + ATA_REG_LBA2, 0);
|
||||
|
||||
// Send Identify command
|
||||
|
||||
outb(port_base + ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
|
||||
|
||||
// Check if status is 0 (no drive)
|
||||
|
||||
uint8_t status = inb(port_base + ATA_REG_STATUS);
|
||||
if (status == 0) return 0;
|
||||
|
||||
// Wait until BSY clears
|
||||
|
||||
int timeout = 10000;
|
||||
while ((inb(port_base + ATA_REG_STATUS) & ATA_SR_BSY) && --timeout > 0) {
|
||||
status = inb(port_base + ATA_REG_STATUS);
|
||||
if (status == 0) return 0; // Check again
|
||||
if (status == 0) return 0;
|
||||
}
|
||||
if (timeout <= 0) return 0; // Hardware didn't respond
|
||||
|
||||
// Check for error
|
||||
if (inb(port_base + ATA_REG_STATUS) & ATA_SR_ERR) {
|
||||
return 0; // Error, likely not ATA
|
||||
}
|
||||
|
||||
// Wait for DRQ or ERR
|
||||
while (!(inb(port_base + ATA_REG_STATUS) & (ATA_SR_DRQ | ATA_SR_ERR)));
|
||||
|
||||
if (timeout <= 0) return 0;
|
||||
|
||||
if (inb(port_base + ATA_REG_STATUS) & ATA_SR_ERR) return 0;
|
||||
|
||||
// Read 256 words (512 bytes) of identity data
|
||||
while (!(inb(port_base + ATA_REG_STATUS) & (ATA_SR_DRQ | ATA_SR_ERR)));
|
||||
|
||||
if (inb(port_base + ATA_REG_STATUS) & ATA_SR_ERR) return 0;
|
||||
|
||||
uint32_t sectors = 0;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
uint16_t data = inw(port_base + ATA_REG_DATA);
|
||||
(void)data;
|
||||
if (i == 60) sectors |= (uint32_t)data;
|
||||
if (i == 61) sectors |= (uint32_t)data << 16;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
return sectors;
|
||||
}
|
||||
|
||||
static int ata_read_sector(Disk *disk, uint32_t lba, uint8_t *buffer) {
|
||||
ATADriverData *data = (ATADriverData*)disk->driver_data;
|
||||
uint16_t port_base = data->port_base;
|
||||
bool slave = data->slave;
|
||||
|
||||
|
||||
// For partition reads, add the partition LBA offset
|
||||
if (disk->is_partition && disk->parent) {
|
||||
lba += disk->partition_lba_offset;
|
||||
// Use parent's driver
|
||||
data = (ATADriverData*)disk->parent->driver_data;
|
||||
port_base = data->port_base;
|
||||
slave = data->slave;
|
||||
}
|
||||
|
||||
ata_wait_bsy(port_base);
|
||||
|
||||
// Select drive and send highest 4 bits of LBA
|
||||
|
||||
outb(port_base + ATA_REG_HDDEVSEL, 0xE0 | (slave << 4) | ((lba >> 24) & 0x0F));
|
||||
outb(port_base + ATA_REG_FEATURES, 0x00);
|
||||
outb(port_base + ATA_REG_SEC_COUNT0, 1);
|
||||
@@ -137,25 +141,33 @@ static int ata_read_sector(Disk *disk, uint32_t lba, uint8_t *buffer) {
|
||||
outb(port_base + ATA_REG_LBA1, (uint8_t)(lba >> 8));
|
||||
outb(port_base + ATA_REG_LBA2, (uint8_t)(lba >> 16));
|
||||
outb(port_base + ATA_REG_COMMAND, ATA_CMD_READ_PIO);
|
||||
|
||||
|
||||
ata_wait_bsy(port_base);
|
||||
ata_wait_drq(port_base);
|
||||
|
||||
|
||||
uint16_t *ptr = (uint16_t*)buffer;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
ptr[i] = inw(port_base + ATA_REG_DATA);
|
||||
}
|
||||
|
||||
return 0; // Success
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ata_write_sector(Disk *disk, uint32_t lba, const uint8_t *buffer) {
|
||||
ATADriverData *data = (ATADriverData*)disk->driver_data;
|
||||
uint16_t port_base = data->port_base;
|
||||
bool slave = data->slave;
|
||||
|
||||
|
||||
// For partition writes, add the partition LBA offset
|
||||
if (disk->is_partition && disk->parent) {
|
||||
lba += disk->partition_lba_offset;
|
||||
data = (ATADriverData*)disk->parent->driver_data;
|
||||
port_base = data->port_base;
|
||||
slave = data->slave;
|
||||
}
|
||||
|
||||
ata_wait_bsy(port_base);
|
||||
|
||||
|
||||
outb(port_base + ATA_REG_HDDEVSEL, 0xE0 | (slave << 4) | ((lba >> 24) & 0x0F));
|
||||
outb(port_base + ATA_REG_FEATURES, 0x00);
|
||||
outb(port_base + ATA_REG_SEC_COUNT0, 1);
|
||||
@@ -163,75 +175,121 @@ static int ata_write_sector(Disk *disk, uint32_t lba, const uint8_t *buffer) {
|
||||
outb(port_base + ATA_REG_LBA1, (uint8_t)(lba >> 8));
|
||||
outb(port_base + ATA_REG_LBA2, (uint8_t)(lba >> 16));
|
||||
outb(port_base + ATA_REG_COMMAND, ATA_CMD_WRITE_PIO);
|
||||
|
||||
|
||||
ata_wait_bsy(port_base);
|
||||
ata_wait_drq(port_base);
|
||||
|
||||
|
||||
const uint16_t *ptr = (const uint16_t*)buffer;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
outw(port_base + ATA_REG_DATA, ptr[i]);
|
||||
}
|
||||
|
||||
// Flush / Sync
|
||||
|
||||
outb(port_base + ATA_REG_COMMAND, 0xE7); // Cache Flush
|
||||
ata_wait_bsy(port_base);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// === Device Naming ===
|
||||
|
||||
char disk_get_next_free_letter(void) {
|
||||
for (int i = 0; i < MAX_DISKS; i++) {
|
||||
char letter = 'A' + i;
|
||||
bool used = false;
|
||||
for (int j = 0; j < disk_count; j++) {
|
||||
if (disks[j]->letter == letter) {
|
||||
used = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!used) return letter;
|
||||
}
|
||||
return 0; // No free letters
|
||||
const char* disk_get_next_dev_name(void) {
|
||||
static char name[8];
|
||||
name[0] = 's';
|
||||
name[1] = 'd';
|
||||
name[2] = 'a' + next_sd_index;
|
||||
name[3] = 0;
|
||||
next_sd_index++;
|
||||
return name;
|
||||
}
|
||||
|
||||
// === Registration ===
|
||||
|
||||
void disk_register(Disk *disk) {
|
||||
if (disk_count >= MAX_DISKS) return;
|
||||
|
||||
// Ensure letter is unique
|
||||
if (disk->letter == 0) {
|
||||
disk->letter = disk_get_next_free_letter();
|
||||
|
||||
// Auto-assign devname if empty
|
||||
if (disk->devname[0] == 0) {
|
||||
const char *n = disk_get_next_dev_name();
|
||||
dm_strcpy(disk->devname, n);
|
||||
}
|
||||
|
||||
|
||||
disk->registered = true;
|
||||
disks[disk_count++] = disk;
|
||||
|
||||
serial_write("[DISK] Registered /dev/");
|
||||
serial_write(disk->devname);
|
||||
serial_write(" (");
|
||||
serial_write(disk->label);
|
||||
serial_write(")\n");
|
||||
}
|
||||
|
||||
void disk_manager_init(void) {
|
||||
for (int i = 0; i < MAX_DISKS; i++) {
|
||||
disks[i] = NULL;
|
||||
void disk_register_partition(Disk *parent, uint32_t lba_offset, uint32_t sector_count,
|
||||
bool is_fat32, int part_num) {
|
||||
if (disk_count >= MAX_DISKS) return;
|
||||
|
||||
Disk *part = (Disk*)kmalloc(sizeof(Disk));
|
||||
if (!part) return;
|
||||
|
||||
// Build name: parent_devname + partition number (e.g. "sda1")
|
||||
int len = dm_strlen(parent->devname);
|
||||
for (int i = 0; i < len; i++) part->devname[i] = parent->devname[i];
|
||||
part->devname[len] = '0' + part_num;
|
||||
part->devname[len + 1] = 0;
|
||||
|
||||
part->type = parent->type;
|
||||
part->is_fat32 = is_fat32;
|
||||
dm_strcpy(part->label, is_fat32 ? "FAT32 Partition" : "Unknown Partition");
|
||||
part->partition_lba_offset = lba_offset;
|
||||
part->total_sectors = sector_count;
|
||||
part->read_sector = parent->read_sector;
|
||||
part->write_sector = parent->write_sector;
|
||||
part->driver_data = parent->driver_data;
|
||||
part->parent = parent;
|
||||
part->is_partition = true;
|
||||
part->registered = true;
|
||||
|
||||
disks[disk_count++] = part;
|
||||
|
||||
serial_write("[DISK] Registered /dev/");
|
||||
serial_write(part->devname);
|
||||
serial_write(" (LBA offset ");
|
||||
serial_write_num(lba_offset);
|
||||
serial_write(", ");
|
||||
serial_write_num(sector_count);
|
||||
serial_write(" sectors, FAT32=");
|
||||
serial_write(" sectors, FAT32=");
|
||||
serial_write(is_fat32 ? "yes" : "no");
|
||||
serial_write(")\n");
|
||||
|
||||
if (is_fat32) {
|
||||
// Try to initialize and mount FAT32 volume to VFS
|
||||
void *vol = fat32_mount_volume(part);
|
||||
if (vol) {
|
||||
char mount_path[32];
|
||||
mount_path[0] = '/';
|
||||
mount_path[1] = 'd'; mount_path[2] = 'e'; mount_path[3] = 'v'; mount_path[4] = '/';
|
||||
dm_strcpy(mount_path + 5, part->devname);
|
||||
|
||||
if (vfs_mount(mount_path, part->devname, "fat32", fat32_get_realfs_ops(), vol)) {
|
||||
serial_write("[VFS] Mounted ");
|
||||
serial_write(mount_path);
|
||||
serial_write(" to VFS\n");
|
||||
wm_notify_fs_change();
|
||||
} else {
|
||||
serial_write("[VFS] Failed to mount ");
|
||||
serial_write(mount_path);
|
||||
serial_write("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
disk_count = 0;
|
||||
|
||||
// Register A: (Ramdisk)
|
||||
Disk *ramdisk = (Disk*)kmalloc(sizeof(Disk));
|
||||
ramdisk->letter = 'A';
|
||||
ramdisk->type = DISK_TYPE_RAM;
|
||||
ramdisk->is_fat32 = true; // Ramdisk is always formatted
|
||||
dm_strcpy(ramdisk->name, "RAM");
|
||||
ramdisk->read_sector = ramdisk_read;
|
||||
ramdisk->write_sector = ramdisk_write;
|
||||
ramdisk->driver_data = NULL;
|
||||
ramdisk->partition_lba_offset = 0;
|
||||
|
||||
disk_register(ramdisk);
|
||||
}
|
||||
|
||||
Disk* disk_get_by_letter(char letter) {
|
||||
// Uppercase
|
||||
if (letter >= 'a' && letter <= 'z') letter -= 32;
|
||||
|
||||
// === Lookup ===
|
||||
|
||||
Disk* disk_get_by_name(const char *devname) {
|
||||
if (!devname) return NULL;
|
||||
for (int i = 0; i < disk_count; i++) {
|
||||
if (disks[i]->letter == letter) {
|
||||
if (dm_strcmp(disks[i]->devname, devname) == 0) {
|
||||
return disks[i];
|
||||
}
|
||||
}
|
||||
@@ -247,130 +305,181 @@ Disk* disk_get_by_index(int index) {
|
||||
return disks[index];
|
||||
}
|
||||
|
||||
// === Backward Compat (deprecated) ===
|
||||
|
||||
// === MBR Partition Table Structures ===
|
||||
char disk_get_next_free_letter(void) {
|
||||
char letter = 'B' + next_drive_letter_idx++;
|
||||
if (letter > 'Z') return 0;
|
||||
return letter;
|
||||
}
|
||||
|
||||
Disk* disk_get_by_letter(char letter) {
|
||||
// Maps old letter scheme: A=ramfs (not a block device), B+=first real disk, etc.
|
||||
if (letter >= 'a' && letter <= 'z') letter -= 32;
|
||||
|
||||
// A: was the ramdisk — return NULL since ramfs is now VFS-managed
|
||||
if (letter == 'A') return NULL;
|
||||
|
||||
// B-Z map to disk indices 0, 1, 2...
|
||||
// Find real disks (non-RAM, non-partition-parent)
|
||||
int real_idx = 0;
|
||||
for (int i = 0; i < disk_count; i++) {
|
||||
if (disks[i]->is_partition && disks[i]->is_fat32) {
|
||||
if (real_idx == (letter - 'B')) {
|
||||
return disks[i];
|
||||
}
|
||||
real_idx++;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// === MBR Partition Table ===
|
||||
|
||||
typedef struct {
|
||||
uint8_t status; // 0x80 = bootable, 0x00 = inactive
|
||||
uint8_t chs_first[3]; // CHS of first sector
|
||||
uint8_t type; // Partition type
|
||||
uint8_t chs_last[3]; // CHS of last sector
|
||||
uint32_t lba_start; // LBA of first sector
|
||||
uint32_t sector_count; // Number of sectors
|
||||
uint8_t status;
|
||||
uint8_t chs_first[3];
|
||||
uint8_t type;
|
||||
uint8_t chs_last[3];
|
||||
uint32_t lba_start;
|
||||
uint32_t sector_count;
|
||||
} __attribute__((packed)) MBR_PartitionEntry;
|
||||
|
||||
// FAT32 partition type codes
|
||||
#define PART_TYPE_FAT32 0x0B
|
||||
#define PART_TYPE_FAT32_LBA 0x0C
|
||||
|
||||
// Check if sector contains a valid FAT32 BPB (Volume Boot Record)
|
||||
static bool is_fat32_bpb(const uint8_t *sector) {
|
||||
// Must have 0xAA55 boot signature
|
||||
if (sector[510] != 0x55 || sector[511] != 0xAA) return false;
|
||||
|
||||
// Check for FAT32 filesystem string at offset 82
|
||||
// "FAT32 " in the fs_type field of the BPB
|
||||
|
||||
if (sector[82] == 'F' && sector[83] == 'A' && sector[84] == 'T' &&
|
||||
sector[85] == '3' && sector[86] == '2') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Also accept if bytes_per_sector is 512 and sectors_per_fat_16 is 0
|
||||
// (FAT32 always has sectors_per_fat_16 == 0)
|
||||
|
||||
uint16_t bps = *(uint16_t*)§or[11];
|
||||
uint16_t spf16 = *(uint16_t*)§or[22];
|
||||
uint32_t spf32 = *(uint32_t*)§or[36];
|
||||
if (bps == 512 && spf16 == 0 && spf32 > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse MBR partition table and find a FAT32 partition.
|
||||
// Sets disk->partition_lba_offset and returns true if found.
|
||||
static bool detect_fat32_partition(Disk *disk) {
|
||||
// Parse MBR and register each partition as a child block device
|
||||
static void parse_mbr_partitions(Disk *disk) {
|
||||
uint8_t *buffer = (uint8_t*)kmalloc(512);
|
||||
if (!buffer) return false;
|
||||
|
||||
// Read sector 0 (MBR or raw BPB)
|
||||
if (!buffer) return;
|
||||
|
||||
if (disk->read_sector(disk, 0, buffer) != 0) {
|
||||
kfree(buffer);
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Must have 0xAA55 boot signature
|
||||
|
||||
// Check for valid MBR signature
|
||||
if (buffer[510] != 0x55 || buffer[511] != 0xAA) {
|
||||
kfree(buffer);
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check MBR partition table entries (4 entries at offset 446)
|
||||
|
||||
MBR_PartitionEntry *partitions = (MBR_PartitionEntry*)&buffer[446];
|
||||
|
||||
int part_num = 1;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (partitions[i].type == PART_TYPE_FAT32 ||
|
||||
partitions[i].type == PART_TYPE_FAT32_LBA) {
|
||||
|
||||
uint32_t part_lba = partitions[i].lba_start;
|
||||
|
||||
// Read the partition's first sector to verify it's a valid FAT32 BPB
|
||||
uint32_t start = partitions[i].lba_start;
|
||||
uint32_t size = partitions[i].sector_count;
|
||||
uint8_t type = partitions[i].type;
|
||||
|
||||
if (type == 0x00) continue; // Empty entry
|
||||
if (size == 0) continue;
|
||||
if (start >= disk->total_sectors) continue; // Invalid start
|
||||
|
||||
bool fat32 = false;
|
||||
if (type == PART_TYPE_FAT32 || type == PART_TYPE_FAT32_LBA) {
|
||||
// Verify by reading the BPB
|
||||
uint8_t *pbuf = (uint8_t*)kmalloc(512);
|
||||
if (!pbuf) { kfree(buffer); return false; }
|
||||
|
||||
if (disk->read_sector(disk, part_lba, pbuf) == 0 && is_fat32_bpb(pbuf)) {
|
||||
disk->partition_lba_offset = part_lba;
|
||||
if (pbuf) {
|
||||
if (disk->read_sector(disk, start, pbuf) == 0) {
|
||||
fat32 = is_fat32_bpb(pbuf);
|
||||
}
|
||||
kfree(pbuf);
|
||||
kfree(buffer);
|
||||
return true;
|
||||
}
|
||||
kfree(pbuf);
|
||||
}
|
||||
|
||||
disk_register_partition(disk, partitions[i].lba_start,
|
||||
partitions[i].sector_count, fat32, part_num);
|
||||
part_num++;
|
||||
}
|
||||
|
||||
// Fallback: check if sector 0 itself is a raw FAT32 BPB (no partition table)
|
||||
if (is_fat32_bpb(buffer)) {
|
||||
|
||||
// Fallback: if no partitions found, check if entire disk is a raw FAT32 volume
|
||||
if (part_num == 1 && is_fat32_bpb(buffer)) {
|
||||
serial_write("[DISK] No MBR partitions — raw FAT32 volume on /dev/");
|
||||
serial_write(disk->devname);
|
||||
serial_write("\n");
|
||||
disk->is_fat32 = true;
|
||||
disk->partition_lba_offset = 0;
|
||||
kfree(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
kfree(buffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
// === ATA Drive Discovery ===
|
||||
|
||||
static void try_add_ata_drive(uint16_t port, bool slave, const char *name) {
|
||||
if (ata_identify(port, slave)) {
|
||||
uint32_t sectors = ata_identify(port, slave);
|
||||
if (sectors > 0) {
|
||||
Disk *new_disk = (Disk*)kmalloc(sizeof(Disk));
|
||||
if (!new_disk) return;
|
||||
|
||||
|
||||
ATADriverData *data = (ATADriverData*)kmalloc(sizeof(ATADriverData));
|
||||
data->port_base = port;
|
||||
data->slave = slave;
|
||||
|
||||
new_disk->letter = 0; // Auto-assign
|
||||
|
||||
new_disk->devname[0] = 0; // Auto-assign
|
||||
new_disk->type = DISK_TYPE_IDE;
|
||||
dm_strcpy(new_disk->name, name);
|
||||
dm_strcpy(new_disk->label, name);
|
||||
new_disk->read_sector = ata_read_sector;
|
||||
new_disk->write_sector = ata_write_sector;
|
||||
new_disk->driver_data = data;
|
||||
new_disk->partition_lba_offset = 0;
|
||||
|
||||
// Detect FAT32 (with MBR partition support)
|
||||
if (detect_fat32_partition(new_disk)) {
|
||||
new_disk->is_fat32 = true;
|
||||
disk_register(new_disk);
|
||||
} else {
|
||||
kfree(data);
|
||||
kfree(new_disk);
|
||||
}
|
||||
new_disk->total_sectors = sectors;
|
||||
new_disk->parent = NULL;
|
||||
new_disk->is_partition = false;
|
||||
new_disk->is_fat32 = false;
|
||||
|
||||
disk_register(new_disk);
|
||||
|
||||
// Parse MBR to find partitions
|
||||
parse_mbr_partitions(new_disk);
|
||||
}
|
||||
}
|
||||
|
||||
// === Init & Scan ===
|
||||
|
||||
void disk_manager_init(void) {
|
||||
for (int i = 0; i < MAX_DISKS; i++) {
|
||||
disks[i] = NULL;
|
||||
}
|
||||
disk_count = 0;
|
||||
next_sd_index = 0;
|
||||
next_drive_letter_idx = 0;
|
||||
|
||||
serial_write("[DISK] Disk manager initialized (VFS mode)\n");
|
||||
// NOTE: Ramdisk (A:) is no longer registered here.
|
||||
// RAMFS is managed directly by fat32.c and mounted at "/" via VFS.
|
||||
}
|
||||
|
||||
void disk_manager_scan(void) {
|
||||
// Probe Standard ATA Ports
|
||||
try_add_ata_drive(ATA_PRIMARY_IO, false, "IDE1");
|
||||
try_add_ata_drive(ATA_PRIMARY_IO, true, "IDE2");
|
||||
try_add_ata_drive(ATA_SECONDARY_IO, false, "IDE3");
|
||||
try_add_ata_drive(ATA_SECONDARY_IO, true, "IDE4");
|
||||
serial_write("[DISK] Initializing AHCI (SATA DMA)...\n");
|
||||
ahci_init();
|
||||
|
||||
if (ahci_get_port_count() == 0) {
|
||||
serial_write("[DISK] No AHCI ports found, falling back to legacy IDE PIO...\n");
|
||||
try_add_ata_drive(ATA_PRIMARY_IO, false, "IDE Primary Master");
|
||||
try_add_ata_drive(ATA_PRIMARY_IO, true, "IDE Primary Slave");
|
||||
try_add_ata_drive(ATA_SECONDARY_IO, false, "IDE Secondary Master");
|
||||
try_add_ata_drive(ATA_SECONDARY_IO, true, "IDE Secondary Slave");
|
||||
} else {
|
||||
serial_write("[DISK] AHCI initialized successfully, skipping legacy IDE.\n");
|
||||
}
|
||||
}
|
||||
@@ -97,3 +97,23 @@ int pci_find_device_by_class(uint8_t class_code, uint8_t subclass, pci_device_t*
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t pci_get_bar(pci_device_t *dev, int bar_num) {
|
||||
if (!dev || bar_num < 0 || bar_num > 5) return 0;
|
||||
uint8_t offset = 0x10 + (bar_num * 4);
|
||||
return pci_read_config(dev->bus, dev->device, dev->function, offset);
|
||||
}
|
||||
|
||||
void pci_enable_bus_mastering(pci_device_t *dev) {
|
||||
if (!dev) return;
|
||||
uint32_t cmd = pci_read_config(dev->bus, dev->device, dev->function, 0x04);
|
||||
cmd |= (1 << 2); // Set Bus Master bit
|
||||
pci_write_config(dev->bus, dev->device, dev->function, 0x04, cmd);
|
||||
}
|
||||
|
||||
void pci_enable_mmio(pci_device_t *dev) {
|
||||
if (!dev) return;
|
||||
uint32_t cmd = pci_read_config(dev->bus, dev->device, dev->function, 0x04);
|
||||
cmd |= (1 << 1); // Set Memory Space bit
|
||||
pci_write_config(dev->bus, dev->device, dev->function, 0x04, cmd);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,9 @@ typedef struct {
|
||||
|
||||
#define PCI_CLASS_NETWORK_CONTROLLER 0x02
|
||||
#define PCI_CLASS_ETHERNET_CONTROLLER 0x00
|
||||
#define PCI_CLASS_MASS_STORAGE 0x01
|
||||
#define PCI_SUBCLASS_SATA 0x06
|
||||
#define PCI_SUBCLASS_IDE 0x01
|
||||
|
||||
uint32_t pci_read_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset);
|
||||
void pci_write_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset, uint32_t value);
|
||||
@@ -35,4 +38,9 @@ int pci_enumerate_devices(pci_device_t* devices, int max_devices);
|
||||
int pci_find_device(uint16_t vendor_id, uint16_t device_id, pci_device_t* device);
|
||||
int pci_find_device_by_class(uint8_t class_code, uint8_t subclass, pci_device_t* device);
|
||||
|
||||
// BAR access and bus mastering helpers
|
||||
uint32_t pci_get_bar(pci_device_t *dev, int bar_num);
|
||||
void pci_enable_bus_mastering(pci_device_t *dev);
|
||||
void pci_enable_mmio(pci_device_t *dev);
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user