aboutsummaryrefslogtreecommitdiff
path: root/src/kernel
diff options
context:
space:
mode:
authorAlex Auvolat <alex.auvolat@ens.fr>2015-02-24 15:08:01 +0100
committerAlex Auvolat <alex.auvolat@ens.fr>2015-02-24 15:08:01 +0100
commit7e908dabaaf6c67ef5000406a0bb3a6a29beca01 (patch)
treeb0516cc4771ca3a8e202327d2864804afefe4cfc /src/kernel
parent91c5969cdddf2241418082998e76bdbb836ed03e (diff)
downloadkogata-7e908dabaaf6c67ef5000406a0bb3a6a29beca01.tar.gz
kogata-7e908dabaaf6c67ef5000406a0bb3a6a29beca01.zip
Add PCI IDE driver (only PIO mode, no DMA yet.)
Diffstat (limited to 'src/kernel')
-rw-r--r--src/kernel/Makefile2
-rw-r--r--src/kernel/core/idt.c1
-rw-r--r--src/kernel/core/kmain.c2
-rw-r--r--src/kernel/dev/pci.c8
-rw-r--r--src/kernel/dev/pciide.c762
-rw-r--r--src/kernel/include/dev/ata.h7
-rw-r--r--src/kernel/include/dev/pci.h12
-rw-r--r--src/kernel/include/dev/pciide.h114
-rw-r--r--src/kernel/include/sys.h31
9 files changed, 925 insertions, 14 deletions
diff --git a/src/kernel/Makefile b/src/kernel/Makefile
index 0c9a62b..a4e0edb 100644
--- a/src/kernel/Makefile
+++ b/src/kernel/Makefile
@@ -3,7 +3,7 @@ OBJ = core/loader.o core/dbglog.o \
core/gdt.o core/idt.o core/interrupt.o core/context_switch.o core/thread.o \
core/frame.o core/paging.o core/region.o core/kmalloc.o core/worker.o \
user/vfs.o user/nullfs.o user/process.o user/elf.o user/syscall.o \
- dev/pci.o
+ dev/pci.o dev/pciide.o
LIB = ../common/libc/libc.lib ../common/libkogata/libkogata.lib ../common/libalgo/libalgo.lib
diff --git a/src/kernel/core/idt.c b/src/kernel/core/idt.c
index abab3cc..d34d03f 100644
--- a/src/kernel/core/idt.c
+++ b/src/kernel/core/idt.c
@@ -112,6 +112,7 @@ void idt_irq_handler(registers_t *regs) {
}
outb(0x20, 0x20);
+ if (regs->err_code != 0) dbg_printf("IRQ%d\n", regs->err_code);
if (irq_handlers[regs->err_code] != 0) {
irq_handlers[regs->err_code](regs);
}
diff --git a/src/kernel/core/kmain.c b/src/kernel/core/kmain.c
index c77750a..9bf9d0d 100644
--- a/src/kernel/core/kmain.c
+++ b/src/kernel/core/kmain.c
@@ -24,6 +24,7 @@
#include <string.h>
#include <dev/pci.h>
+#include <dev/pciide.h>
// ===== FOR TESTS =====
#define TEST_PLACEHOLDER_AFTER_IDT
@@ -135,6 +136,7 @@ void kernel_init_stage2(void* data) {
// Scan for devices
pci_setup();
+ pciide_detect(iofs);
// Add kernel command line to iofs
{
diff --git a/src/kernel/dev/pci.c b/src/kernel/dev/pci.c
index cfd2318..147eff5 100644
--- a/src/kernel/dev/pci.c
+++ b/src/kernel/dev/pci.c
@@ -193,21 +193,21 @@ void pci_setup() {
void pci_irq_storage(registers_t *r) {
for (int i = 0; i < PCI_MAX_DEVICES; i++) {
if (pci_devices[i].irq == PCI_IRQ_STORAGE && pci_devices[i].irq_handler != 0)
- pci_devices[i].irq_handler();
+ pci_devices[i].irq_handler(i);
}
}
void pci_irq_network(registers_t *r) {
for (int i = 0; i < PCI_MAX_DEVICES; i++) {
if (pci_devices[i].irq == PCI_IRQ_NETWORK && pci_devices[i].irq_handler != 0)
- pci_devices[i].irq_handler();
+ pci_devices[i].irq_handler(i);
}
}
void pci_irq_default(registers_t *r) {
for (int i = 0; i < PCI_MAX_DEVICES; i++) {
if (pci_devices[i].irq == PCI_IRQ_DEFAULT && pci_devices[i].irq_handler != 0)
- pci_devices[i].irq_handler();
+ pci_devices[i].irq_handler(i);
}
}
@@ -217,8 +217,8 @@ void pci_set_irq(int dev_id, uint8_t irq) {
&& irq != PCI_IRQ_STORAGE && irq != PCI_IRQ_NETWORK
&& irq != PCI_IRQ_DISABLE) return;
- pci_devices[dev_id].irq = irq;
pci_write_config_byte(dev_id, PCI_CONFIG_INTERRUPT_LINE, irq);
+ pci_devices[dev_id].irq = pci_read_config_byte(dev_id, PCI_CONFIG_INTERRUPT_LINE);
}
/* vim: set ts=4 sw=4 tw=0 noet :*/
diff --git a/src/kernel/dev/pciide.c b/src/kernel/dev/pciide.c
new file mode 100644
index 0000000..58bccfd
--- /dev/null
+++ b/src/kernel/dev/pciide.c
@@ -0,0 +1,762 @@
+#include <string.h>
+#include <malloc.h>
+#include <thread.h>
+#include <nullfs.h>
+#include <printf.h>
+
+#include <dev/pci.h>
+#include <dev/pciide.h>
+
+static void ide_register_device(ide_controller_t *c, uint8_t device, fs_t *iofs);
+
+// ================================ //
+// HELPER FUNCTIONS FOR PCI IDE I/O //
+// ================================ //
+
+static void ide_write(ide_controller_t *c, uint8_t channel, uint8_t reg, uint8_t data) {
+ if (reg > 0x07 && reg < 0x0C) {
+ ide_write(c, channel, ATA_REG_CONTROL, 0x80 | c->channels[channel].nIEN);
+ }
+ if (reg < 0x08) {
+ outb(c->channels[channel].base + reg - 0x00, data);
+ } else if (reg < 0x0C) {
+ outb(c->channels[channel].base + reg - 0x06, data);
+ } else if (reg < 0x0E) {
+ outb(c->channels[channel].ctrl + reg - 0x0A, data);
+ } else if (reg < 0x16) {
+ outb(c->channels[channel].bmide + reg - 0x0E, data);
+ }
+ if (reg > 0x07 && reg < 0x0C) {
+ ide_write(c, channel, ATA_REG_CONTROL, c->channels[channel].nIEN);
+ }
+}
+
+static uint8_t ide_read(ide_controller_t *c, uint8_t channel, uint8_t reg) {
+ uint8_t result = 0;
+ if (reg > 0x07 && reg < 0x0C) {
+ ide_write(c, channel, ATA_REG_CONTROL, 0x80 | c->channels[channel].nIEN);
+ }
+ if (reg < 0x08) {
+ result = inb(c->channels[channel].base + reg - 0x00);
+ } else if (reg < 0x0C) {
+ result = inb(c->channels[channel].base + reg - 0x06);
+ } else if (reg < 0x0E) {
+ result = inb(c->channels[channel].ctrl + reg - 0x0A);
+ } else if (reg < 0x16) {
+ result = inb(c->channels[channel].bmide + reg - 0x0E);
+ }
+ if (reg > 0x07 && reg < 0x0C) {
+ ide_write(c, channel, ATA_REG_CONTROL, c->channels[channel].nIEN);
+ }
+ return result;
+}
+
+static void ide_read_buffer(ide_controller_t *c, uint8_t channel, uint8_t reg, void* buffer, size_t quads) {
+ if (reg > 0x07 && reg < 0x0C) {
+ ide_write(c, channel, ATA_REG_CONTROL, 0x80 | c->channels[channel].nIEN);
+ }
+ if (reg < 0x08) {
+ insl(c->channels[channel].base + reg - 0x00, buffer, quads);
+ } else if (reg < 0x0C) {
+ insl(c->channels[channel].base + reg - 0x06, buffer, quads);
+ } else if (reg < 0x0E) {
+ insl(c->channels[channel].ctrl + reg - 0x0A, buffer, quads);
+ } else if (reg < 0x16) {
+ insl(c->channels[channel].bmide + reg - 0x0E, buffer, quads);
+ }
+ if (reg > 0x07 && reg < 0x0C) {
+ ide_write(c, channel, ATA_REG_CONTROL, c->channels[channel].nIEN);
+ }
+}
+
+static int ide_polling(ide_controller_t *c, uint8_t channel, bool advanced_check) {
+ // (I) Delay 400 nanosecond for BSY to be set:
+ for(int i = 0; i < 4; i++)
+ ide_read(c, channel, ATA_REG_ALTSTATUS); // Reading the Alternate Status port wastes 100ns; loop four times.
+
+ // (II) Wait for BSY to be cleared:
+ while (ide_read(c, channel, ATA_REG_STATUS) & ATA_SR_BSY);
+
+ if (advanced_check) {
+ uint8_t state = ide_read(c, channel, ATA_REG_STATUS); // Read Status Register.
+
+ // (III) Check For Errors:
+ if (state & ATA_SR_ERR)
+ return 2; // Error.
+
+ // (IV) Check If Device fault:
+ if (state & ATA_SR_DF)
+ return 1; // Device Fault.
+
+ // (V) Check DRQ:
+ // BSY = 0; DF = 0; ERR = 0 so we should check for DRQ now.
+ if ((state & ATA_SR_DRQ) == 0)
+ return 3; // DRQ should be set
+ }
+ return 0; // ok
+}
+
+static uint8_t ide_print_error(ide_controller_t *c, int drive, uint8_t err) {
+ if (err == 0)
+ return err;
+
+ dbg_printf("IDE: ");
+ if (err == 1) {
+ dbg_printf("- Device Fault\n ");
+ err = 19;
+ } else if (err == 2) {
+ uint8_t st = ide_read(c, c->devices[drive].channel, ATA_REG_ERROR);
+ if (st & ATA_ER_AMNF) {
+ dbg_printf("- No Address Mark Found\n ");
+ err = 7;
+ }
+ if (st & ATA_ER_TK0NF) {
+ dbg_printf("- No Media or Media Error\n ");
+ err = 3;
+ }
+ if (st & ATA_ER_ABRT) {
+ dbg_printf("- Command Aborted\n ");
+ err = 20;
+ }
+ if (st & ATA_ER_MCR) {
+ dbg_printf("- No Media or Media Error\n ");
+ err = 3;
+ }
+ if (st & ATA_ER_IDNF) {
+ dbg_printf("- ID mark not Found\n ");
+ err = 21;
+ }
+ if (st & ATA_ER_MC) {
+ dbg_printf("- No Media or Media Error\n ");
+ err = 3;
+ }
+ if (st & ATA_ER_UNC) {
+ dbg_printf("- Uncorrectable Data Error\n ");
+ err = 22;
+ }
+ if (st & ATA_ER_BBK) {
+ dbg_printf("- Bad Sectors\n ");
+ err = 13;
+ }
+ } else if (err == 3) {
+ dbg_printf("- Reads Nothing\n ");
+ err = 23;
+ } else if (err == 4) {
+ dbg_printf("- Write Protected\n ");
+ err = 8;
+ }
+ dbg_printf("- [%s %s] %s\n",
+ (const char *[]){"Primary", "Secondary"}[c->devices[drive].channel],
+ (const char *[]){"Master", "Slave"}[c->devices[drive].drive],
+ c->devices[drive].model);
+
+ return err;
+}
+
+// ============ //
+// IRQ HANDLERS //
+// ============ //
+
+STATIC_MUTEX(on_pciirq);
+STATIC_MUTEX(on_irq14);
+STATIC_MUTEX(on_irq15);
+
+static thread_t *wait_irq14 = 0, *wait_irq15 = 0, *wait_pciirq = 0;
+
+void irq14_handler(registers_t *regs) {
+ if (wait_irq14) {
+ resume_thread(wait_irq14, true);
+ wait_irq14 = 0;
+ }
+}
+
+void irq15_handler(registers_t *regs) {
+ if (wait_irq15) {
+ resume_thread(wait_irq15, true);
+ wait_irq15 = 0;
+ }
+}
+
+void pciirq_handler(int pci_id) {
+ if (wait_pciirq) {
+ resume_thread(wait_pciirq, true);
+ wait_pciirq = 0;
+ }
+}
+
+static void ide_prewait_irq(ide_controller_t *c, int channel) {
+ int irq = c->channels[channel].irq;
+ if (irq == 14) {
+ mutex_lock(&on_irq14);
+ wait_irq14 = current_thread;
+ } else if (irq == 15) {
+ mutex_lock(&on_irq15);
+ wait_irq15 = current_thread;
+ } else {
+ mutex_lock(&on_pciirq);
+ wait_pciirq = current_thread;
+ }
+}
+
+static void ide_wait_irq(ide_controller_t *c, int channel) {
+ asm volatile("cli");
+ int irq = c->channels[channel].irq;
+ if (irq == 14) {
+ if (wait_irq14) pause();
+ mutex_unlock(&on_irq14);
+ } else if (irq == 15) {
+ if (wait_irq15) pause();
+ mutex_unlock(&on_irq15);
+ } else {
+ if (wait_pciirq) pause();
+ mutex_unlock(&on_pciirq);
+ }
+ asm volatile("sti");
+}
+
+// ===================================== //
+// ACTUAL READING AND WRITING OF SECTORS //
+// ===================================== //
+
+static uint8_t ide_ata_access(ide_controller_t *c, int direction,
+ uint8_t drive, uint32_t lba, uint8_t numsects, void* ptr)
+{
+ uint32_t channel = c->devices[drive].channel; // Read the Channel.
+ uint32_t slavebit = c->devices[drive].drive; // Read the Drive [Master/Slave]
+ uint32_t bus = c->channels[channel].base; // Bus Base, like 0x1F0 which is also data port.
+ const uint32_t words = 256; // Almost every ATA drive has a sector-size of 512-byte.
+
+ uint16_t cyl;
+ uint8_t head, sect, err;
+
+ // (I) Select one from LBA28, LBA48 or CHS;
+ uint8_t lba_mode /* 0: CHS, 1:LBA28, 2: LBA48 */;
+ uint8_t lba_io[6];
+ if (lba >= 0x10000000) { // Sure Drive should support LBA in this case, or you are
+ // giving a wrong LBA.
+ // LBA48:
+ lba_mode = 2;
+ lba_io[0] = (lba & 0x000000FF) >> 0;
+ lba_io[1] = (lba & 0x0000FF00) >> 8;
+ lba_io[2] = (lba & 0x00FF0000) >> 16;
+ lba_io[3] = (lba & 0xFF000000) >> 24;
+ lba_io[4] = 0;
+ lba_io[5] = 0;
+ head = 0; // Lower 4-bits of HDDEVSEL are not used here.
+ } else if (c->devices[drive].cap & 0x200) { // Drive supports LBA?
+ // LBA28:
+ lba_mode = 1;
+ lba_io[0] = (lba & 0x00000FF) >> 0;
+ lba_io[1] = (lba & 0x000FF00) >> 8;
+ lba_io[2] = (lba & 0x0FF0000) >> 16;
+ lba_io[3] = 0; // These Registers are not used here.
+ lba_io[4] = 0; // These Registers are not used here.
+ lba_io[5] = 0; // These Registers are not used here.
+ head = (lba & 0xF000000) >> 24;
+ } else {
+ // CHS:
+ lba_mode = 0;
+ sect = (lba % 63) + 1;
+ cyl = (lba + 1 - sect) / (16 * 63);
+ lba_io[0] = sect;
+ lba_io[1] = (cyl >> 0) & 0xFF;
+ lba_io[2] = (cyl >> 8) & 0xFF;
+ lba_io[3] = 0;
+ lba_io[4] = 0;
+ lba_io[5] = 0;
+ head = (lba + 1 - sect) % (16 * 63) / (63); // Head number is written to HDDEVSEL lower 4-bits.
+ }
+
+ // (II) See if drive supports DMA or not;
+ bool dma = false; // We don't support DMA
+
+ // (III) Wait if the drive is busy;
+ while (ide_read(c, channel, ATA_REG_STATUS) & ATA_SR_BSY); // Wait if busy.
+
+ // (IV) Select Drive from the controller;
+ if (lba_mode == 0) {
+ ide_write(c, channel, ATA_REG_HDDEVSEL, 0xA0 | (slavebit << 4) | head); // Drive & CHS.
+ } else {
+ ide_write(c, channel, ATA_REG_HDDEVSEL, 0xE0 | (slavebit << 4) | head); // Drive & LBA
+ }
+
+ // (V) Write Parameters;
+ if (lba_mode == 2) {
+ ide_write(c, channel, ATA_REG_SECCOUNT1, 0);
+ ide_write(c, channel, ATA_REG_LBA3, lba_io[3]);
+ ide_write(c, channel, ATA_REG_LBA4, lba_io[4]);
+ ide_write(c, channel, ATA_REG_LBA5, lba_io[5]);
+ }
+ ide_write(c, channel, ATA_REG_SECCOUNT0, numsects);
+ ide_write(c, channel, ATA_REG_LBA0, lba_io[0]);
+ ide_write(c, channel, ATA_REG_LBA1, lba_io[1]);
+ ide_write(c, channel, ATA_REG_LBA2, lba_io[2]);
+
+ // (VI) Select the command and send it;
+ // Routine that is followed:
+ // If ( DMA & LBA48) DO_DMA_EXT;
+ // If ( DMA & LBA28) DO_DMA_LBA;
+ // If ( DMA & LBA28) DO_DMA_CHS;
+ // If (!DMA & LBA48) DO_PIO_EXT;
+ // If (!DMA & LBA28) DO_PIO_LBA;
+ // If (!DMA & !LBA#) DO_PIO_CHS;
+ uint8_t cmd;
+ if (lba_mode == 0 && !dma && direction == 0) cmd = ATA_CMD_READ_PIO;
+ if (lba_mode == 1 && !dma && direction == 0) cmd = ATA_CMD_READ_PIO;
+ if (lba_mode == 2 && !dma && direction == 0) cmd = ATA_CMD_READ_PIO_EXT;
+ if (lba_mode == 0 && dma && direction == 0) cmd = ATA_CMD_READ_DMA;
+ if (lba_mode == 1 && dma && direction == 0) cmd = ATA_CMD_READ_DMA;
+ if (lba_mode == 2 && dma && direction == 0) cmd = ATA_CMD_READ_DMA_EXT;
+ if (lba_mode == 0 && !dma && direction == 1) cmd = ATA_CMD_WRITE_PIO;
+ if (lba_mode == 1 && !dma && direction == 1) cmd = ATA_CMD_WRITE_PIO;
+ if (lba_mode == 2 && !dma && direction == 1) cmd = ATA_CMD_WRITE_PIO_EXT;
+ if (lba_mode == 0 && dma && direction == 1) cmd = ATA_CMD_WRITE_DMA;
+ if (lba_mode == 1 && dma && direction == 1) cmd = ATA_CMD_WRITE_DMA;
+ if (lba_mode == 2 && dma && direction == 1) cmd = ATA_CMD_WRITE_DMA_EXT;
+ ide_write(c, channel, ATA_REG_COMMAND, cmd); // Send the Command.
+
+ if (dma) {
+ if (direction == 0) {
+ // DMA Read.
+ ASSERT(false);
+ } else {
+ // DMA Write.
+ ASSERT(false);
+ }
+ } else {
+ if (direction == 0) {
+ // PIO Read.
+ for (int i = 0; i < numsects; i++) {
+ err = ide_polling(c, channel, true);
+ if (err) return err; // Polling, set error and exit if there is.
+ insw(bus, ptr, words);
+ ptr += (words*2);
+ }
+ } else {
+ // PIO Write.
+ for (int i = 0; i < numsects; i++) {
+ ide_polling(c, channel, false); // Polling.
+ outsw(bus, ptr, words);
+ ptr += (words*2);
+ }
+ ide_write(c, channel, ATA_REG_COMMAND,
+ (char []){ATA_CMD_CACHE_FLUSH,
+ ATA_CMD_CACHE_FLUSH,
+ ATA_CMD_CACHE_FLUSH_EXT}[lba_mode]);
+ ide_polling(c, channel, false); // Polling.
+ }
+ }
+
+ return 0; // Easy, isn't it?
+}
+
+
+static uint8_t ide_atapi_read(ide_controller_t *c, uint8_t drive, uint32_t lba,
+ uint8_t numsects, void* ptr)
+{
+ uint32_t channel = c->devices[drive].channel;
+ uint32_t slavebit = c->devices[drive].drive;
+ uint32_t bus = c->channels[channel].base;
+ const uint32_t words = 1024; // Sector Size. ATAPI drives have a sector size of 2048 bytes.
+ uint8_t err;
+
+
+ // Enable IRQs:
+ c->channels[channel].nIEN = 0;
+ ide_write(c, channel, ATA_REG_CONTROL, 0);
+
+ // (I): Setup SCSI Packet:
+ uint8_t atapi_packet[12];
+ atapi_packet[ 0] = ATAPI_CMD_READ;
+ atapi_packet[ 1] = 0x0;
+ atapi_packet[ 2] = (lba >> 24) & 0xFF;
+ atapi_packet[ 3] = (lba >> 16) & 0xFF;
+ atapi_packet[ 4] = (lba >> 8) & 0xFF;
+ atapi_packet[ 5] = (lba >> 0) & 0xFF;
+ atapi_packet[ 6] = 0x0;
+ atapi_packet[ 7] = 0x0;
+ atapi_packet[ 8] = 0x0;
+ atapi_packet[ 9] = numsects;
+ atapi_packet[10] = 0x0;
+ atapi_packet[11] = 0x0;
+
+ // (II): Select the drive:
+ ide_write(c, channel, ATA_REG_HDDEVSEL, slavebit << 4);
+
+ // (III): Delay 400 nanoseconds for select to complete:
+ for(int i = 0; i < 4; i++)
+ ide_read(c, channel, ATA_REG_ALTSTATUS); // Reading the Alternate Status port wastes 100ns.
+
+ // (IV): Inform the Controller that we use PIO mode:
+ ide_write(c, channel, ATA_REG_FEATURES, 0); // PIO mode.
+
+ // (V): Tell the Controller the size of buffer:
+ ide_write(c, channel, ATA_REG_LBA1, (words * 2) & 0xFF); // Lower Byte of Sector Size.
+ ide_write(c, channel, ATA_REG_LBA2, (words * 2) >> 8); // Upper Byte of Sector Size.
+
+ // (VI): Send the Packet Command:
+ ide_write(c, channel, ATA_REG_COMMAND, ATA_CMD_PACKET); // Send the Command.
+
+ // (VII): Waiting for the driver to finish or return an error code:
+ err = ide_polling(c, channel, true);
+ if (err) return err; // Polling and return if error.
+
+ // (VIII): Sending the packet data:
+ ide_prewait_irq(c, channel);
+ outsw(bus, atapi_packet, 6);
+
+ // (IX): Receiving Data:
+ for (int i = 0; i < numsects; i++) {
+
+ ide_wait_irq(c, channel); // Wait for an IRQ.
+ ide_prewait_irq(c, channel);
+ err = ide_polling(c, channel, 1);
+ if (err) return err; // Polling and return if error.
+
+ insw(bus, ptr, words);
+ ptr += (words * 2);
+ }
+
+ // (X): Waiting for an IRQ:
+ ide_wait_irq(c, channel);
+
+ // (XI): Waiting for BSY & DRQ to clear:
+ while (ide_read(c, channel, ATA_REG_STATUS) & (ATA_SR_BSY | ATA_SR_DRQ));
+
+ return 0; // Easy, ... Isn't it?
+}
+
+static uint8_t ide_read_sectors(ide_controller_t *c, uint8_t drive,
+ uint32_t lba, uint8_t numsects, void* ptr)
+{
+ // 1: Check if the drive presents:
+ if (drive > 3 || !c->devices[drive].present) return 1; // Drive Not Found!
+
+ // 2: Check if inputs are valid:
+ if (((lba + numsects) >= c->devices[drive].size) && (c->devices[drive].type == IDE_ATA))
+ return 2; // Seeking to invalid position.
+
+ // 3: Read in PIO Mode through Polling & IRQs:
+ if (c->devices[drive].type == IDE_ATA) {
+ uint8_t err = ide_ata_access(c, ATA_READ, drive, lba, numsects, ptr);
+ return ide_print_error(c, drive, err);
+ } else if (c->devices[drive].type == IDE_ATAPI) {
+ uint8_t err = 0;
+ // for (int i = 0; i < numsects; i++)
+ // err = ide_atapi_read(c, drive, lba + i, 1, ptr + (i*2048));
+ err = ide_atapi_read(c, drive, lba, numsects, ptr);
+ return ide_print_error(c, drive, err);
+ } else {
+ return 1;
+ }
+}
+
+static uint8_t ide_write_sectors(ide_controller_t *c, uint8_t drive,
+ uint8_t numsects, uint32_t lba, void* ptr)
+{
+ // 1: Check if the drive presents:
+ if (drive > 3 || !c->devices[drive].present)
+ return 1;
+
+ // 2: Check if inputs are valid:
+ if (((lba + numsects) > c->devices[drive].size) && (c->devices[drive].type == IDE_ATA))
+ return 2; // Seeking to invalid position.
+
+ // 3: Read in PIO Mode through Polling & IRQs:
+ if (c->devices[drive].type == IDE_ATA) {
+ uint8_t err = ide_ata_access(c, ATA_WRITE, drive, lba, numsects, ptr);
+ return ide_print_error(c, drive, err);
+ } else if (c->devices[drive].type == IDE_ATAPI) {
+ return 4; // Write-Protected.
+ } else {
+ return 1;
+ }
+}
+
+// ============== //
+// SETUP ROUTINES //
+// ============== //
+
+static void ide_init(int pci_id, ide_controller_t *c, fs_t *iofs) {
+ pci_devices[pci_id].data = c;
+ c->pci_id = pci_id;
+
+ // Set IRQ to PCI_IRQ_STORAGE
+ uint8_t prog_if = pci_read_config_byte(pci_id, PCI_CONFIG_PROG_IF);
+ if (prog_if == 0x8A || prog_if == 0x80) {
+ c->channels[ATA_PRIMARY].irq = IRQ14;
+ c->channels[ATA_SECONDARY].irq = IRQ15;
+ } else {
+ pci_set_irq(pci_id, PCI_IRQ_STORAGE);
+ c->channels[ATA_PRIMARY].irq = PCI_IRQ_STORAGE;
+ c->channels[ATA_SECONDARY].irq = PCI_IRQ_STORAGE;
+ }
+
+ // Set IRQ handler
+ pci_devices[pci_id].irq_handler = pciirq_handler;
+
+ // Detect IO ports
+ uint32_t bar0 = pci_read_config_long(pci_id, PCI_CONFIG_BAR0);
+ uint32_t bar1 = pci_read_config_long(pci_id, PCI_CONFIG_BAR1);
+ uint32_t bar2 = pci_read_config_long(pci_id, PCI_CONFIG_BAR2);
+ uint32_t bar3 = pci_read_config_long(pci_id, PCI_CONFIG_BAR3);
+ uint32_t bar4 = pci_read_config_long(pci_id, PCI_CONFIG_BAR4);
+
+ c->channels[ATA_PRIMARY ].base = (bar0 ? (bar0 & 0xFFFFFFFC) : 0x1F0);
+ c->channels[ATA_PRIMARY ].ctrl = (bar1 ? (bar1 & 0xFFFFFFFC) : 0x3F6);
+ c->channels[ATA_SECONDARY].base = (bar2 ? (bar2 & 0xFFFFFFFC) : 0x170);
+ c->channels[ATA_SECONDARY].ctrl = (bar3 ? (bar3 & 0xFFFFFFFC) : 0x376);
+ c->channels[ATA_PRIMARY ].bmide = (bar4 & 0xFFFFFFFC) + 0; // Bus Master IDE
+ c->channels[ATA_SECONDARY].bmide = (bar4 & 0xFFFFFFFC) + 8; // Bus Master IDE
+
+ // Disable IRQs:
+ ide_write(c, ATA_PRIMARY , ATA_REG_CONTROL, 2);
+ ide_write(c, ATA_SECONDARY, ATA_REG_CONTROL, 2);
+
+ // Detect drives
+ for (int i = 0; i < 4; i++) {
+ c->devices[i].present = 0;
+ }
+
+ int count = 0;
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < 2; j++) {
+ uint8_t err = 0, type = IDE_ATA, status;
+ union {
+ char b[512];
+ uint16_t w[256];
+ uint32_t l[128];
+ } ide_buf;
+
+ // (I) Select Drive:
+ ide_write(c, i, ATA_REG_HDDEVSEL, 0xA0 | (j << 4));
+ usleep(1000);
+
+ // (II) Send ATA Identify Command:
+ ide_write(c, i, ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
+ usleep(1000);
+
+ // (III) Polling:
+ if (ide_read(c, i, ATA_REG_STATUS) == 0) continue; // If Status = 0, No Device.
+
+ while(1) {
+ status = ide_read(c, i, ATA_REG_STATUS);
+ if ((status & ATA_SR_ERR)) { // If Err, Device is not ATA.
+ err = 1;
+ break;
+ }
+ if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRQ)) break; // Everything is right.
+ }
+
+ // (IV) Probe for ATAPI Devices:
+ if (err != 0) {
+ uint8_t cl = ide_read(c, i, ATA_REG_LBA1);
+ uint8_t ch = ide_read(c, i, ATA_REG_LBA2);
+
+ if ((cl == 0x14 && ch == 0xEB) || (cl == 0x69 && ch == 0x96)) {
+ type = IDE_ATAPI;
+ } else {
+ continue; // Unknown Type (may not be a device).
+ }
+
+ ide_write(c, i, ATA_REG_COMMAND, ATA_CMD_IDENTIFY_PACKET);
+ usleep(1000);
+ }
+
+ // (V) Read Identification Space of the Device:
+ ide_read_buffer(c, i, ATA_REG_DATA, ide_buf.b, 128);
+
+ // (VI) Read Device Parameters:
+ c->devices[count].present = 1;
+ c->devices[count].type = type;
+ c->devices[count].channel = i;
+ c->devices[count].drive = j;
+ c->devices[count].sig = ide_buf.w[ATA_IDENT_DEVICETYPE / 2];
+ c->devices[count].cap = ide_buf.w[ATA_IDENT_CAPABILITIES / 2];
+ c->devices[count].cmdset = ide_buf.l[ATA_IDENT_COMMANDSETS / 4];
+
+ // (VII) Get Size:
+ if (c->devices[count].cmdset & (1 << 26)) {
+ // Device uses 48-Bit Addressing:
+ c->devices[count].size = ide_buf.l[ATA_IDENT_MAX_LBA_EXT / 4];
+ } else {
+ // Device uses CHS or 28-bit Addressing:
+ c->devices[count].size = ide_buf.l[ATA_IDENT_MAX_LBA / 4];
+ }
+
+ // (VIII) String indicates model of device (like Western Digital HDD and SONY DVD-RW...):
+ for(int k = 0; k < 40; k += 2) {
+ c->devices[count].model[k] = ide_buf.b[ATA_IDENT_MODEL + k + 1];
+ c->devices[count].model[k + 1] = ide_buf.b[ATA_IDENT_MODEL + k];
+ }
+ c->devices[count].model[40] = 0; // Terminate String.
+
+ count++;
+ }
+ }
+
+ dbg_printf("PCI IDE controller (PCI device %d)\n", pci_id);
+ for (int i = 0; i < 4; i++) {
+ if (c->devices[i].present) {
+ dbg_printf(" Found %s Drive (%d sectors) - %s (irq %d)\n",
+ (const char *[]){"ATA", "ATAPI"}[c->devices[i].type],
+ c->devices[i].size,
+ c->devices[i].model,
+ c->channels[c->devices[i].channel].irq);
+ ide_register_device(c, i, iofs);
+ }
+ }
+}
+
+void pciide_detect(fs_t *iofs) {
+ idt_set_irq_handler(IRQ14, irq14_handler);
+ idt_set_irq_handler(IRQ15, irq15_handler);
+
+ for (int i = 0; i < PCI_MAX_DEVICES; i++) {
+ if (pci_devices[i].vendor_id != 0xFFFF &&
+ pci_devices[i].base_class == PCI_BC_STORAGE &&
+ pci_devices[i].sub_class == PCI_SC_IDE)
+ {
+ ide_controller_t *c = (ide_controller_t*)malloc(sizeof(ide_controller_t));
+ if (c == 0) return;
+
+ memset(c, 0, sizeof(ide_controller_t));
+ ide_init(i, c, iofs);
+ }
+ }
+}
+
+// ======================== //
+// INTERFACING WITH THE VFS //
+// ======================== //
+
+static int next_ata_id = 0;
+static int next_atapi_id = 0;
+
+typedef struct {
+ ide_controller_t *c;
+ uint32_t block_size; // ATA: 512, ATAPI: 2048
+ uint8_t device;
+ uint8_t type;
+} ide_vfs_dev_t;
+
+static bool ide_vfs_open(fs_node_ptr n, int mode, fs_handle_t *s);
+static bool ide_vfs_stat(fs_node_ptr n, stat_t *st);
+static void ide_vfs_dispose(fs_node_ptr n);
+
+static size_t ide_vfs_read(fs_handle_ptr f, size_t offset, size_t len, char* buf);
+static size_t ide_vfs_write(fs_handle_ptr f, size_t offset, size_t len, const char* buf);
+static int ide_vfs_ioctl(fs_handle_ptr f, int command, void* data);
+static void ide_vfs_close(fs_handle_ptr f);
+
+static fs_node_ops_t ide_vfs_node_ops = {
+ .open = ide_vfs_open,
+ .stat = ide_vfs_stat,
+ .walk = 0,
+ .delete = 0,
+ .move = 0,
+ .create = 0,
+ .dispose = ide_vfs_dispose
+};
+
+static fs_handle_ops_t ide_vfs_handle_ops = {
+ .read = ide_vfs_read,
+ .write = ide_vfs_write,
+ .ioctl = ide_vfs_ioctl,
+ .close = ide_vfs_close,
+ .readdir = 0,
+ .get_page = 0,
+ .commit_page = 0
+};
+
+void ide_register_device(ide_controller_t *c, uint8_t device, fs_t *iofs) {
+ ide_vfs_dev_t *d = (ide_vfs_dev_t*)malloc(sizeof(ide_vfs_dev_t));
+ if (d == 0) return;
+
+ d->c = c;
+ d->device = device;
+ d->type = c->devices[device].type;
+ d->block_size = (d->type == IDE_ATAPI ? 2048 : 512);
+
+ char name[40];
+
+ if (d->type == IDE_ATAPI) {
+ snprintf(name, 40, "/atapi%d", next_atapi_id);
+ next_atapi_id++;
+ } else {
+ snprintf(name, 40, "/ata%d", next_ata_id);
+ next_ata_id++;
+ }
+
+ bool add_ok = nullfs_add_node(iofs, name, d, &ide_vfs_node_ops);
+ if (add_ok) {
+ dbg_printf(" Registered as %s.\n", name);
+ } else {
+ free(d);
+ }
+}
+
+bool ide_vfs_open(fs_node_ptr n, int mode, fs_handle_t *s) {
+ ide_vfs_dev_t *d = (ide_vfs_dev_t*)n;
+
+ int ok_modes = (FM_READ
+ | (d->type == IDE_ATA ? FM_WRITE : 0)
+ | FM_IOCTL);
+ if (mode & ~ok_modes) return false;
+
+ s->ops = &ide_vfs_handle_ops;
+ s->data = d;
+ s->mode = mode;
+
+ return true;
+}
+
+bool ide_vfs_stat(fs_node_ptr n, stat_t *st) {
+ ide_vfs_dev_t *d = (ide_vfs_dev_t*)n;
+
+ st->type = FT_BLOCKDEV;
+ st->access = (d->type == IDE_ATA ? FM_WRITE : 0) | FM_READ | FM_IOCTL;
+ st->size = d->c->devices[d->device].size * d->block_size;
+
+ return true;
+}
+
+void ide_vfs_dispose(fs_node_ptr n) {
+ free(n);
+}
+
+size_t ide_vfs_read(fs_handle_ptr f, size_t offset, size_t len, char* buf) {
+ ide_vfs_dev_t *d = (ide_vfs_dev_t*)f;
+
+ if (offset % d->block_size != 0) return 0;
+ if (len % d->block_size != 0) return 0;
+
+ uint8_t err = ide_read_sectors(d->c, d->device, offset / d->block_size, len / d->block_size, buf);
+ if (err != 0) return 0;
+
+ return len;
+}
+
+size_t ide_vfs_write(fs_handle_ptr f, size_t offset, size_t len, const char* buf) {
+ ide_vfs_dev_t *d = (ide_vfs_dev_t*)f;
+
+ if (offset % d->block_size != 0) return 0;
+ if (len % d->block_size != 0) return 0;
+
+ uint8_t err = ide_write_sectors(d->c, d->device,
+ offset / d->block_size, len / d->block_size, (char*)buf);
+ if (err != 0) return 0;
+
+ return len;
+}
+
+int ide_vfs_ioctl(fs_handle_ptr f, int command, void* data) {
+ // TODO
+ return 0;
+}
+
+void ide_vfs_close(fs_handle_ptr f) {
+ // nothing to do
+}
+
+/* vim: set ts=4 sw=4 tw=0 noet :*/
diff --git a/src/kernel/include/dev/ata.h b/src/kernel/include/dev/ata.h
deleted file mode 100644
index c0a4bff..0000000
--- a/src/kernel/include/dev/ata.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#pragma once
-
-#include <vfs.h>
-
-
-
-/* vim: set ts=4 sw=4 tw=0 noet :*/
diff --git a/src/kernel/include/dev/pci.h b/src/kernel/include/dev/pci.h
index 9fe0f3b..c1dfa99 100644
--- a/src/kernel/include/dev/pci.h
+++ b/src/kernel/include/dev/pci.h
@@ -12,11 +12,18 @@
#define PCI_IRQ_NETWORK 11
#define PCI_IRQ_DISABLE 0xFF
-#define PCI_CONFIG_DEVICE_ID 0x02
#define PCI_CONFIG_VENDOR_ID 0x00
+#define PCI_CONFIG_DEVICE_ID 0x02
+#define PCI_CONFIG_PROG_IF 0x09
#define PCI_CONFIG_CLASS 0x0B
#define PCI_CONFIG_SUBCLASS 0x0A
#define PCI_CONFIG_HEADER_TYPE 0x0E
+#define PCI_CONFIG_BAR0 0x10
+#define PCI_CONFIG_BAR1 0x14
+#define PCI_CONFIG_BAR2 0x18
+#define PCI_CONFIG_BAR3 0x1C
+#define PCI_CONFIG_BAR4 0x20
+#define PCI_CONFIG_BAR5 0x24
#define PCI_CONFIG_SECONDARY_BUS 0x19
#define PCI_CONFIG_INTERRUPT_LINE 0x3C
@@ -28,7 +35,7 @@
#define PCI_SC_IDE 0x01
#define PCI_SC_PCI_TO_PCI 0x04
-typedef void (*pci_irq_handler_t)();
+typedef void (*pci_irq_handler_t)(int dev_id);
typedef struct {
uint8_t bus;
@@ -43,6 +50,7 @@ typedef struct {
uint8_t irq;
pci_irq_handler_t irq_handler;
+ void* data; // data block for use by specific driver
} pci_device_t;
extern pci_device_t pci_devices[PCI_MAX_DEVICES];
diff --git a/src/kernel/include/dev/pciide.h b/src/kernel/include/dev/pciide.h
new file mode 100644
index 0000000..ca65ca8
--- /dev/null
+++ b/src/kernel/include/dev/pciide.h
@@ -0,0 +1,114 @@
+#pragma once
+
+#include <vfs.h>
+
+#define ATA_SR_BSY 0x80
+#define ATA_SR_DRDY 0x40
+#define ATA_SR_DF 0x20
+#define ATA_SR_DSC 0x10
+#define ATA_SR_DRQ 0x08
+#define ATA_SR_CORR 0x04
+#define ATA_SR_IDX 0x02
+#define ATA_SR_ERR 0x01
+
+#define ATA_ER_BBK 0x80
+#define ATA_ER_UNC 0x40
+#define ATA_ER_MC 0x20
+#define ATA_ER_IDNF 0x10
+#define ATA_ER_MCR 0x08
+#define ATA_ER_ABRT 0x04
+#define ATA_ER_TK0NF 0x02
+#define ATA_ER_AMNF 0x01
+
+#define ATA_CMD_READ_PIO 0x20
+#define ATA_CMD_READ_PIO_EXT 0x24
+#define ATA_CMD_READ_DMA 0xC8
+#define ATA_CMD_READ_DMA_EXT 0x25
+#define ATA_CMD_WRITE_PIO 0x30
+#define ATA_CMD_WRITE_PIO_EXT 0x34
+#define ATA_CMD_WRITE_DMA 0xCA
+#define ATA_CMD_WRITE_DMA_EXT 0x35
+#define ATA_CMD_CACHE_FLUSH 0xE7
+#define ATA_CMD_CACHE_FLUSH_EXT 0xEA
+#define ATA_CMD_PACKET 0xA0
+#define ATA_CMD_IDENTIFY_PACKET 0xA1
+#define ATA_CMD_IDENTIFY 0xEC
+
+#define ATAPI_CMD_READ 0xA8
+#define ATAPI_CMD_EJECT 0x1B
+
+#define ATA_IDENT_DEVICETYPE 0
+#define ATA_IDENT_CYLINDERS 2
+#define ATA_IDENT_HEADS 6
+#define ATA_IDENT_SECTORS 12
+#define ATA_IDENT_SERIAL 20
+#define ATA_IDENT_MODEL 54
+#define ATA_IDENT_CAPABILITIES 98
+#define ATA_IDENT_FIELDVALID 106
+#define ATA_IDENT_MAX_LBA 120
+#define ATA_IDENT_COMMANDSETS 164
+#define ATA_IDENT_MAX_LBA_EXT 200
+
+#define IDE_ATA 0x00
+#define IDE_ATAPI 0x01
+
+#define ATA_MASTER 0x00
+#define ATA_SLAVE 0x01
+
+#define ATA_REG_DATA 0x00
+#define ATA_REG_ERROR 0x01
+#define ATA_REG_FEATURES 0x01
+#define ATA_REG_SECCOUNT0 0x02
+#define ATA_REG_LBA0 0x03
+#define ATA_REG_LBA1 0x04
+#define ATA_REG_LBA2 0x05
+#define ATA_REG_HDDEVSEL 0x06
+#define ATA_REG_COMMAND 0x07
+#define ATA_REG_STATUS 0x07
+#define ATA_REG_SECCOUNT1 0x08
+#define ATA_REG_LBA3 0x09
+#define ATA_REG_LBA4 0x0A
+#define ATA_REG_LBA5 0x0B
+#define ATA_REG_CONTROL 0x0C
+#define ATA_REG_ALTSTATUS 0x0C
+#define ATA_REG_DEVADDRESS 0x0D
+
+// Channels:
+#define ATA_PRIMARY 0x00
+#define ATA_SECONDARY 0x01
+
+// Directions:
+#define ATA_READ 0x00
+#define ATA_WRITE 0x01
+
+typedef struct {
+ uint16_t base; // IO base
+ uint16_t ctrl; // Control base
+ uint16_t bmide; // Bus Master IDE
+ uint8_t nIEN; // do not use interrupt
+ uint8_t irq; // which irq to wait for
+} ide_channels_regs_t;
+
+typedef struct {
+ uint8_t present; // 0 : empty, 1 : present
+ uint8_t channel; // 0 : primary, 1 : secondary
+ uint8_t drive; // 0 : master, 1 : slave
+ uint16_t type; // 0 : ATA, 1 : ATAPI
+ uint16_t sig; // drive signature
+ uint16_t cap; // features (capabilities)
+ uint32_t cmdset; // supported command set
+ uint32_t size; // size in sectors
+ char model[41]; // model string
+} ide_device_t;
+
+typedef struct {
+ ide_channels_regs_t channels[2];
+ ide_device_t devices[4];
+ bool irq_invoked;
+ char atapi_packet[12];
+ int pci_id;
+} ide_controller_t;
+
+void pciide_detect(fs_t *iofs); // iofs = where to add ATA drives
+
+/* vim: set ts=4 sw=4 tw=0 noet :*/
diff --git a/src/kernel/include/sys.h b/src/kernel/include/sys.h
index 365e709..944df96 100644
--- a/src/kernel/include/sys.h
+++ b/src/kernel/include/sys.h
@@ -15,6 +15,22 @@ static inline void outl(uint16_t port, uint32_t value) {
asm volatile("outl %1, %0" : : "dN"(port), "a"(value));
}
+static inline void outsb(uint16_t port, const void *addr, size_t cnt) {
+ // write cnt bytes to port
+ asm volatile ("rep outsb" : "+S" (addr), "+c" (cnt) : "d" (port));
+}
+
+static inline void outsw(uint16_t port, const void *addr, size_t cnt) {
+ // write cnt words to port
+ asm volatile ("rep outsw" : "+S" (addr), "+c" (cnt) : "d" (port));
+}
+
+static inline void outsl (uint16_t port, const void *addr, size_t cnt) {
+ // write cnt longwords to port
+ asm volatile ("rep outsl" : "+S" (addr), "+c" (cnt) : "d" (port));
+}
+
+
static inline uint8_t inb(uint16_t port) {
uint8_t ret;
asm volatile("inb %1, %0" : "=a"(ret) : "dN"(port));
@@ -33,6 +49,21 @@ static inline uint32_t inl(uint16_t port) {
return ret;
}
+static inline void insb(uint16_t port, void *addr, size_t cnt) {
+ // read cnt bytes from port and put them at addr
+ asm volatile ("rep insb" : "+D" (addr), "+c" (cnt) : "d" (port) : "memory");
+}
+
+static inline void insw(uint16_t port, void *addr, size_t cnt) {
+ // read cnt words from port and put them at addr
+ asm volatile ("rep insw" : "+D" (addr), "+c" (cnt) : "d" (port) : "memory");
+}
+
+static inline void insl(uint16_t port, void *addr, size_t cnt) {
+ // read cnt longwords from port and put them at addr
+ asm volatile ("rep insl" : "+D" (addr), "+c" (cnt) : "d" (port) : "memory");
+}
+
static inline void invlpg(void* addr) {
asm volatile("invlpg (%0)" : : "r"(addr) : "memory");
}