summaryrefslogtreecommitdiff
path: root/src/kernel/ide.c
diff options
context:
space:
mode:
authorBenji Dial <Benji3.141@gmail.com>2020-08-11 11:33:21 -0400
committerBenji Dial <Benji3.141@gmail.com>2020-08-11 11:33:21 -0400
commit63167f223e1f54910f6b80e698390ee60aec79ee (patch)
tree41844f646bdcb5c9ba241bb5867c5e4f51737d52 /src/kernel/ide.c
parent77d7a284c02bc6b1b3a3a92ad5d957172cee9b81 (diff)
downloadportland-os-63167f223e1f54910f6b80e698390ee60aec79ee.tar.gz
lots of progress
currently, BAR fields of IDE drives are all returning zero, and the ATA read function isn't working. i'm not sure why. i'm going to work on VESA next, and come back to the IDE driver later
Diffstat (limited to 'src/kernel/ide.c')
-rw-r--r--src/kernel/ide.c189
1 files changed, 189 insertions, 0 deletions
diff --git a/src/kernel/ide.c b/src/kernel/ide.c
new file mode 100644
index 0000000..a2d3895
--- /dev/null
+++ b/src/kernel/ide.c
@@ -0,0 +1,189 @@
+#include <stdint.h>
+#include <stdbool.h>
+#include "panic.h"
+#include "util.h"
+#include "drive.h"
+#include "pci.h"
+#include "ata.h"
+
+#define MAX_IDE_DRIVES 8
+
+struct ide_drive_info {
+ uint16_t base_port;
+ uint16_t control_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;
+
+ while ((status = inb(info->base_port | ATA_REG_STATUS)) & ATA_STATUS_BUSY)
+ ;
+
+ return (status & (ATA_STATUS_ERROR | ATA_STATUS_DRIVE_FAULT)) || !(status & ATA_STATUS_DRIVE_READY);
+}
+
+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.");
+
+ struct ide_drive_info *info = ide_drives + d->drive_id;
+
+ if (start + count > d->n_sectors)
+ count = d->n_sectors - start;
+
+ if (count & 0xffffff00)
+ panic("IDE ATA driver only supports reading up to 128k at a time 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");
+
+ outb(info->base_port | ATA_REG_SELECT, (info->slave ? 0xf0 : 0xe0) | (start >> 24));
+ outb(info->base_port | ATA_REG_COUNT_LOW, 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;
+
+ for (uint16_t i = 0; i < count; ++i) {
+ if (poll(info))
+ return i;
+ for (uint16_t j = 0; j < 256; ++j)
+ *buffer_16++ = 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) {
+ 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) {
+ //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) {
+ 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)];
+ 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);
+}
+
+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();
+
+ outb(base_port | ATA_REG_CMD, ATA_CMD_ID);
+ spin_delay();
+
+ if (!inb(base_port | ATA_REG_STATUS))
+ return;
+
+ if (n_ide_drives == MAX_IDE_DRIVES)
+ panic("Maximum IDE drives reached");
+
+ outb(base_port | ATA_REG_CONTROL, ATA_CONTROL_NO_IRQS);
+
+ 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;
+
+ struct drive data;
+ data.drive_id = n_ide_drives;
+
+ 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);
+
+ //These two are listed on OSDev Wiki
+ // as being ATAPI device types.
+ if ((type != 0xeb14) && (type != 0x9669))
+ return;
+
+ outb(base_port | ATA_REG_CMD, ATAPI_CMD_ID);
+
+ data.read_sectors = &ide_atapi_rs;
+ data.write_sectors = &ide_atapi_ws;
+ data.drive_type = "IDE ATAPI";
+ break;
+ }
+ 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;
+ }
+ 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;
+
+ commit_drive(data);
+ ++n_ide_drives;
+}
+
+void init_ide() {
+ uint16_t check_from = 0;
+ struct pci_device *device;
+ //double parentheses to let gcc know this assignment isn't a mistake
+ 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);
+ }
+} \ No newline at end of file