diff --git a/src/dev/ahci.c b/src/dev/ahci.c index 1ad2b8c..337c531 100644 --- a/src/dev/ahci.c +++ b/src/dev/ahci.c @@ -46,10 +46,9 @@ static void ahci_strcpy(char *d, const char *s) { // Kernel virtual to physical address conversion extern uint64_t v2p(uint64_t vaddr); - -// ============================================================================ -// Port Setup -// ============================================================================ +extern uint64_t p2v(uint64_t paddr); +static int ahci_disk_sync(Disk *disk); +static int ahci_find_free_slot(HBA_PORT *port); static void ahci_stop_cmd(HBA_PORT *port) { // 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->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); + int cmd_tbl_size = sizeof(HBA_CMD_TBL) + 32 * 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); @@ -135,10 +133,6 @@ static void ahci_port_rebase(ahci_port_state_t *ps) { 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++) { @@ -147,6 +141,76 @@ static int ahci_find_free_slot(HBA_PORT *port) { 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) { if (!ahci_initialized || port_num < 0 || port_num >= MAX_AHCI_PORTS) return -1; 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; 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 - 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; + extern uint64_t paging_get_pml4_phys(void); + extern uint64_t paging_virt2phys(uint64_t pml4_phys, uint64_t virtual_addr); + uint64_t pml4 = paging_get_pml4_phys(); + 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; // Setup Command FIS 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; 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); - 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; + // Setup PRDT - handle buffers spanning multiple physical pages + extern uint64_t paging_get_pml4_phys(void); + extern uint64_t paging_virt2phys(uint64_t pml4_phys, uint64_t virtual_addr); + uint64_t pml4 = paging_get_pml4_phys(); + 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->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); } +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 // ============================================================================ @@ -368,9 +500,7 @@ void ahci_init(void) { 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 + uint64_t abar_virt = p2v(abar_phys); for (uint64_t offset = 0; offset < 0x2000; offset += 4096) { paging_map_page(paging_get_pml4_phys(), abar_virt + offset, abar_phys + offset, @@ -409,6 +539,7 @@ void ahci_init(void) { ahci_port_rebase(&ports[i]); ports[i].active = true; active_port_count++; + ahci_initialized = true; // Register as a block device Disk *disk = (Disk*)kmalloc(sizeof(Disk)); @@ -418,85 +549,36 @@ void ahci_init(void) { 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; + + 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"); + disk->total_sectors = 0; + } + + disk->read_sector = ahci_disk_read_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->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); + extern int disk_rescan(Disk *disk); 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); - } + disk_rescan(disk); } } else if (type == 1) { serial_write("[AHCI] Port "); @@ -514,3 +596,52 @@ void ahci_init(void) { 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); +} + diff --git a/src/dev/ahci.h b/src/dev/ahci.h index 8c18536..22cf909 100644 --- a/src/dev/ahci.h +++ b/src/dev/ahci.h @@ -152,6 +152,12 @@ typedef struct { #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 +#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 diff --git a/src/dev/disk.h b/src/dev/disk.h index 986678e..21ebc2d 100644 --- a/src/dev/disk.h +++ b/src/dev/disk.h @@ -18,16 +18,18 @@ typedef enum { } DiskType; typedef struct Disk { - char devname[16]; // Device name: "sda", "sdb", "sda1", etc. + char devname[16]; DiskType type; bool is_fat32; - 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 + bool is_esp; + char label[32]; + uint32_t partition_lba_offset; + uint32_t total_sectors; 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 (*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 void *driver_data; @@ -38,6 +40,17 @@ typedef struct Disk { bool registered; } 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 void disk_manager_init(void); void disk_manager_scan(void); @@ -45,7 +58,7 @@ 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); + bool is_fat32, bool is_esp, int part_num); // Lookup 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); 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 diff --git a/src/dev/disk_manager.c b/src/dev/disk_manager.c index 83645c8..8e8a485 100644 --- a/src/dev/disk_manager.c +++ b/src/dev/disk_manager.c @@ -227,6 +227,105 @@ static int ata_write_sector(Disk *disk, uint32_t lba, const uint8_t *buffer) { 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 === const char* disk_get_next_dev_name(void) { @@ -257,11 +356,13 @@ void disk_register(Disk *disk) { serial_write(disk->devname); serial_write(" ("); 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, - bool is_fat32, int part_num) { + bool is_fat32, bool is_esp, int part_num) { if (disk_count >= MAX_DISKS) return; 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->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->total_sectors = sector_count; 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_num(sector_count); serial_write(" sectors, FAT32="); - serial_write(" sectors, FAT32="); serial_write(is_fat32 ? "yes" : "no"); + if (is_esp) serial_write(", ESP=yes"); serial_write(")\n"); if (is_fat32) { @@ -411,12 +513,18 @@ static void parse_mbr_partitions(Disk *disk) { if (!buffer) return; 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); return; } // Check for valid MBR signature if (buffer[510] != 0x55 || buffer[511] != 0xAA) { + serial_write("[DISK] Invalid MBR signature on /dev/"); + serial_write(disk->devname); + serial_write("\n"); kfree(buffer); return; } @@ -424,6 +532,7 @@ static void parse_mbr_partitions(Disk *disk) { MBR_PartitionEntry *partitions = (MBR_PartitionEntry*)&buffer[446]; int part_num = 1; + int part_count = 0; for (int i = 0; i < 4; i++) { uint32_t start = partitions[i].lba_start; 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, - partitions[i].sector_count, fat32, part_num); + partitions[i].sector_count, fat32, false, part_num); + part_count++; part_num++; } @@ -457,6 +567,10 @@ static void parse_mbr_partitions(Disk *disk) { serial_write("\n"); disk->is_fat32 = true; 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); @@ -479,6 +593,8 @@ static void try_add_ata_drive(uint16_t port, bool slave, const char *name) { dm_strcpy(new_disk->label, name); new_disk->read_sector = ata_read_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->partition_lba_offset = 0; new_disk->total_sectors = sectors; @@ -504,8 +620,6 @@ void disk_manager_init(void) { next_drive_letter_idx = 0; 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) { @@ -522,4 +636,408 @@ void disk_manager_scan(void) { } else { 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; } \ No newline at end of file