diff options
author | Alex Auvolat <alex.auvolat@ens.fr> | 2015-02-24 15:08:01 +0100 |
---|---|---|
committer | Alex Auvolat <alex.auvolat@ens.fr> | 2015-02-24 15:08:01 +0100 |
commit | 7e908dabaaf6c67ef5000406a0bb3a6a29beca01 (patch) | |
tree | b0516cc4771ca3a8e202327d2864804afefe4cfc /src/kernel | |
parent | 91c5969cdddf2241418082998e76bdbb836ed03e (diff) | |
download | kogata-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/Makefile | 2 | ||||
-rw-r--r-- | src/kernel/core/idt.c | 1 | ||||
-rw-r--r-- | src/kernel/core/kmain.c | 2 | ||||
-rw-r--r-- | src/kernel/dev/pci.c | 8 | ||||
-rw-r--r-- | src/kernel/dev/pciide.c | 762 | ||||
-rw-r--r-- | src/kernel/include/dev/ata.h | 7 | ||||
-rw-r--r-- | src/kernel/include/dev/pci.h | 12 | ||||
-rw-r--r-- | src/kernel/include/dev/pciide.h | 114 | ||||
-rw-r--r-- | src/kernel/include/sys.h | 31 |
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"); } |