mirror of
https://github.com/BoredDevNL/BoredOS.git
synced 2026-05-15 10:48:38 +00:00
disk: add GPT/MBR writing and disk rescan support
This commit is contained in:
parent
3e26332b1a
commit
fd18369bd7
4 changed files with 783 additions and 108 deletions
311
src/dev/ahci.c
311
src/dev/ahci.c
|
|
@ -46,10 +46,9 @@ static void ahci_strcpy(char *d, const char *s) {
|
||||||
|
|
||||||
// Kernel virtual to physical address conversion
|
// Kernel virtual to physical address conversion
|
||||||
extern uint64_t v2p(uint64_t vaddr);
|
extern uint64_t v2p(uint64_t vaddr);
|
||||||
|
extern uint64_t p2v(uint64_t paddr);
|
||||||
// ============================================================================
|
static int ahci_disk_sync(Disk *disk);
|
||||||
// Port Setup
|
static int ahci_find_free_slot(HBA_PORT *port);
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
static void ahci_stop_cmd(HBA_PORT *port) {
|
static void ahci_stop_cmd(HBA_PORT *port) {
|
||||||
// Clear ST (Start)
|
// Clear ST (Start)
|
||||||
|
|
@ -116,8 +115,7 @@ static void ahci_port_rebase(ahci_port_state_t *ps) {
|
||||||
port->fb = (uint32_t)(fb_phys & 0xFFFFFFFF);
|
port->fb = (uint32_t)(fb_phys & 0xFFFFFFFF);
|
||||||
port->fbu = (uint32_t)(fb_phys >> 32);
|
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) + 32 * sizeof(HBA_PRDT_ENTRY);
|
||||||
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);
|
ps->cmd_tbl = (HBA_CMD_TBL*)kmalloc_aligned(cmd_tbl_size, 256);
|
||||||
if (!ps->cmd_tbl) return;
|
if (!ps->cmd_tbl) return;
|
||||||
mem_memset(ps->cmd_tbl, 0, cmd_tbl_size);
|
mem_memset(ps->cmd_tbl, 0, cmd_tbl_size);
|
||||||
|
|
@ -135,10 +133,6 @@ static void ahci_port_rebase(ahci_port_state_t *ps) {
|
||||||
ahci_start_cmd(port);
|
ahci_start_cmd(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Sector I/O
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
static int ahci_find_free_slot(HBA_PORT *port) {
|
static int ahci_find_free_slot(HBA_PORT *port) {
|
||||||
uint32_t slots = (port->sact | port->ci);
|
uint32_t slots = (port->sact | port->ci);
|
||||||
for (int i = 0; i < 32; i++) {
|
for (int i = 0; i < 32; i++) {
|
||||||
|
|
@ -147,6 +141,76 @@ static int ahci_find_free_slot(HBA_PORT *port) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ahci_identify(int port_num, uint32_t *sectors, char *model) {
|
||||||
|
ahci_port_state_t *ps = &ports[port_num];
|
||||||
|
HBA_PORT *port = ps->port;
|
||||||
|
|
||||||
|
uint64_t rflags = spinlock_acquire_irqsave(&ps->lock);
|
||||||
|
port->is = 0xFFFFFFFF;
|
||||||
|
int slot = ahci_find_free_slot(port);
|
||||||
|
if (slot < 0) { spinlock_release_irqrestore(&ps->lock, rflags); 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;
|
||||||
|
cmd_hdr->prdtl = 1;
|
||||||
|
|
||||||
|
HBA_CMD_TBL *cmd_tbl = ps->cmd_tbl;
|
||||||
|
mem_memset(cmd_tbl, 0, sizeof(HBA_CMD_TBL) + 32 * sizeof(HBA_PRDT_ENTRY));
|
||||||
|
|
||||||
|
uint16_t *buf = (uint16_t*)kmalloc_aligned(512, 512);
|
||||||
|
uint64_t phys = v2p((uint64_t)buf);
|
||||||
|
|
||||||
|
cmd_tbl->prdt[0].dba = (uint32_t)(phys & 0xFFFFFFFF);
|
||||||
|
cmd_tbl->prdt[0].dbau = (uint32_t)(phys >> 32);
|
||||||
|
cmd_tbl->prdt[0].dbc = 511; // 512 bytes
|
||||||
|
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; // Command
|
||||||
|
fis->command = 0xEC; // IDENTIFY DEVICE
|
||||||
|
|
||||||
|
// Wait for port to be idle
|
||||||
|
int timeout = 1000000;
|
||||||
|
while ((port->tfd & (ATA_SR_BSY | ATA_SR_DRQ)) && --timeout > 0);
|
||||||
|
if (timeout <= 0) { kfree(buf); spinlock_release_irqrestore(&ps->lock, rflags); return -1; }
|
||||||
|
|
||||||
|
port->ci = (1 << slot);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if ((port->ci & (1 << slot)) == 0) break;
|
||||||
|
if (port->is & HBA_PORT_IS_TFES) { kfree(buf); spinlock_release_irqrestore(&ps->lock, rflags); return -1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract sectors (28-bit LBA for now, or 48-bit if supported)
|
||||||
|
uint32_t s28 = *((uint32_t*)&buf[60]);
|
||||||
|
uint64_t s48 = *((uint64_t*)&buf[100]);
|
||||||
|
|
||||||
|
if (s48 > 0) *sectors = (uint32_t)s48;
|
||||||
|
else *sectors = s28;
|
||||||
|
|
||||||
|
// Extract model name (Words 27-46, 40 bytes, big-endian shorts)
|
||||||
|
for (int i = 0; i < 20; i++) {
|
||||||
|
model[i*2] = (char)(buf[27+i] >> 8);
|
||||||
|
model[i*2+1] = (char)(buf[27+i] & 0xFF);
|
||||||
|
}
|
||||||
|
model[40] = 0;
|
||||||
|
|
||||||
|
// Swap bytes in model string (ATA strings are byte-swapped)
|
||||||
|
for (int i = 0; i < 40; i += 2) {
|
||||||
|
char tmp = model[i];
|
||||||
|
model[i] = model[i+1];
|
||||||
|
model[i+1] = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(buf);
|
||||||
|
spinlock_release_irqrestore(&ps->lock, rflags);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int ahci_read_sectors(int port_num, uint64_t lba, uint32_t count, uint8_t *buffer) {
|
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;
|
if (!ahci_initialized || port_num < 0 || port_num >= MAX_AHCI_PORTS) return -1;
|
||||||
ahci_port_state_t *ps = &ports[port_num];
|
ahci_port_state_t *ps = &ports[port_num];
|
||||||
|
|
@ -167,14 +231,38 @@ int ahci_read_sectors(int port_num, uint64_t lba, uint32_t count, uint8_t *buffe
|
||||||
cmd_hdr->prdtl = 1;
|
cmd_hdr->prdtl = 1;
|
||||||
|
|
||||||
HBA_CMD_TBL *cmd_tbl = ps->cmd_tbl;
|
HBA_CMD_TBL *cmd_tbl = ps->cmd_tbl;
|
||||||
mem_memset(cmd_tbl, 0, sizeof(HBA_CMD_TBL) + sizeof(HBA_PRDT_ENTRY));
|
mem_memset(cmd_tbl, 0, sizeof(HBA_CMD_TBL) + 32 * sizeof(HBA_PRDT_ENTRY));
|
||||||
|
|
||||||
// Setup PRDT
|
extern uint64_t paging_get_pml4_phys(void);
|
||||||
uint64_t buf_phys = v2p((uint64_t)buffer);
|
extern uint64_t paging_virt2phys(uint64_t pml4_phys, uint64_t virtual_addr);
|
||||||
cmd_tbl->prdt[0].dba = (uint32_t)(buf_phys & 0xFFFFFFFF);
|
uint64_t pml4 = paging_get_pml4_phys();
|
||||||
cmd_tbl->prdt[0].dbau = (uint32_t)(buf_phys >> 32);
|
uint64_t buf_addr = (uint64_t)buffer;
|
||||||
cmd_tbl->prdt[0].dbc = (count * 512) - 1; // 0-based byte count
|
uint32_t remaining = count * 512;
|
||||||
cmd_tbl->prdt[0].i = 1;
|
int prd_idx = 0;
|
||||||
|
|
||||||
|
while (remaining > 0 && prd_idx < 32) {
|
||||||
|
uint64_t phys = paging_virt2phys(pml4, buf_addr);
|
||||||
|
if (!phys) {
|
||||||
|
spinlock_release_irqrestore(&ps->lock, rflags);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t offset = buf_addr & 0xFFF;
|
||||||
|
uint32_t can_do = 4096 - offset;
|
||||||
|
if (can_do > remaining) can_do = remaining;
|
||||||
|
|
||||||
|
cmd_tbl->prdt[prd_idx].dba = (uint32_t)(phys & 0xFFFFFFFF);
|
||||||
|
cmd_tbl->prdt[prd_idx].dbau = (uint32_t)(phys >> 32);
|
||||||
|
cmd_tbl->prdt[prd_idx].dbc = can_do - 1; // 0-based
|
||||||
|
cmd_tbl->prdt[prd_idx].i = 0;
|
||||||
|
|
||||||
|
buf_addr += can_do;
|
||||||
|
remaining -= can_do;
|
||||||
|
prd_idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prd_idx > 0) cmd_tbl->prdt[prd_idx - 1].i = 1; // Interrupt on last
|
||||||
|
cmd_hdr->prdtl = prd_idx;
|
||||||
|
|
||||||
// Setup Command FIS
|
// Setup Command FIS
|
||||||
FIS_REG_H2D *fis = (FIS_REG_H2D*)&cmd_tbl->cfis;
|
FIS_REG_H2D *fis = (FIS_REG_H2D*)&cmd_tbl->cfis;
|
||||||
|
|
@ -238,13 +326,39 @@ int ahci_write_sectors(int port_num, uint64_t lba, uint32_t count, const uint8_t
|
||||||
cmd_hdr->prdtl = 1;
|
cmd_hdr->prdtl = 1;
|
||||||
|
|
||||||
HBA_CMD_TBL *cmd_tbl = ps->cmd_tbl;
|
HBA_CMD_TBL *cmd_tbl = ps->cmd_tbl;
|
||||||
mem_memset(cmd_tbl, 0, sizeof(HBA_CMD_TBL) + sizeof(HBA_PRDT_ENTRY));
|
mem_memset(cmd_tbl, 0, sizeof(HBA_CMD_TBL) + 32 * sizeof(HBA_PRDT_ENTRY));
|
||||||
|
|
||||||
uint64_t buf_phys = v2p((uint64_t)buffer);
|
// Setup PRDT - handle buffers spanning multiple physical pages
|
||||||
cmd_tbl->prdt[0].dba = (uint32_t)(buf_phys & 0xFFFFFFFF);
|
extern uint64_t paging_get_pml4_phys(void);
|
||||||
cmd_tbl->prdt[0].dbau = (uint32_t)(buf_phys >> 32);
|
extern uint64_t paging_virt2phys(uint64_t pml4_phys, uint64_t virtual_addr);
|
||||||
cmd_tbl->prdt[0].dbc = (count * 512) - 1;
|
uint64_t pml4 = paging_get_pml4_phys();
|
||||||
cmd_tbl->prdt[0].i = 1;
|
uint64_t buf_addr = (uint64_t)buffer;
|
||||||
|
uint32_t remaining = count * 512;
|
||||||
|
int prd_idx = 0;
|
||||||
|
|
||||||
|
while (remaining > 0 && prd_idx < 32) {
|
||||||
|
uint64_t phys = paging_virt2phys(pml4, buf_addr);
|
||||||
|
if (!phys) {
|
||||||
|
spinlock_release_irqrestore(&ps->lock, rflags);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t offset = buf_addr & 0xFFF;
|
||||||
|
uint32_t can_do = 4096 - offset;
|
||||||
|
if (can_do > remaining) can_do = remaining;
|
||||||
|
|
||||||
|
cmd_tbl->prdt[prd_idx].dba = (uint32_t)(phys & 0xFFFFFFFF);
|
||||||
|
cmd_tbl->prdt[prd_idx].dbau = (uint32_t)(phys >> 32);
|
||||||
|
cmd_tbl->prdt[prd_idx].dbc = can_do - 1; // 0-based
|
||||||
|
cmd_tbl->prdt[prd_idx].i = 0;
|
||||||
|
|
||||||
|
buf_addr += can_do;
|
||||||
|
remaining -= can_do;
|
||||||
|
prd_idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prd_idx > 0) cmd_tbl->prdt[prd_idx - 1].i = 1; // Interrupt on last
|
||||||
|
cmd_hdr->prdtl = prd_idx;
|
||||||
|
|
||||||
FIS_REG_H2D *fis = (FIS_REG_H2D*)&cmd_tbl->cfis;
|
FIS_REG_H2D *fis = (FIS_REG_H2D*)&cmd_tbl->cfis;
|
||||||
fis->fis_type = FIS_TYPE_REG_H2D;
|
fis->fis_type = FIS_TYPE_REG_H2D;
|
||||||
|
|
@ -321,6 +435,24 @@ static int ahci_disk_write_sector(Disk *disk, uint32_t sector, const uint8_t *bu
|
||||||
return ahci_write_sectors(data->ahci_port, (uint64_t)sector, 1, buffer);
|
return ahci_write_sectors(data->ahci_port, (uint64_t)sector, 1, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ahci_disk_read_sectors(Disk *disk, uint32_t sector, uint32_t count, uint8_t *buffer) {
|
||||||
|
AHCIDriverData *data = (AHCIDriverData*)disk->driver_data;
|
||||||
|
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, count, buffer);
|
||||||
|
}
|
||||||
|
return ahci_read_sectors(data->ahci_port, (uint64_t)sector, count, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ahci_disk_write_sectors(Disk *disk, uint32_t sector, uint32_t count, 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, count, buffer);
|
||||||
|
}
|
||||||
|
return ahci_write_sectors(data->ahci_port, (uint64_t)sector, count, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Initialization
|
// Initialization
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
@ -368,9 +500,7 @@ void ahci_init(void) {
|
||||||
serial_write_hex((uint32_t)abar_phys);
|
serial_write_hex((uint32_t)abar_phys);
|
||||||
serial_write("\n");
|
serial_write("\n");
|
||||||
|
|
||||||
// Map ABAR region into kernel virtual address space
|
uint64_t abar_virt = p2v(abar_phys);
|
||||||
// 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) {
|
for (uint64_t offset = 0; offset < 0x2000; offset += 4096) {
|
||||||
paging_map_page(paging_get_pml4_phys(), abar_virt + offset,
|
paging_map_page(paging_get_pml4_phys(), abar_virt + offset,
|
||||||
abar_phys + offset,
|
abar_phys + offset,
|
||||||
|
|
@ -409,6 +539,7 @@ void ahci_init(void) {
|
||||||
ahci_port_rebase(&ports[i]);
|
ahci_port_rebase(&ports[i]);
|
||||||
ports[i].active = true;
|
ports[i].active = true;
|
||||||
active_port_count++;
|
active_port_count++;
|
||||||
|
ahci_initialized = true;
|
||||||
|
|
||||||
// Register as a block device
|
// Register as a block device
|
||||||
Disk *disk = (Disk*)kmalloc(sizeof(Disk));
|
Disk *disk = (Disk*)kmalloc(sizeof(Disk));
|
||||||
|
|
@ -418,85 +549,36 @@ void ahci_init(void) {
|
||||||
|
|
||||||
disk->devname[0] = 0; // Auto-assign
|
disk->devname[0] = 0; // Auto-assign
|
||||||
disk->type = DISK_TYPE_SATA;
|
disk->type = DISK_TYPE_SATA;
|
||||||
|
|
||||||
|
uint32_t sectors = 0;
|
||||||
|
char model[64];
|
||||||
|
if (ahci_identify(i, §ors, model) == 0) {
|
||||||
|
ahci_strcpy(disk->label, model);
|
||||||
|
disk->total_sectors = sectors;
|
||||||
|
} else {
|
||||||
ahci_strcpy(disk->label, "SATA Drive");
|
ahci_strcpy(disk->label, "SATA Drive");
|
||||||
|
disk->total_sectors = 0;
|
||||||
|
}
|
||||||
|
|
||||||
disk->read_sector = ahci_disk_read_sector;
|
disk->read_sector = ahci_disk_read_sector;
|
||||||
disk->write_sector = ahci_disk_write_sector;
|
disk->write_sector = ahci_disk_write_sector;
|
||||||
|
disk->read_sectors = ahci_disk_read_sectors;
|
||||||
|
disk->write_sectors = ahci_disk_write_sectors;
|
||||||
|
disk->sync = ahci_disk_sync;
|
||||||
disk->driver_data = drv;
|
disk->driver_data = drv;
|
||||||
disk->partition_lba_offset = 0;
|
disk->partition_lba_offset = 0;
|
||||||
disk->total_sectors = 0;
|
|
||||||
disk->parent = NULL;
|
disk->parent = NULL;
|
||||||
disk->is_partition = false;
|
disk->is_partition = false;
|
||||||
disk->is_fat32 = false;
|
disk->is_fat32 = false;
|
||||||
|
|
||||||
disk_register(disk);
|
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);
|
extern void serial_write(const char *str);
|
||||||
|
extern int disk_rescan(Disk *disk);
|
||||||
serial_write("[AHCI] Probing partitions on /dev/");
|
serial_write("[AHCI] Probing partitions on /dev/");
|
||||||
serial_write(disk->devname);
|
serial_write(disk->devname);
|
||||||
serial_write("...\n");
|
serial_write("...\n");
|
||||||
|
disk_rescan(disk);
|
||||||
// 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) {
|
} else if (type == 1) {
|
||||||
serial_write("[AHCI] Port ");
|
serial_write("[AHCI] Port ");
|
||||||
|
|
@ -514,3 +596,52 @@ void ahci_init(void) {
|
||||||
serial_write("[AHCI] No active SATA ports found\n");
|
serial_write("[AHCI] No active SATA ports found\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ahci_flush_cache(int port_num) {
|
||||||
|
HBA_PORT *port = &abar->ports[port_num];
|
||||||
|
ahci_port_state_t *ps = &ports[port_num];
|
||||||
|
|
||||||
|
if (port_num < 0 || port_num >= 32 || !ps->active) return -1;
|
||||||
|
|
||||||
|
uint64_t rflags = spinlock_acquire_irqsave(&ps->lock);
|
||||||
|
|
||||||
|
port->is = 0xFFFFFFFF; // Clear interrupts
|
||||||
|
int slot = ahci_find_free_slot(port);
|
||||||
|
if (slot == -1) { spinlock_release_irqrestore(&ps->lock, rflags); return -1; }
|
||||||
|
|
||||||
|
HBA_CMD_HEADER *cmd_header = (HBA_CMD_HEADER*)p2v(port->clb);
|
||||||
|
cmd_header += slot;
|
||||||
|
cmd_header->cfl = sizeof(FIS_REG_H2D) / 4;
|
||||||
|
cmd_header->w = 0;
|
||||||
|
cmd_header->prdtl = 0;
|
||||||
|
|
||||||
|
HBA_CMD_TBL *cmd_tbl = (HBA_CMD_TBL*)p2v(cmd_header->ctba);
|
||||||
|
for (int i = 0; i < 256; i++) ((uint8_t*)cmd_tbl)[i] = 0;
|
||||||
|
|
||||||
|
FIS_REG_H2D *fis = (FIS_REG_H2D*)(&cmd_tbl->cfis);
|
||||||
|
fis->fis_type = FIS_TYPE_REG_H2D;
|
||||||
|
fis->c = 1;
|
||||||
|
fis->command = 0xEA; // FLUSH CACHE EXT
|
||||||
|
|
||||||
|
// Wait for port to be ready
|
||||||
|
int timeout = 1000000;
|
||||||
|
while ((port->tfd & (ATA_SR_BSY | ATA_SR_DRQ)) && --timeout > 0);
|
||||||
|
if (timeout == 0) { spinlock_release_irqrestore(&ps->lock, rflags); return -1; }
|
||||||
|
|
||||||
|
port->ci = (1 << slot);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if ((port->ci & (1 << slot)) == 0) break;
|
||||||
|
if (port->is & HBA_PORT_IS_TFES) { spinlock_release_irqrestore(&ps->lock, rflags); return -1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
spinlock_release_irqrestore(&ps->lock, rflags);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ahci_disk_sync(Disk *disk) {
|
||||||
|
AHCIDriverData *drv = (AHCIDriverData*)disk->driver_data;
|
||||||
|
if (!drv) return -1;
|
||||||
|
return ahci_flush_cache(drv->ahci_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -152,6 +152,12 @@ typedef struct {
|
||||||
#define HBA_PORT_CMD_FRE 0x0010 // FIS Receive Enable
|
#define HBA_PORT_CMD_FRE 0x0010 // FIS Receive Enable
|
||||||
#define HBA_PORT_CMD_FR 0x4000 // FIS Receive Running
|
#define HBA_PORT_CMD_FR 0x4000 // FIS Receive Running
|
||||||
#define HBA_PORT_CMD_CR 0x8000 // Command List Running
|
#define HBA_PORT_CMD_CR 0x8000 // Command List Running
|
||||||
|
#define HBA_PORT_IS_TFES (1u << 30)
|
||||||
|
|
||||||
|
#define ATA_SR_BSY 0x80
|
||||||
|
#define ATA_SR_DRDY 0x40
|
||||||
|
#define ATA_SR_DRQ 0x08
|
||||||
|
#define ATA_SR_ERR 0x01
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// ATA Commands
|
// ATA Commands
|
||||||
|
|
|
||||||
|
|
@ -18,16 +18,18 @@ typedef enum {
|
||||||
} DiskType;
|
} DiskType;
|
||||||
|
|
||||||
typedef struct Disk {
|
typedef struct Disk {
|
||||||
char devname[16]; // Device name: "sda", "sdb", "sda1", etc.
|
char devname[16];
|
||||||
DiskType type;
|
DiskType type;
|
||||||
bool is_fat32;
|
bool is_fat32;
|
||||||
char label[32]; // Human-readable label
|
bool is_esp;
|
||||||
uint32_t partition_lba_offset; // LBA offset of partition (0 for whole disk)
|
char label[32];
|
||||||
uint32_t total_sectors; // Total sectors on this device/partition
|
uint32_t partition_lba_offset;
|
||||||
|
uint32_t total_sectors;
|
||||||
// Function pointers for driver operations
|
|
||||||
int (*read_sector)(struct Disk *disk, uint32_t sector, uint8_t *buffer);
|
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);
|
int (*write_sector)(struct Disk *disk, uint32_t sector, const uint8_t *buffer);
|
||||||
|
int (*read_sectors)(struct Disk *disk, uint32_t sector, uint32_t count, uint8_t *buffer);
|
||||||
|
int (*write_sectors)(struct Disk *disk, uint32_t sector, uint32_t count, const uint8_t *buffer);
|
||||||
|
int (*sync)(struct Disk *disk);
|
||||||
|
|
||||||
// Private driver data
|
// Private driver data
|
||||||
void *driver_data;
|
void *driver_data;
|
||||||
|
|
@ -38,6 +40,17 @@ typedef struct Disk {
|
||||||
bool registered;
|
bool registered;
|
||||||
} Disk;
|
} Disk;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t lba_start;
|
||||||
|
uint32_t sector_count;
|
||||||
|
uint8_t part_type;
|
||||||
|
uint8_t flags;
|
||||||
|
char label[36];
|
||||||
|
} disk_partition_spec_t;
|
||||||
|
|
||||||
|
#define PART_FLAG_ESP 0x01
|
||||||
|
#define MIN_INSTALL_SECTORS 2097152
|
||||||
|
|
||||||
// Initialization and scanning
|
// Initialization and scanning
|
||||||
void disk_manager_init(void);
|
void disk_manager_init(void);
|
||||||
void disk_manager_scan(void);
|
void disk_manager_scan(void);
|
||||||
|
|
@ -45,7 +58,7 @@ void disk_manager_scan(void);
|
||||||
// Device registration
|
// Device registration
|
||||||
void disk_register(Disk *disk);
|
void disk_register(Disk *disk);
|
||||||
void disk_register_partition(Disk *parent, uint32_t lba_offset, uint32_t sector_count,
|
void disk_register_partition(Disk *parent, uint32_t lba_offset, uint32_t sector_count,
|
||||||
bool is_fat32, int part_num);
|
bool is_fat32, bool is_esp, int part_num);
|
||||||
|
|
||||||
// Lookup
|
// Lookup
|
||||||
Disk* disk_get_by_name(const char *devname);
|
Disk* disk_get_by_name(const char *devname);
|
||||||
|
|
@ -59,4 +72,11 @@ const char* disk_get_next_dev_name(void); // Returns "sda", "sdb", etc.
|
||||||
Disk* disk_get_by_letter(char letter);
|
Disk* disk_get_by_letter(char letter);
|
||||||
char disk_get_next_free_letter(void);
|
char disk_get_next_free_letter(void);
|
||||||
|
|
||||||
|
int disk_write_gpt(Disk *disk, disk_partition_spec_t *parts, int count);
|
||||||
|
int disk_write_mbr(Disk *disk, disk_partition_spec_t *parts, int count);
|
||||||
|
|
||||||
|
int disk_sync(Disk *disk);
|
||||||
|
|
||||||
|
int disk_rescan(Disk *disk);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -227,6 +227,105 @@ static int ata_write_sector(Disk *disk, uint32_t lba, const uint8_t *buffer) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ata_read_sectors(Disk *disk, uint32_t lba, uint32_t count, uint8_t *buffer) {
|
||||||
|
ATADriverData *data = (ATADriverData*)disk->driver_data;
|
||||||
|
uint16_t port_base = data->port_base;
|
||||||
|
bool slave = data->slave;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t flags = spinlock_acquire_irqsave(&ide_lock);
|
||||||
|
|
||||||
|
while (count > 0) {
|
||||||
|
uint8_t batch = (count > 255) ? 255 : (uint8_t)count;
|
||||||
|
if (ata_wait_bsy(port_base) != 0) {
|
||||||
|
spinlock_release_irqrestore(&ide_lock, flags);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
outb(port_base + ATA_REG_HDDEVSEL, 0xE0 | (slave << 4) | ((lba >> 24) & 0x0F));
|
||||||
|
outb(port_base + ATA_REG_SEC_COUNT0, batch);
|
||||||
|
outb(port_base + ATA_REG_LBA0, (uint8_t)(lba));
|
||||||
|
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);
|
||||||
|
|
||||||
|
for (uint8_t b = 0; b < batch; b++) {
|
||||||
|
if (ata_wait_bsy(port_base) != 0 || ata_wait_drq(port_base) != 0) {
|
||||||
|
spinlock_release_irqrestore(&ide_lock, flags);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
uint16_t *pptr = (uint16_t*)(buffer + (b * 512));
|
||||||
|
for (int i = 0; i < 256; i++) {
|
||||||
|
pptr[i] = inw(port_base + ATA_REG_DATA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lba += batch;
|
||||||
|
buffer += batch * 512;
|
||||||
|
count -= batch;
|
||||||
|
}
|
||||||
|
|
||||||
|
spinlock_release_irqrestore(&ide_lock, flags);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ata_write_sectors(Disk *disk, uint32_t lba, uint32_t count, const uint8_t *buffer) {
|
||||||
|
ATADriverData *data = (ATADriverData*)disk->driver_data;
|
||||||
|
uint16_t port_base = data->port_base;
|
||||||
|
bool slave = data->slave;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t flags = spinlock_acquire_irqsave(&ide_lock);
|
||||||
|
|
||||||
|
while (count > 0) {
|
||||||
|
uint8_t batch = (count > 255) ? 255 : (uint8_t)count;
|
||||||
|
if (ata_wait_bsy(port_base) != 0) {
|
||||||
|
spinlock_release_irqrestore(&ide_lock, flags);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
outb(port_base + ATA_REG_HDDEVSEL, 0xE0 | (slave << 4) | ((lba >> 24) & 0x0F));
|
||||||
|
outb(port_base + ATA_REG_SEC_COUNT0, batch);
|
||||||
|
outb(port_base + ATA_REG_LBA0, (uint8_t)(lba));
|
||||||
|
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);
|
||||||
|
|
||||||
|
for (uint8_t b = 0; b < batch; b++) {
|
||||||
|
if (ata_wait_bsy(port_base) != 0 || ata_wait_drq(port_base) != 0) {
|
||||||
|
spinlock_release_irqrestore(&ide_lock, flags);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
const uint16_t *pptr = (const uint16_t*)(buffer + (b * 512));
|
||||||
|
for (int i = 0; i < 256; i++) {
|
||||||
|
outw(port_base + ATA_REG_DATA, pptr[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outb(port_base + ATA_REG_COMMAND, 0xE7); // Cache Flush
|
||||||
|
if (ata_wait_bsy(port_base) != 0) {
|
||||||
|
spinlock_release_irqrestore(&ide_lock, flags);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
lba += batch;
|
||||||
|
buffer += batch * 512;
|
||||||
|
count -= batch;
|
||||||
|
}
|
||||||
|
|
||||||
|
spinlock_release_irqrestore(&ide_lock, flags);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// === Device Naming ===
|
// === Device Naming ===
|
||||||
|
|
||||||
const char* disk_get_next_dev_name(void) {
|
const char* disk_get_next_dev_name(void) {
|
||||||
|
|
@ -257,11 +356,13 @@ void disk_register(Disk *disk) {
|
||||||
serial_write(disk->devname);
|
serial_write(disk->devname);
|
||||||
serial_write(" (");
|
serial_write(" (");
|
||||||
serial_write(disk->label);
|
serial_write(disk->label);
|
||||||
serial_write(")\n");
|
serial_write(") size=");
|
||||||
|
serial_write_num(disk->total_sectors);
|
||||||
|
serial_write(" sectors\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void disk_register_partition(Disk *parent, uint32_t lba_offset, uint32_t sector_count,
|
void disk_register_partition(Disk *parent, uint32_t lba_offset, uint32_t sector_count,
|
||||||
bool is_fat32, int part_num) {
|
bool is_fat32, bool is_esp, int part_num) {
|
||||||
if (disk_count >= MAX_DISKS) return;
|
if (disk_count >= MAX_DISKS) return;
|
||||||
|
|
||||||
Disk *part = (Disk*)kmalloc(sizeof(Disk));
|
Disk *part = (Disk*)kmalloc(sizeof(Disk));
|
||||||
|
|
@ -275,7 +376,8 @@ void disk_register_partition(Disk *parent, uint32_t lba_offset, uint32_t sector_
|
||||||
|
|
||||||
part->type = parent->type;
|
part->type = parent->type;
|
||||||
part->is_fat32 = is_fat32;
|
part->is_fat32 = is_fat32;
|
||||||
dm_strcpy(part->label, is_fat32 ? "FAT32 Partition" : "Unknown Partition");
|
part->is_esp = is_esp;
|
||||||
|
dm_strcpy(part->label, is_esp ? "EFI System Partition" : (is_fat32 ? "FAT32 Partition" : "Unknown Partition"));
|
||||||
part->partition_lba_offset = lba_offset;
|
part->partition_lba_offset = lba_offset;
|
||||||
part->total_sectors = sector_count;
|
part->total_sectors = sector_count;
|
||||||
part->read_sector = parent->read_sector;
|
part->read_sector = parent->read_sector;
|
||||||
|
|
@ -294,8 +396,8 @@ void disk_register_partition(Disk *parent, uint32_t lba_offset, uint32_t sector_
|
||||||
serial_write(", ");
|
serial_write(", ");
|
||||||
serial_write_num(sector_count);
|
serial_write_num(sector_count);
|
||||||
serial_write(" sectors, FAT32=");
|
serial_write(" sectors, FAT32=");
|
||||||
serial_write(" sectors, FAT32=");
|
|
||||||
serial_write(is_fat32 ? "yes" : "no");
|
serial_write(is_fat32 ? "yes" : "no");
|
||||||
|
if (is_esp) serial_write(", ESP=yes");
|
||||||
serial_write(")\n");
|
serial_write(")\n");
|
||||||
|
|
||||||
if (is_fat32) {
|
if (is_fat32) {
|
||||||
|
|
@ -411,12 +513,18 @@ static void parse_mbr_partitions(Disk *disk) {
|
||||||
if (!buffer) return;
|
if (!buffer) return;
|
||||||
|
|
||||||
if (disk->read_sector(disk, 0, buffer) != 0) {
|
if (disk->read_sector(disk, 0, buffer) != 0) {
|
||||||
|
serial_write("[DISK] MBR read failed on /dev/");
|
||||||
|
serial_write(disk->devname);
|
||||||
|
serial_write("\n");
|
||||||
kfree(buffer);
|
kfree(buffer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for valid MBR signature
|
// Check for valid MBR signature
|
||||||
if (buffer[510] != 0x55 || buffer[511] != 0xAA) {
|
if (buffer[510] != 0x55 || buffer[511] != 0xAA) {
|
||||||
|
serial_write("[DISK] Invalid MBR signature on /dev/");
|
||||||
|
serial_write(disk->devname);
|
||||||
|
serial_write("\n");
|
||||||
kfree(buffer);
|
kfree(buffer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -424,6 +532,7 @@ static void parse_mbr_partitions(Disk *disk) {
|
||||||
MBR_PartitionEntry *partitions = (MBR_PartitionEntry*)&buffer[446];
|
MBR_PartitionEntry *partitions = (MBR_PartitionEntry*)&buffer[446];
|
||||||
int part_num = 1;
|
int part_num = 1;
|
||||||
|
|
||||||
|
int part_count = 0;
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
uint32_t start = partitions[i].lba_start;
|
uint32_t start = partitions[i].lba_start;
|
||||||
uint32_t size = partitions[i].sector_count;
|
uint32_t size = partitions[i].sector_count;
|
||||||
|
|
@ -446,7 +555,8 @@ static void parse_mbr_partitions(Disk *disk) {
|
||||||
}
|
}
|
||||||
|
|
||||||
disk_register_partition(disk, partitions[i].lba_start,
|
disk_register_partition(disk, partitions[i].lba_start,
|
||||||
partitions[i].sector_count, fat32, part_num);
|
partitions[i].sector_count, fat32, false, part_num);
|
||||||
|
part_count++;
|
||||||
part_num++;
|
part_num++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -457,6 +567,10 @@ static void parse_mbr_partitions(Disk *disk) {
|
||||||
serial_write("\n");
|
serial_write("\n");
|
||||||
disk->is_fat32 = true;
|
disk->is_fat32 = true;
|
||||||
disk->partition_lba_offset = 0;
|
disk->partition_lba_offset = 0;
|
||||||
|
} else if (part_count == 0) {
|
||||||
|
serial_write("[DISK] No MBR partitions found on /dev/");
|
||||||
|
serial_write(disk->devname);
|
||||||
|
serial_write("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
kfree(buffer);
|
kfree(buffer);
|
||||||
|
|
@ -479,6 +593,8 @@ static void try_add_ata_drive(uint16_t port, bool slave, const char *name) {
|
||||||
dm_strcpy(new_disk->label, name);
|
dm_strcpy(new_disk->label, name);
|
||||||
new_disk->read_sector = ata_read_sector;
|
new_disk->read_sector = ata_read_sector;
|
||||||
new_disk->write_sector = ata_write_sector;
|
new_disk->write_sector = ata_write_sector;
|
||||||
|
new_disk->read_sectors = ata_read_sectors;
|
||||||
|
new_disk->write_sectors = ata_write_sectors;
|
||||||
new_disk->driver_data = data;
|
new_disk->driver_data = data;
|
||||||
new_disk->partition_lba_offset = 0;
|
new_disk->partition_lba_offset = 0;
|
||||||
new_disk->total_sectors = sectors;
|
new_disk->total_sectors = sectors;
|
||||||
|
|
@ -504,8 +620,6 @@ void disk_manager_init(void) {
|
||||||
next_drive_letter_idx = 0;
|
next_drive_letter_idx = 0;
|
||||||
|
|
||||||
log_ok("Disk manager ready");
|
log_ok("Disk manager ready");
|
||||||
// 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) {
|
void disk_manager_scan(void) {
|
||||||
|
|
@ -523,3 +637,407 @@ void disk_manager_scan(void) {
|
||||||
log_ok("AHCI ports initialized, skipping IDE");
|
log_ok("AHCI ports initialized, skipping IDE");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint32_t crc32_compute(const uint8_t *data, uint32_t len) {
|
||||||
|
uint32_t crc = 0xFFFFFFFF;
|
||||||
|
for (uint32_t i = 0; i < len; i++) {
|
||||||
|
crc ^= data[i];
|
||||||
|
for (int j = 0; j < 8; j++)
|
||||||
|
crc = (crc >> 1) ^ (0xEDB88320 & -(crc & 1));
|
||||||
|
}
|
||||||
|
return ~crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define GPT_PART_ENTRY_COUNT 128
|
||||||
|
#define GPT_PART_ENTRY_SIZE 128
|
||||||
|
_Static_assert(GPT_PART_ENTRY_COUNT * GPT_PART_ENTRY_SIZE == 32 * 512,
|
||||||
|
"GPT partition array must be exactly 32 sectors");
|
||||||
|
|
||||||
|
static const uint8_t GPT_GUID_ESP[16] = {
|
||||||
|
0x28, 0x73, 0x2A, 0xC1, 0x1F, 0xF8, 0xD2, 0x11,
|
||||||
|
0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B
|
||||||
|
};
|
||||||
|
static const uint8_t GPT_GUID_BASIC_DATA[16] = {
|
||||||
|
0xA2, 0xA0, 0xD0, 0xEB, 0xE5, 0xB9, 0x33, 0x44,
|
||||||
|
0x87, 0xC0, 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct __attribute__((packed)) {
|
||||||
|
uint64_t signature;
|
||||||
|
uint32_t revision;
|
||||||
|
uint32_t header_size;
|
||||||
|
uint32_t crc32;
|
||||||
|
uint32_t reserved;
|
||||||
|
uint64_t my_lba;
|
||||||
|
uint64_t alternate_lba;
|
||||||
|
uint64_t first_usable_lba;
|
||||||
|
uint64_t last_usable_lba;
|
||||||
|
uint8_t disk_guid[16];
|
||||||
|
uint64_t partition_entry_lba;
|
||||||
|
uint32_t num_partition_entries;
|
||||||
|
uint32_t size_of_partition_entry;
|
||||||
|
uint32_t partition_entry_array_crc32;
|
||||||
|
} GPT_Header;
|
||||||
|
|
||||||
|
typedef struct __attribute__((packed)) {
|
||||||
|
uint8_t type_guid[16];
|
||||||
|
uint8_t partition_guid[16];
|
||||||
|
uint64_t start_lba;
|
||||||
|
uint64_t end_lba;
|
||||||
|
uint64_t attributes;
|
||||||
|
uint16_t name[36];
|
||||||
|
} GPT_Entry;
|
||||||
|
|
||||||
|
static void gpt_make_pseudo_guid(uint8_t *guid, const char *label, uint32_t total_sectors) {
|
||||||
|
uint32_t h = 5381;
|
||||||
|
for (int i = 0; label[i]; i++)
|
||||||
|
h = h * 33 + (unsigned char)label[i];
|
||||||
|
h ^= total_sectors;
|
||||||
|
for (int i = 0; i < 16; i++)
|
||||||
|
guid[i] = (uint8_t)(h >> ((i % 4) * 8));
|
||||||
|
guid[8] = (guid[8] & 0x3F) | 0x80;
|
||||||
|
guid[6] = (guid[6] & 0x0F) | 0x40;
|
||||||
|
}
|
||||||
|
|
||||||
|
int disk_write_gpt(Disk *disk, disk_partition_spec_t *parts, int count) {
|
||||||
|
if (!disk || !parts || count <= 0 || count > GPT_PART_ENTRY_COUNT)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
uint32_t first_usable = 2048;
|
||||||
|
uint32_t last_usable = disk->total_sectors - 34;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
if (parts[i].sector_count == 0) {
|
||||||
|
serial_write("[GPT] Error: zero-sized partition\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
uint32_t start = parts[i].lba_start;
|
||||||
|
uint32_t end = start + parts[i].sector_count - 1;
|
||||||
|
|
||||||
|
if (start % 2048 != 0) {
|
||||||
|
start = ((start + 2047) / 2048) * 2048;
|
||||||
|
parts[i].lba_start = start;
|
||||||
|
end = start + parts[i].sector_count - 1;
|
||||||
|
serial_write("[GPT] Warning: start rounded up to 2048 boundary\n");
|
||||||
|
}
|
||||||
|
if (start < first_usable || end > last_usable) {
|
||||||
|
serial_write("[GPT] Error: partition out of usable range\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
for (int j = 0; j < i; j++) {
|
||||||
|
uint32_t js = parts[j].lba_start;
|
||||||
|
uint32_t je = js + parts[j].sector_count - 1;
|
||||||
|
if (start <= je && end >= js) {
|
||||||
|
serial_write("[GPT] Error: overlapping partitions\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *entry_buf = (uint8_t *)kmalloc(GPT_PART_ENTRY_COUNT * GPT_PART_ENTRY_SIZE);
|
||||||
|
if (!entry_buf) return -1;
|
||||||
|
for (int i = 0; i < GPT_PART_ENTRY_COUNT * GPT_PART_ENTRY_SIZE; i++) entry_buf[i] = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
GPT_Entry *e = (GPT_Entry *)(entry_buf + i * GPT_PART_ENTRY_SIZE);
|
||||||
|
if (parts[i].flags & PART_FLAG_ESP)
|
||||||
|
for (int b = 0; b < 16; b++) e->type_guid[b] = GPT_GUID_ESP[b];
|
||||||
|
else
|
||||||
|
for (int b = 0; b < 16; b++) e->type_guid[b] = GPT_GUID_BASIC_DATA[b];
|
||||||
|
|
||||||
|
gpt_make_pseudo_guid(e->partition_guid, parts[i].label, disk->total_sectors);
|
||||||
|
e->start_lba = parts[i].lba_start;
|
||||||
|
e->end_lba = parts[i].lba_start + parts[i].sector_count - 1;
|
||||||
|
e->attributes = (parts[i].flags & PART_FLAG_ESP) ? 0x01 : 0x00;
|
||||||
|
/* UTF-16LE name */
|
||||||
|
for (int c = 0; c < 36 && parts[i].label[c]; c++)
|
||||||
|
e->name[c] = (uint16_t)(unsigned char)parts[i].label[c];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t entry_crc = crc32_compute(entry_buf, GPT_PART_ENTRY_COUNT * GPT_PART_ENTRY_SIZE);
|
||||||
|
|
||||||
|
uint8_t *hdr_buf = (uint8_t *)kmalloc(512);
|
||||||
|
if (!hdr_buf) { kfree(entry_buf); return -1; }
|
||||||
|
for (int i = 0; i < 512; i++) hdr_buf[i] = 0;
|
||||||
|
|
||||||
|
GPT_Header *hdr = (GPT_Header *)hdr_buf;
|
||||||
|
hdr->signature = 0x5452415020494645ULL;
|
||||||
|
hdr->revision = 0x00010000;
|
||||||
|
hdr->header_size = 92;
|
||||||
|
hdr->crc32 = 0;
|
||||||
|
hdr->reserved = 0;
|
||||||
|
hdr->my_lba = 1;
|
||||||
|
hdr->alternate_lba = disk->total_sectors - 1;
|
||||||
|
hdr->first_usable_lba = first_usable;
|
||||||
|
hdr->last_usable_lba = last_usable;
|
||||||
|
gpt_make_pseudo_guid(hdr->disk_guid, disk->devname, disk->total_sectors);
|
||||||
|
hdr->partition_entry_lba = 2;
|
||||||
|
hdr->num_partition_entries = GPT_PART_ENTRY_COUNT;
|
||||||
|
hdr->size_of_partition_entry = GPT_PART_ENTRY_SIZE;
|
||||||
|
hdr->partition_entry_array_crc32 = entry_crc;
|
||||||
|
|
||||||
|
hdr->crc32 = 0;
|
||||||
|
hdr->crc32 = crc32_compute(hdr_buf, hdr->header_size);
|
||||||
|
|
||||||
|
uint8_t *mbr_buf = (uint8_t *)kmalloc(512);
|
||||||
|
if (!mbr_buf) { kfree(entry_buf); kfree(hdr_buf); return -1; }
|
||||||
|
for (int i = 0; i < 512; i++) mbr_buf[i] = 0;
|
||||||
|
mbr_buf[446] = 0x00; /* Status: Non-bootable */
|
||||||
|
mbr_buf[447] = 0x00; mbr_buf[448] = 0x02; mbr_buf[449] = 0x00; /* CHS Start: 0x000200 */
|
||||||
|
mbr_buf[450] = 0xEE; /* Type: GPT Protective */
|
||||||
|
mbr_buf[451] = 0xFF; mbr_buf[452] = 0xFF; mbr_buf[453] = 0xFF; /* CHS End: 0xFFFFFF */
|
||||||
|
mbr_buf[454] = 0x01; mbr_buf[455] = 0x00; mbr_buf[456] = 0x00; mbr_buf[457] = 0x00; /* LBA Start: 1 */
|
||||||
|
uint32_t pmbr_size = disk->total_sectors - 1;
|
||||||
|
mbr_buf[458] = (uint8_t)(pmbr_size);
|
||||||
|
mbr_buf[459] = (uint8_t)(pmbr_size >> 8);
|
||||||
|
mbr_buf[460] = (uint8_t)(pmbr_size >> 16);
|
||||||
|
mbr_buf[461] = (uint8_t)(pmbr_size >> 24);
|
||||||
|
mbr_buf[510] = 0x55;
|
||||||
|
mbr_buf[511] = 0xAA;
|
||||||
|
disk->write_sector(disk, 0, mbr_buf);
|
||||||
|
kfree(mbr_buf);
|
||||||
|
|
||||||
|
if (disk->write_sector(disk, 1, hdr_buf) != 0) {
|
||||||
|
serial_write("[GPT] Error: failed to write header\n");
|
||||||
|
kfree(entry_buf); kfree(hdr_buf); return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int s = 0; s < 32; s++) {
|
||||||
|
if (disk->write_sector(disk, 2 + s, entry_buf + s * 512) != 0) {
|
||||||
|
serial_write("[GPT] Error: failed to write partition entries\n");
|
||||||
|
kfree(entry_buf); kfree(hdr_buf); return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GPT_Header *bhdr = (GPT_Header *)hdr_buf;
|
||||||
|
for (int i = 0; i < 512; i++) hdr_buf[i] = 0;
|
||||||
|
bhdr->signature = 0x5452415020494645ULL;
|
||||||
|
bhdr->revision = 0x00010000;
|
||||||
|
bhdr->header_size = 92;
|
||||||
|
bhdr->my_lba = disk->total_sectors - 1;
|
||||||
|
bhdr->alternate_lba = 1;
|
||||||
|
bhdr->first_usable_lba = first_usable;
|
||||||
|
bhdr->last_usable_lba = last_usable;
|
||||||
|
gpt_make_pseudo_guid(bhdr->disk_guid, disk->devname, disk->total_sectors);
|
||||||
|
bhdr->partition_entry_lba = disk->total_sectors - 33;
|
||||||
|
bhdr->num_partition_entries = GPT_PART_ENTRY_COUNT;
|
||||||
|
bhdr->size_of_partition_entry = GPT_PART_ENTRY_SIZE;
|
||||||
|
bhdr->partition_entry_array_crc32 = entry_crc;
|
||||||
|
bhdr->crc32 = 0;
|
||||||
|
bhdr->crc32 = crc32_compute(hdr_buf, bhdr->header_size);
|
||||||
|
|
||||||
|
for (int s = 0; s < 32; s++) {
|
||||||
|
disk->write_sector(disk, disk->total_sectors - 33 + s, entry_buf + s * 512);
|
||||||
|
}
|
||||||
|
disk->write_sector(disk, disk->total_sectors - 1, hdr_buf);
|
||||||
|
|
||||||
|
kfree(entry_buf);
|
||||||
|
kfree(hdr_buf);
|
||||||
|
|
||||||
|
serial_write("[DISK] GPT written to /dev/");
|
||||||
|
serial_write(disk->devname);
|
||||||
|
serial_write("\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int disk_write_mbr(Disk *disk, disk_partition_spec_t *parts, int count) {
|
||||||
|
if (!disk || !parts || count <= 0 || count > 4) return -1;
|
||||||
|
|
||||||
|
uint8_t *buf = (uint8_t *)kmalloc(512);
|
||||||
|
if (!buf) return -1;
|
||||||
|
for (int i = 0; i < 512; i++) buf[i] = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
if (parts[i].sector_count == 0) { kfree(buf); return -1; }
|
||||||
|
uint8_t *entry = buf + 446 + i * 16;
|
||||||
|
entry[0] = 0x80;
|
||||||
|
entry[4] = 0x0C;
|
||||||
|
uint32_t lba_start = parts[i].lba_start;
|
||||||
|
uint32_t sec_count = parts[i].sector_count;
|
||||||
|
entry[8] = (uint8_t)(lba_start);
|
||||||
|
entry[9] = (uint8_t)(lba_start >> 8);
|
||||||
|
entry[10] = (uint8_t)(lba_start >> 16);
|
||||||
|
entry[11] = (uint8_t)(lba_start >> 24);
|
||||||
|
entry[12] = (uint8_t)(sec_count);
|
||||||
|
entry[13] = (uint8_t)(sec_count >> 8);
|
||||||
|
entry[14] = (uint8_t)(sec_count >> 16);
|
||||||
|
entry[15] = (uint8_t)(sec_count >> 24);
|
||||||
|
}
|
||||||
|
buf[510] = 0x55;
|
||||||
|
buf[511] = 0xAA;
|
||||||
|
|
||||||
|
int ret = disk->write_sector(disk, 0, buf);
|
||||||
|
kfree(buf);
|
||||||
|
if (ret == 0) {
|
||||||
|
serial_write("[DISK] MBR written to /dev/");
|
||||||
|
serial_write(disk->devname);
|
||||||
|
serial_write("\n");
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int disk_sync(Disk *disk) {
|
||||||
|
if (!disk) return -1;
|
||||||
|
Disk *target = disk->parent ? disk->parent : disk;
|
||||||
|
|
||||||
|
// Use device-specific sync if available
|
||||||
|
if (target->sync) return target->sync(target);
|
||||||
|
|
||||||
|
if (target->type == DISK_TYPE_IDE) {
|
||||||
|
ATADriverData *data = (ATADriverData *)target->driver_data;
|
||||||
|
if (!data) return -1;
|
||||||
|
uint64_t flags = spinlock_acquire_irqsave(&ide_lock);
|
||||||
|
if (ata_wait_bsy(data->port_base) == 0) {
|
||||||
|
outb(data->port_base + ATA_REG_HDDEVSEL, data->slave ? 0xB0 : 0xA0);
|
||||||
|
outb(data->port_base + ATA_REG_COMMAND, 0xE7);
|
||||||
|
ata_wait_bsy(data->port_base);
|
||||||
|
}
|
||||||
|
spinlock_release_irqrestore(&ide_lock, flags);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void disk_remove_partitions(Disk *parent) {
|
||||||
|
for (int i = 0; i < disk_count; i++) {
|
||||||
|
if (disks[i] && disks[i]->parent == parent) {
|
||||||
|
Disk *p = disks[i];
|
||||||
|
|
||||||
|
// Unmount from VFS if it's mounted
|
||||||
|
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, p->devname);
|
||||||
|
|
||||||
|
extern bool vfs_umount(const char *mount_path);
|
||||||
|
vfs_umount(mount_path);
|
||||||
|
|
||||||
|
for (int j = i; j < disk_count - 1; j++) {
|
||||||
|
disks[j] = disks[j + 1];
|
||||||
|
}
|
||||||
|
disks[disk_count - 1] = NULL;
|
||||||
|
disk_count--;
|
||||||
|
i--;
|
||||||
|
kfree(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parse_gpt_partitions(Disk *disk) {
|
||||||
|
uint8_t *buffer = (uint8_t*)kmalloc(512);
|
||||||
|
if (!buffer) return;
|
||||||
|
|
||||||
|
if (disk->read_sector(disk, 1, buffer) != 0) {
|
||||||
|
serial_write("[DISK] GPT header read failed on /dev/");
|
||||||
|
serial_write(disk->devname);
|
||||||
|
serial_write("\n");
|
||||||
|
kfree(buffer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GPT_Header *hdr = (GPT_Header *)buffer;
|
||||||
|
if (hdr->signature != 0x5452415020494645ULL) {
|
||||||
|
serial_write("[DISK] GPT signature missing on /dev/");
|
||||||
|
serial_write(disk->devname);
|
||||||
|
serial_write("\n");
|
||||||
|
kfree(buffer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t num_entries = hdr->num_partition_entries;
|
||||||
|
uint32_t entry_size = hdr->size_of_partition_entry;
|
||||||
|
uint64_t entry_lba = hdr->partition_entry_lba;
|
||||||
|
|
||||||
|
uint8_t *entry_buf = (uint8_t*)kmalloc(512);
|
||||||
|
if (!entry_buf) { kfree(buffer); return; }
|
||||||
|
|
||||||
|
int part_num = 1;
|
||||||
|
int part_count = 0;
|
||||||
|
for (uint32_t i = 0; i < num_entries && i < 128; i++) {
|
||||||
|
uint32_t entry_lba_offset = (uint32_t)entry_lba + (i * entry_size) / 512;
|
||||||
|
uint32_t entry_sector_offset = (i * entry_size) % 512;
|
||||||
|
|
||||||
|
if (disk->read_sector(disk, entry_lba_offset, entry_buf) != 0) break;
|
||||||
|
|
||||||
|
GPT_Entry *entry = (GPT_Entry *)(entry_buf + entry_sector_offset);
|
||||||
|
|
||||||
|
bool zero = true;
|
||||||
|
for (int j = 0; j < 16; j++) if (entry->type_guid[j] != 0) { zero = false; break; }
|
||||||
|
if (zero) continue;
|
||||||
|
|
||||||
|
uint32_t start = (uint32_t)entry->start_lba;
|
||||||
|
uint32_t end = (uint32_t)entry->end_lba;
|
||||||
|
uint32_t size = end - start + 1;
|
||||||
|
|
||||||
|
if (size == 0) continue;
|
||||||
|
|
||||||
|
static const uint8_t esp_guid[16] = {
|
||||||
|
0x28, 0x73, 0x2A, 0xC1, 0x1F, 0xF8, 0xD2, 0x11,
|
||||||
|
0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B
|
||||||
|
};
|
||||||
|
bool is_esp = true;
|
||||||
|
for (int j = 0; j < 16; j++) if (entry->type_guid[j] != esp_guid[j]) { is_esp = false; break; }
|
||||||
|
|
||||||
|
bool fat32 = false;
|
||||||
|
if (is_esp) fat32 = true;
|
||||||
|
else {
|
||||||
|
uint8_t *pbuf = (uint8_t*)kmalloc(512);
|
||||||
|
if (pbuf) {
|
||||||
|
if (disk->read_sector(disk, start, pbuf) == 0) {
|
||||||
|
fat32 = is_fat32_bpb(pbuf);
|
||||||
|
}
|
||||||
|
kfree(pbuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
disk_register_partition(disk, start, size, fat32, is_esp, part_num++);
|
||||||
|
part_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (part_count == 0) {
|
||||||
|
serial_write("[DISK] GPT found but no partitions registered on /dev/");
|
||||||
|
serial_write(disk->devname);
|
||||||
|
serial_write("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(entry_buf);
|
||||||
|
kfree(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
int disk_rescan(Disk *disk) {
|
||||||
|
if (!disk || disk->is_partition) return -1;
|
||||||
|
|
||||||
|
disk_remove_partitions(disk);
|
||||||
|
|
||||||
|
serial_write("[DISK] Rescanning /dev/");
|
||||||
|
serial_write(disk->devname);
|
||||||
|
serial_write("\n");
|
||||||
|
|
||||||
|
uint8_t *buffer = (uint8_t*)kmalloc(512);
|
||||||
|
if (buffer) {
|
||||||
|
if (disk->read_sector(disk, 1, buffer) == 0) {
|
||||||
|
GPT_Header *hdr = (GPT_Header*)buffer;
|
||||||
|
if (hdr->signature == 0x5452415020494645ULL) {
|
||||||
|
serial_write("[DISK] GPT detected on /dev/");
|
||||||
|
serial_write(disk->devname);
|
||||||
|
serial_write("\n");
|
||||||
|
kfree(buffer);
|
||||||
|
parse_gpt_partitions(disk);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
serial_write("[DISK] GPT probe read failed on /dev/");
|
||||||
|
serial_write(disk->devname);
|
||||||
|
serial_write("\n");
|
||||||
|
}
|
||||||
|
kfree(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_mbr_partitions(disk);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue