diff options
Diffstat (limited to 'src/kernel/ide.c')
-rw-r--r-- | src/kernel/ide.c | 226 |
1 files changed, 121 insertions, 105 deletions
diff --git a/src/kernel/ide.c b/src/kernel/ide.c index a2d3895..a32f149 100644 --- a/src/kernel/ide.c +++ b/src/kernel/ide.c @@ -1,171 +1,192 @@ -#include <stdint.h> #include <stdbool.h> +#include <stdint.h> +#include "drive.h" #include "panic.h" #include "util.h" -#include "drive.h" -#include "pci.h" #include "ata.h" +#include "pci.h" #define MAX_IDE_DRIVES 8 struct ide_drive_info { uint16_t base_port; - uint16_t control_port; + uint16_t alt_port; bool slave; }; struct ide_drive_info ide_drives[MAX_IDE_DRIVES]; drive_id_t n_ide_drives = 0; -void spin_delay() { - for (uint32_t i = -1; i; --i) - ; -} - -//return true on error condition -bool poll(struct ide_drive_info *info) { - spin_delay(); - - uint8_t status; +typedef uint16_t spinner_t; - while ((status = inb(info->base_port | ATA_REG_STATUS)) & ATA_STATUS_BUSY) - ; +//returns the status after waiting +uint8_t wait_after_cmd(uint16_t base_port) { + for (spinner_t n = -1; n; --n) { + uint8_t s = inb(base_port | ATA_REG_STATUS); + if (s & ATA_STATUS_ERROR) + panic("Error status in IDE driver."); + if (!(s & ATA_STATUS_BUSY)) + return s; + } + panic("Spun out in IDE driver."); +} - return (status & (ATA_STATUS_ERROR | ATA_STATUS_DRIVE_FAULT)) || !(status & ATA_STATUS_DRIVE_READY); +//returns the status after waiting +uint8_t wait_for_ready(uint16_t base_port) { + for (spinner_t n = -1; n; --n) { + uint8_t s = inb(base_port | ATA_REG_STATUS); + if (s & ATA_STATUS_ERROR) + panic("Error status in IDE driver."); + if (s & ATA_STATUS_DRIVE_READY) + return s; + } + panic("Spun out in IDE driver."); } -uint8_t ide_ata_rs(struct drive *d, uint32_t start, uint32_t count, void *buffer) { - if (start & 0xf0000000) - panic("IDE ATA driver only supports LBA28 addressing currently."); +//returns the status after waiting +uint8_t wait_for_error_or_ready(uint16_t base_port) { + for (spinner_t n = -1; n; --n) { + uint8_t s = inb(base_port | ATA_REG_STATUS); + if (s & (ATA_STATUS_DRIVE_READY | ATA_STATUS_ERROR)) + return s; + } + panic("Spun out in IDE driver."); +} - struct ide_drive_info *info = ide_drives + d->drive_id; +//returns the status after waiting +uint8_t wait_for_data_ready_not_busy(uint16_t base_port) { + for (spinner_t n = -1; n; --n) { + uint8_t s = inb(base_port | ATA_REG_STATUS); + if (!(s & ATA_STATUS_BUSY) && (s & ATA_STATUS_DATA_READY)) + return s; + if (s & ATA_STATUS_ERROR) + panic("Error status in IDE driver."); + } + panic("Spun out in IDE driver."); +} +uint8_t ide_ata_rs(const struct drive *d, uint32_t start, uint32_t count, void *buffer) { + if (start >= d->n_sectors) + return 0; if (start + count > d->n_sectors) count = d->n_sectors - start; + if (!count) + return 0; + if (start & 0xf0000000) + panic("IDE ATA driver does not support reads starting past 256MiB currently."); if (count & 0xffffff00) - panic("IDE ATA driver only supports reading up to 128k at a time currently."); + panic("IDE ATA driver does not support reads over 128kiB in length currently."); uint32_t lba = start & 0x00ffffff; - uint32_t spin = -1; - while (inb(info->base_port | ATA_REG_STATUS) & ATA_STATUS_BUSY) - if (!spin--) - panic("Spun out in IDE ATA reading"); + struct ide_drive_info *info = ide_drives + d->drive_id; outb(info->base_port | ATA_REG_SELECT, (info->slave ? 0xf0 : 0xe0) | (start >> 24)); - outb(info->base_port | ATA_REG_COUNT_LOW, count); + //spin? + + outb(info->base_port | ATA_REG_COUNT, count); outb(info->base_port | ATA_REG_LBA0, lba); outb(info->base_port | ATA_REG_LBA1, lba >> 8); outb(info->base_port | ATA_REG_LBA2, lba >> 16); - uint16_t *buffer_16 = buffer; + outb(info->base_port | ATA_REG_CMD, ATA_CMD_READ); + + uint16_t *buf16 = (uint16_t *)buffer; for (uint16_t i = 0; i < count; ++i) { - if (poll(info)) - return i; + wait_for_data_ready_not_busy(info->base_port); for (uint16_t j = 0; j < 256; ++j) - *buffer_16++ = inw(info->base_port + ATA_REG_DATA); + *buf16++ = inw(info->base_port | ATA_REG_DATA); } return count; } -uint8_t ide_ata_ws(struct drive *d, uint32_t start, uint32_t count, void *buffer) { +uint8_t ide_ata_ws(const struct drive *d, uint32_t start, uint32_t count, const void *buffer) { panic("IDE ATA writing not implemented yet"); return 0; } -uint8_t ide_atapi_rs(struct drive *d, uint32_t start, uint32_t count, void *buffer) { +uint8_t ide_atapi_rs(const struct drive *d, uint32_t start, uint32_t count, void *buffer) { //panic("IDE ATAPI reading not implemented yet"); return 0; } -uint8_t ide_atapi_ws(struct drive *d, uint32_t start, uint32_t count, void *buffer) { +uint8_t ide_atapi_ws(const struct drive *d, uint32_t start, uint32_t count, const void *buffer) { panic("IDE ATAPI writing not implemented yet"); return 0; } struct id_space { - uint16_t device_type;//0 - uint8_t skip1[98 - 2]; - uint16_t capabilities;//98 - uint8_t skip2[120 - (98 + 2)]; + uint8_t skip1[120]; uint32_t max_lba;//120 - uint8_t skip3[164 - (120 + 4)]; - uint32_t command_sets;//164 - uint8_t skip4[200 - (164 + 4)]; - uint32_t max_lba_ext;//200 - uint8_t skip5[512 - (200 + 4)]; -} __attribute__ ((__packed__)); - -void read_id_space(uint16_t base_port, struct id_space *to) { - uint32_t *to_32 = (uint32_t *)to; - for (uint8_t i = 0; i < 128; ++i) - *(to_32++) = ind(base_port | ATA_REG_DATA); -} + uint8_t skip2[512 - (120 + 4)]; +} __attribute__ ((__packed__ )); -void test_drive(uint16_t base_port, uint16_t control_port, bool slave) { - outb(base_port | ATA_REG_SELECT, slave ? ATA_SLAVE_SELECT : ATA_MASTER_SELECT); - spin_delay(); +void vga_printsz(char *s); - outb(base_port | ATA_REG_CMD, ATA_CMD_ID); - spin_delay(); +void test_drive(uint16_t base_port, uint16_t alt_port, bool slave) { + if (n_ide_drives == MAX_IDE_DRIVES) + panic("Maximum number of IDE drives reached."); - if (!inb(base_port | ATA_REG_STATUS)) - return; + struct ide_drive_info *next = ide_drives + n_ide_drives; + struct drive next_d; + next_d.drive_id = n_ide_drives; - if (n_ide_drives == MAX_IDE_DRIVES) - panic("Maximum IDE drives reached"); + next->base_port = base_port; + next->alt_port = alt_port; + next->slave = slave; - outb(base_port | ATA_REG_CONTROL, ATA_CONTROL_NO_IRQS); + outb(base_port | ATA_REG_SELECT, slave ? 0xb0 : 0xa0); + outb(base_port | ATA_REG_COUNT, 0); + outb(base_port | ATA_REG_LBA0, 0); + outb(base_port | ATA_REG_LBA1, 0); + outb(base_port | ATA_REG_LBA2, 0); - struct ide_drive_info *this_drive = ide_drives + n_ide_drives; - this_drive->base_port = base_port; - this_drive->control_port = control_port; - this_drive->slave = slave; + outb(base_port | ATA_REG_CMD, ATA_CMD_ID); - struct drive data; - data.drive_id = n_ide_drives; + if (!inb(base_port | ATA_REG_STATUS)) + return; - uint32_t spin_out = -1; - while (1) {//wait for identify to complete - uint8_t status = inb(base_port | ATA_REG_STATUS); - if (status & ATA_STATUS_ERROR) { - uint16_t type = inb(base_port | ATA_REG_LBA1) + - (inb(base_port | ATA_REG_LBA2) << 8); + struct id_space ids; - //These two are listed on OSDev Wiki - // as being ATAPI device types. - if ((type != 0xeb14) && (type != 0x9669)) - return; + if (!(wait_for_error_or_ready(base_port) & ATA_STATUS_ERROR)) { + wait_for_ready(base_port); + for (uint16_t *ids_16 = (uint16_t *)&ids; ids_16 < (uint16_t *)&ids + 256; ++ids_16) + *ids_16 = inw(base_port | ATA_REG_DATA); - outb(base_port | ATA_REG_CMD, ATAPI_CMD_ID); + next_d.drive_type = "IDE PATA"; + next_d.read_sectors = &ide_ata_rs; + next_d.write_sectors = &ide_ata_ws; + } + else { + uint16_t code = inb(base_port | 0x4) + (inb(base_port | 0x5) << 8); + if (!code) { + panic("PATA identification aborted."); + } + if (code == 0xeb14) { + next_d.drive_type = "IDE PATAPI"; + next_d.read_sectors = &ide_atapi_rs; + next_d.write_sectors = &ide_atapi_ws; - data.read_sectors = &ide_atapi_rs; - data.write_sectors = &ide_atapi_ws; - data.drive_type = "IDE ATAPI"; - break; + //TODO: issue proper identify command + ids.max_lba = -1; } - if (!(status & ATA_STATUS_BUSY) && (status & ATA_STATUS_DATA_READY)) { - data.read_sectors = &ide_ata_rs; - data.write_sectors = &ide_ata_ws; - data.drive_type = "IDE ATA"; - break; + else if (code == 0xc33c) { + next_d.drive_type = "IDE SATA"; + + //TODO: set up drive hooks, issue proper identify command + return; } - if (!spin_out--) - panic("IDE ATA identify won't complete"); } - struct id_space id; - read_id_space(base_port, &id); - - if (!(id.capabilities & ATA_CAP_LBA)) - //TODO: CHS compability driver? - return; - data.n_sectors = (id.command_sets & ATA_SET_EXT) ? id.max_lba_ext : id.max_lba; + //in the future, maybe add support for 48-bit LBA, and/or CHS addressing + if (!ids.max_lba) + panic("Encountered ATA drive that doesn't support 28-bit LBA"); + next_d.n_sectors = ids.max_lba; - commit_drive(data); + commit_drive(next_d); ++n_ide_drives; } @@ -176,14 +197,9 @@ void init_ide() { while ((device = find_pci_device_from_class_and_subclass(PCI_MASS_STORAGE, PCI_IDE, check_from, &check_from))) { ++check_from; - uint16_t primary_base_port = device->bar0 <= 1 ? 0x01f0 : device->bar0; - uint16_t primary_control_port = device->bar1 <= 1 ? 0x03f6 : device->bar1; - uint16_t secondary_base_port = device->bar2 <= 1 ? 0x0170 : device->bar2; - uint16_t secondary_control_port = device->bar3 <= 1 ? 0x0376 : device->bar3; - - test_drive(primary_base_port, primary_control_port, false); - test_drive(primary_base_port, primary_control_port, true); - test_drive(secondary_base_port, secondary_control_port, false); - test_drive(secondary_base_port, secondary_control_port, true); + test_drive(0x01f0, 0x03f6, false); + test_drive(0x01f0, 0x03f6, true); + test_drive(0x0170, 0x0376, false); + test_drive(0x0170, 0x0376, true); } }
\ No newline at end of file |