Orion
Barry Importing existing Orion kernel d41a53c (2 years, 4 months ago)/* * This file is the driver for the PCI IDE devices, which handles access to * ATA/ATAPI devices. It detects the drives connected to the system, and * handles any access to them - with the ability to do DMA if possible. This * driver will access the drives in the best available way, presenting a uniform * interface for any other code to use. It also adds a device node to the DevFS * mount for every device, to provide access to the rest of the OS easily. */ #include <stdint.h> #include <stddef.h> #include <fcntl.h> #include <sys/stat.h> #include <string.h> #include <errno.h> #include <time.h> #include "../drivers.h" #include "../pci.h" #include "../../mem/frame.h" #include "../../proc/proc.h" #include "../../vfs/vfs.h" #include "../../io.h" #include "../../screen.h" struct partition { uint8_t drive; uint8_t shead, ssect, scyl; uint8_t type; uint8_t ehead, esect, ecyl; uint32_t lba; uint32_t size; }; size_t ata_read(File *file, char *buf, size_t size, off_t offset); size_t ata_write(File *file, char *buf, size_t size, off_t offset); int ata_open(File *file); FileOps ideFileOps = { .read = ata_read, .write = ata_write, .open = ata_open, }; Driver ideDriver = { .major = 2, .ops = &ideFileOps, .next = NULL, }; page_t ataDmaArea; uint8_t ide_read(uint8_t chan, uint8_t reg); void ide_write(uint8_t chan, uint8_t reg, uint8_t data); /* Status */ #define ATA_SR_BSY 0x80 /* Busy */ #define ATA_SR_DRDY 0x40 /* Drive ready */ #define ATA_SR_DF 0x20 /* Drive write fault */ #define ATA_SR_DSC 0x10 /* Drive seek complete */ #define ATA_SR_DRQ 0x08 /* Data request ready */ #define ATA_SR_CORR 0x04 /* Corrected data */ #define ATA_SR_IDX 0x02 /* Index */ #define ATA_SR_ERR 0x01 /* Error */ /* Error */ #define ATA_ER_BBK 0x80 /* Bad block */ #define ATA_ER_UNC 0x40 /* Uncorrectable data */ #define ATA_ER_MC 0x20 /* Media changed */ #define ATA_ER_IDNF 0x10 /* ID mark not found */ #define ATA_ER_MCR 0x08 /* Media change request */ #define ATA_ER_ABRT 0x04 /* Command aborted */ #define ATA_ER_T0NF 0x02 /* Track 0 not found */ #define ATA_ER_AMNF 0x01 /* No address mark */ /* Command */ #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 0 #define IDE_ATAPI 1 #define ATA_MASTER 0 #define ATA_SLAVE 1 #define ATA_PRIMARY 0 #define ATA_SECONDARY 1 #define ATA_READ 0 #define ATA_WRITE 1 /* Registers */ #define ATA_REG_DATA 0x0 #define ATA_REG_ERROR 0x1 #define ATA_REG_FEATURES 0x1 #define ATA_REG_SECCOUNT0 0x2 #define ATA_REG_LBA0 0x3 #define ATA_REG_LBA1 0x4 #define ATA_REG_LBA2 0x5 #define ATA_REG_HDDEVSEL 0x6 #define ATA_REG_COMMAND 0x7 #define ATA_REG_STATUS 0x7 #define ATA_REG_SECCOUNT1 0x8 #define ATA_REG_LBA3 0x9 #define ATA_REG_LBA4 0xA #define ATA_REG_LBA5 0xB #define ATA_REG_CONTROL 0xC #define ATA_REG_ALTSTATUS 0xC #define ATA_REG_DEVADDRESS 0xD struct IDEChanRegs { uint16_t base; /* IO base */ uint16_t ctrl; /* Control base */ uint16_t bmide; /* Bus Master IDE */ uint8_t noint; /* No interrupt */ } channels[2]; uint8_t ideBuf[2048] = {0}; volatile unsigned static char ideIrqInvoked = 0; unsigned static char atapiPacket[12] = {0xA8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; struct IDEDevice { uint8_t reserved; uint8_t channel; uint8_t drive; uint16_t type; uint16_t signature; uint16_t capabilities; uint32_t commandSets; uint32_t size; uint8_t model[41]; } ideDevices[4]; /* Read channel */ uint8_t ide_read(uint8_t chan, uint8_t reg) { uint8_t result; if (reg > 0x07 && reg < 0x0C) ide_write(chan, ATA_REG_CONTROL, 0x80 | channels[chan].noint); if (reg < 0x08) result = inb(channels[chan].base + reg - 0x00); else if (reg < 0x0C) result = inb(channels[chan].base + reg - 0x06); else if (reg < 0x0E) result = inb(channels[chan].ctrl + reg - 0x0A); else if (reg < 0x16) result = inb(channels[chan].bmide + reg - 0x0E); if (reg > 0x07 && reg < 0x0C) ide_write(chan, ATA_REG_CONTROL, channels[chan].noint); return result; } /* Write channel */ void ide_write(uint8_t chan, uint8_t reg, uint8_t data) { if (reg > 0x07 && reg < 0x0C) ide_write(chan, ATA_REG_CONTROL, 0x80 | channels[chan].noint); if (reg < 0x08) outb(channels[chan].base + reg - 0x00, data); else if (reg < 0x0C) outb(channels[chan].base + reg - 0x06, data); else if (reg < 0x0E) outb(channels[chan].ctrl + reg - 0x0A, data); else if (reg < 0x16) outb(channels[chan].bmide + reg - 0x0E, data); if (reg > 0x07 && reg < 0x0C) ide_write(chan, ATA_REG_CONTROL, channels[chan].noint); } /* Read identification space */ void ide_read_buffer(uint8_t chan, uint8_t reg, void *buffer, uint32_t quads) { if (reg > 0x07 && reg < 0x0C) ide_write(chan, ATA_REG_CONTROL, 0x80 | channels[chan].noint); asm volatile("pushw %es; movw %ds, %ax; movw %ax, %es"); if (reg < 0x08) insl(channels[chan].base + reg - 0x00, buffer, quads); else if (reg < 0x0C) insl(channels[chan].base + reg - 0x06, buffer, quads); else if (reg < 0x0E) insl(channels[chan].ctrl + reg - 0x0A, buffer, quads); else if (reg < 0x16) insl(channels[chan].bmide + reg - 0x0E, buffer, quads); asm volatile("popw %es"); if (reg > 0x07 && reg < 0x0C) ide_write(chan, ATA_REG_CONTROL, channels[chan].noint); } /* Poll for status after command */ uint8_t ide_polling(uint8_t chan, uint32_t check) { uint8_t i, state; /* Wait for BSY to set */ for (i = 0; i < 4; i++) ide_read(chan, ATA_REG_ALTSTATUS); /* Wait for BSY to clear */ while (ide_read(chan, ATA_REG_STATUS) & ATA_SR_BSY); if (check) { state = ide_read(chan, ATA_REG_STATUS); if (state & ATA_SR_ERR) return 2; if (state & ATA_SR_DF) return 1; if ((state & ATA_SR_DRQ) == 0) return 3; } return 0; } /* Print an IDE error */ uint8_t ide_print_error(uint32_t drive, uint8_t err) { if (err == 0) return err; uint8_t st; kprintf("IDE:"); if (err == 1) { kprintf(" - Device fault"); err = 19; } else if (err == 2) { st = ide_read(ideDevices[drive].channel, ATA_REG_ERROR); if (st & ATA_ER_AMNF) { kprintf(" - No address mark found"); err = 7; } if (st & ATA_ER_T0NF) { kprintf(" - No media or media error"); err = 3; } if (st & ATA_ER_ABRT) { kprintf(" - Command aborted"); err = 20; } if (st & ATA_ER_MCR) { kprintf(" - No media or media error"); err = 3; } if (st & ATA_ER_IDNF) { kprintf(" - ID mark not found"); err = 21; } if (st & ATA_ER_MC) { kprintf(" - No media or media error"); err = 3; } if (st & ATA_ER_UNC) { kprintf(" - Uncorrectable data error"); err = 22; } if (st & ATA_ER_BBK) { kprintf(" - Bad sectors"); err = 13; } } else if (err == 3) { kprintf(" - Read nothing"); err = 23; } else if (err == 4) { kprintf(" - Write protected"); err = 8; } kprintf(" - [%s %s] %s", (char *[]){"Primary","Secondary"}[ideDevices[drive].channel], (char *[]){"Master","Slave"}[ideDevices[drive].drive], ideDevices[drive].model); return err; } /* Initialise an IDE drive */ void ide_drive_init(uint32_t bar0, uint32_t bar1, uint32_t bar2, uint32_t bar3, uint32_t bar4) { channels[ATA_PRIMARY].base = (bar0 & ~3) + 0x1F0 * (!bar0); channels[ATA_PRIMARY].ctrl = (bar1 & ~3) + 0x3F6 * (!bar1); channels[ATA_SECONDARY].base = (bar2 & ~3) + 0x170 * (!bar2); channels[ATA_SECONDARY].ctrl = (bar3 & ~3) + 0x376 * (!bar3); channels[ATA_PRIMARY].bmide = (bar4 & ~3) + 0; channels[ATA_SECONDARY].bmide = (bar4 & ~3) + 8; /* Disable IRQs */ ide_write(ATA_PRIMARY, ATA_REG_CONTROL, 2); ide_write(ATA_SECONDARY, ATA_REG_CONTROL, 2); uint32_t count = 0; uint8_t i, j, k, err, type, status; for (i = 0; i < 2; i++) for (j = 0; j < 2; j++) { err = 0; type = IDE_ATA; ideDevices[count].reserved = 0; ide_write(i, ATA_REG_HDDEVSEL, 0xA0 | (j << 4)); sleep(1); ide_write(i, ATA_REG_COMMAND, ATA_CMD_IDENTIFY); sleep(1); if (ide_read(i, ATA_REG_STATUS) == 0) continue; while (1) { status = ide_read(i, ATA_REG_STATUS); if ((status & ATA_SR_ERR)) { err = 1; break; } if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRQ)) break; } if (err) { uint8_t cl = ide_read(i, ATA_REG_LBA1); uint8_t ch = ide_read(i, ATA_REG_LBA2); if (cl == 0x14 && ch == 0xEB) type = IDE_ATAPI; else if (cl == 0x69 && ch == 0x96) type = IDE_ATAPI; else continue; ide_write(i, ATA_REG_COMMAND, ATA_CMD_IDENTIFY_PACKET); sleep(1); } ide_read_buffer(i, ATA_REG_DATA, ideBuf, 128); uint16_t sig, cap, set; sig = *((uint16_t *) (ideBuf + ATA_IDENT_DEVICETYPE)); cap = *((uint16_t *) (ideBuf + ATA_IDENT_CAPABILITIES)); set = *((uint16_t *) (ideBuf + ATA_IDENT_COMMANDSETS)); ideDevices[count].reserved = 1; ideDevices[count].type = type; ideDevices[count].channel = i; ideDevices[count].drive = j; ideDevices[count].signature = sig; ideDevices[count].capabilities = cap; ideDevices[count].commandSets = set; uint32_t size; if (ideDevices[count].commandSets & (1 << 26)) size = *((uint32_t *) (ideBuf + ATA_IDENT_MAX_LBA_EXT)); else size = *((uint32_t *) (ideBuf + ATA_IDENT_MAX_LBA)); ideDevices[count].size = size; for (k = 0; k < 40; k += 2) { ideDevices[count].model[k] = ideBuf[ATA_IDENT_MODEL + k + 1]; ideDevices[count].model[k + 1] = ideBuf[ATA_IDENT_MODEL + k]; } ideDevices[count].model[40] = 0; count++; } } /* Wait for an IRQ to arrive */ void ide_wait_irq(void) { while (!ideIrqInvoked); ideIrqInvoked = 0; } /* Access an ATA drive */ uint8_t ide_ata_access(uint8_t write, uint8_t drive, uint32_t lba, uint8_t sectors, uint16_t selector, uint32_t edi) { uint8_t lbaMode, dma, cmd; uint8_t lbaIO[6]; uint32_t chan = ideDevices[drive].channel; uint32_t slave = ideDevices[drive].drive; uint32_t bus = channels[chan].base; uint32_t words = 256; uint16_t cyl, i; uint8_t head, sect, err; ide_write(chan, ATA_REG_CONTROL, channels[chan].noint = (ideIrqInvoked = 0) + 2); if (lba >= 0x10000000) { /* LBA48 */ lbaMode = 2; lbaIO[0] = (lba & 0x000000FF) >> 0; lbaIO[1] = (lba & 0x0000FF00) >> 8; lbaIO[2] = (lba & 0x00FF0000) >> 16; lbaIO[3] = (lba & 0xFF000000) >> 24; lbaIO[4] = 0; lbaIO[5] = 0; head = 0; } else if (ideDevices[drive].capabilities & 0x200) { /* LBA28 */ lbaMode = 1; lbaIO[0] = (lba & 0x000000FF) >> 0; lbaIO[1] = (lba & 0x0000FF00) >> 8; lbaIO[2] = (lba & 0x00FF0000) >> 16; lbaIO[3] = 0; lbaIO[4] = 0; lbaIO[5] = 0; head = (lba & 0x0F000000) >> 24; } else { /* CHS */ lbaMode = 0; sect = (lba % 63) + 1; cyl = (lba + 1 - sect) / (16 * 63); lbaIO[0] = sect; lbaIO[1] = (cyl >> 0) & 0xFF; lbaIO[2] = (cyl >> 8) & 0xFF; lbaIO[3] = 0; lbaIO[4] = 0; lbaIO[5] = 0; head = (lba + 1 - sect) % (16 * 63) / 63; } dma = 0; /* XXX */ while (ide_read(chan, ATA_REG_STATUS) & ATA_SR_BSY); if (lbaMode == 0) ide_write(chan, ATA_REG_HDDEVSEL, 0xA0 | (slave << 4) | head); else ide_write(chan, ATA_REG_HDDEVSEL, 0xE0 | (slave << 4) | head); if (lbaMode == 2) { ide_write(chan, ATA_REG_SECCOUNT1, 0); ide_write(chan, ATA_REG_LBA3, lbaIO[3]); ide_write(chan, ATA_REG_LBA4, lbaIO[4]); ide_write(chan, ATA_REG_LBA5, lbaIO[5]); } ide_write(chan, ATA_REG_SECCOUNT0, sectors); ide_write(chan, ATA_REG_LBA0, lbaIO[0]); ide_write(chan, ATA_REG_LBA1, lbaIO[1]); ide_write(chan, ATA_REG_LBA2, lbaIO[2]); if (lbaMode == 0 && !dma && !write) cmd = ATA_CMD_READ_PIO; if (lbaMode == 1 && !dma && !write) cmd = ATA_CMD_READ_PIO; if (lbaMode == 2 && !dma && !write) cmd = ATA_CMD_READ_PIO_EXT; if (lbaMode == 0 && dma && !write) cmd = ATA_CMD_READ_DMA; if (lbaMode == 1 && dma && !write) cmd = ATA_CMD_READ_DMA; if (lbaMode == 2 && dma && !write) cmd = ATA_CMD_READ_DMA_EXT; if (lbaMode == 0 && !dma && write) cmd = ATA_CMD_WRITE_PIO; if (lbaMode == 1 && !dma && write) cmd = ATA_CMD_WRITE_PIO; if (lbaMode == 2 && !dma && write) cmd = ATA_CMD_WRITE_PIO_EXT; if (lbaMode == 0 && dma && write) cmd = ATA_CMD_WRITE_DMA; if (lbaMode == 1 && dma && write) cmd = ATA_CMD_WRITE_DMA; if (lbaMode == 2 && dma && write) cmd = ATA_CMD_WRITE_DMA_EXT; ide_write(chan, ATA_REG_COMMAND, cmd); if (dma) { if (write) { /* TODO */ } else { /* TODO */ } } else { if (write) { for (i = 0; i < sectors; i++) { ide_polling(chan, 0); outsw(bus, (void *) edi, words); } ide_write(chan, ATA_REG_COMMAND, (uint8_t []) { ATA_CMD_CACHE_FLUSH, ATA_CMD_CACHE_FLUSH, ATA_CMD_CACHE_FLUSH_EXT, }[lbaMode]); ide_polling(chan, 0); } else { for (i = 0; i < sectors; i++) { if (err = ide_polling(chan, 1)) return err; insw(bus, (void *) edi, words); edi += words * 2; } } } return 0; } /* IDE IRQ handler */ void ide_irq(InterruptFrame *frame) { ideIrqInvoked = 1; } /* Initialise an IDE drive */ void ide_init(uint8_t bus, uint8_t slot, uint8_t func) { register_driver(&ideDriver); uint8_t class, subclass, progif, header; class = pci_read_byte(bus, slot, func, 11); subclass = pci_read_byte(bus, slot, func, 10); progif = pci_read_byte(bus, slot, func, 9); header = pci_read_byte(bus, slot, func, 14); uint8_t primaryNative = 0, primaryChange = 0, secondaryNative = 0, secondaryChange = 0, busMaster = 0; if (progif & (1 << 0)) primaryNative = 1; if (progif & (1 << 1)) primaryChange = 1; if (progif & (1 << 2)) secondaryNative = 1; if (progif & (1 << 3)) secondaryChange = 1; if (progif & (1 << 7)) busMaster = 1; uint32_t bar0 = 0x1F0, bar1 = 0x3F6, bar2 = 0x170, bar3 = 0x376, bar4 = 0x00; if (primaryNative) { bar0 = pci_read_dword(bus, slot, func, 0x10); bar1 = pci_read_dword(bus, slot, func, 0x14); } if (secondaryNative) { bar2 = pci_read_dword(bus, slot, func, 0x18); bar3 = pci_read_dword(bus, slot, func, 0x1C); } if (busMaster) bar4 = pci_read_dword(bus, slot, func, 0x20); register_interrupt(14, ide_irq); ataDmaArea = alloc_frames(1); ide_drive_init(0x1F0, 0x3F6, 0x170, 0x376, 0x00); uint8_t i, j; char name[16]; for (i = 0; i < 4; i++) { if (!ideDevices[i].reserved) continue; sprintf(name, "/dev/%cd%c", (char []){'h','c'}[ideDevices[i].type], (char []){'a','b'}[ideDevices[i].channel]); mknod(name, S_IFBLK | 0600, MKDEV(ideDriver.major, i * 16)); kprintf(" %s drive [%s] %d MB - %s", (char *[]){"ATA", "ATAPI"}[ideDevices[i].type], name, ideDevices[i].size / 1024 / 2, ideDevices[i].model); /* Attempt to read partition table */ if (ideDevices[i].type != 0) continue; ide_ata_access(0, i, 0, 0x02, 1, ataDmaArea); struct partition *part; for (j = 0; j < 4; j++) { part = (void *) ataDmaArea + 446 + (j * 16); if (!part->type) continue; sprintf(name, "/dev/%cd%c%d", (char []){'h','c'}[ideDevices[i].type], (char []){'a','b'}[ideDevices[i].channel], j + 1); int asdf = mknod(name, S_IFBLK | 0600, MKDEV(ideDriver.major, (i * 16) + (j + 1))); kprintf(" Partition %d: %#.8x - %#.8x", j+1, part->lba*512, (part->lba + part->size) * 512); } } } /* Read a drive */ size_t ata_read(File *file, char *buf, size_t size, off_t offset) { uint16_t min, bufOffset = offset % 512; size_t count = 0; uint16_t dev = MINOR(file->inode->dev) / 16, parti = MINOR(file->inode->dev) % 16; uint32_t lba; struct partition *part; if (parti) { ide_ata_access(0, dev, 0, 1, 0x02, ataDmaArea); part = (void *) ataDmaArea + 446 + ((parti - 1) * 16); if (offset > (part->lba + part->size) * 512) return 0; if (offset + size > (part->lba + part->size) * 512) size = ((part->lba + part->size) * 512) - offset; } else { if (offset > ideDevices[dev].size * 512) return 0; if (offset + size > ideDevices[dev].size * 512) size = (ideDevices[dev].size * 512) - offset; } while (size + bufOffset) { min = (size > 0x1000) ? 0x1000 : size; lba = (parti ? part->lba : 0) + ((offset + count) / 512); ide_ata_access(0, dev, lba, 8, 0x02, ataDmaArea); memcpy((void *) (buf + count), (void *) ataDmaArea + bufOffset, min); size -= min; count += min; bufOffset = 0; } return count; } /* Write a drive */ size_t ata_write(File *file, char *buf, size_t size, off_t offset) { uint16_t min; size_t count = 0; uint16_t dev = MINOR(file->inode->dev) / 16, parti = MINOR(file->inode->dev) % 16; uint32_t lba; struct partition *part; if (parti) { ide_ata_access(0, dev, 0, 1, 0x02, ataDmaArea); part = (void *) ataDmaArea + 446 + ((parti - 1) * 16); if (offset > (part->lba + part->size) * 512) return 0; if (offset + size > (part->lba + part->size) * 512) size = ((part->lba + part->size) * 512) - offset; } else { if (offset > ideDevices[dev].size * 512) return 0; if (offset + size > ideDevices[dev].size * 512) size = (ideDevices[dev].size * 512) - offset; } while (size) { min = (size > 0x1000) ? 0x1000 : size; memcpy((void *) ataDmaArea, // (void *) (((uint32_t) (buf + count) / 512) * 512), (void *) (buf + count), min); lba = (parti ? part->lba : 0) + ((offset + count) / 512); ide_ata_access(1, dev, lba, 8, 0x02, ataDmaArea); size -= min; count += min; } return count; /* * TODO: Currently, offset is sector-aligned, which we don't always * want. This should be fixed by basically checking the offset * for this and reading into the dma buffer before we write to the * adjusted offset in the buffer, this way the surrounding data * isn't deleted by accident with bad alignment. */ } /* Open a drive */ int ata_open(File *file) { if (ideDevices[MINOR(file->inode->dev)/16].reserved) return 0; if (ideDevices[MINOR(file->inode->dev)/16].type != 0) return -EINVAL; return -ENODEV; }