summaryrefslogtreecommitdiff
path: root/src/kernel/ide.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/kernel/ide.c')
-rw-r--r--src/kernel/ide.c226
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