X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=src%2Fata.c;h=3b609c5b73ad1644b1fdcaf86fdb6d71d14cc86f;hb=c79637bfce2e90d724ae2d2cebb3d15c39b5a38e;hp=f245e21199cf58f5fd3fdf7b6fb87329572bfb10;hpb=970a03254708272047d2b47229e40dbe16fc47d5;p=seabios.git diff --git a/src/ata.c b/src/ata.c index f245e21..3b609c5 100644 --- a/src/ata.c +++ b/src/ata.c @@ -3,16 +3,20 @@ // Copyright (C) 2008 Kevin O'Connor // Copyright (C) 2002 MandrakeSoft S.A. // -// This file may be distributed under the terms of the GNU GPLv3 license. +// This file may be distributed under the terms of the GNU LGPLv3 license. -#include "ata.h" // ATA_* #include "types.h" // u8 #include "ioport.h" // inb #include "util.h" // dprintf #include "cmos.h" // inb_cmos -#include "pic.h" // unmask_pic2 +#include "pic.h" // enable_hwirq #include "biosvar.h" // GET_EBDA #include "pci.h" // pci_find_class +#include "pci_ids.h" // PCI_CLASS_STORAGE_OTHER +#include "pci_regs.h" // PCI_INTERRUPT_LINE +#include "boot.h" // add_bcv_hd +#include "disk.h" // struct ata_s +#include "atabits.h" // ATA_CB_STAT #define TIMEOUT 0 #define BSY 1 @@ -24,7 +28,9 @@ #define IDE_SECTOR_SIZE 512 #define CDROM_SECTOR_SIZE 2048 -#define IDE_TIMEOUT 32000u //32 seconds max for IDE ops +#define IDE_TIMEOUT 32000 //32 seconds max for IDE ops + +struct ata_s ATA VAR16_32; /**************************************************************** @@ -32,79 +38,51 @@ ****************************************************************/ // Wait for the specified ide state -static int -await_ide(u8 when_done, u16 base, u16 timeout) +static inline int +await_ide(u8 mask, u8 flags, u16 base, u16 timeout) { - u32 time=0, last=0; + u64 end = calc_future_tsc(timeout); for (;;) { u8 status = inb(base+ATA_CB_STAT); - time++; - u8 result = 0; - if (when_done == BSY) - result = status & ATA_CB_STAT_BSY; - else if (when_done == NOT_BSY) - result = !(status & ATA_CB_STAT_BSY); - else if (when_done == NOT_BSY_DRQ) - result = !(status & ATA_CB_STAT_BSY) && (status & ATA_CB_STAT_DRQ); - else if (when_done == NOT_BSY_NOT_DRQ) - result = !(status & ATA_CB_STAT_BSY) && !(status & ATA_CB_STAT_DRQ); - else if (when_done == NOT_BSY_RDY) - result = !(status & ATA_CB_STAT_BSY) && (status & ATA_CB_STAT_RDY); - - if (result) + if ((status & mask) == flags) return status; - // mod 2048 each 16 ms - if (time>>16 != last) { - last = time >>16; - dprintf(6, "await_ide: (TIMEOUT,BSY,!BSY,!BSY_DRQ" - ",!BSY_!DRQ,!BSY_RDY) %d time= %d timeout= %d\n" - , when_done, time>>11, timeout); - } - if (status & ATA_CB_STAT_ERR) { - dprintf(1, "await_ide: ERROR (TIMEOUT,BSY,!BSY,!BSY_DRQ" - ",!BSY_!DRQ,!BSY_RDY) %d status=%x time= %d timeout= %d\n" - , when_done, status, time>>11, timeout); + if (rdtscll() > end) { + dprintf(1, "IDE time out\n"); return -1; } - if (timeout == 0 || (time>>11) > timeout) - break; } - dprintf(1, "IDE time out\n"); - return -2; } -// Wait for ide state - pauses for one ata cycle first. -static __always_inline int -pause_await_ide(u8 when_done, u16 iobase1, u16 iobase2, u16 timeout) +// Wait for the device to be not-busy. +static int +await_not_bsy(u16 base) { - // Wait one PIO transfer cycle. - inb(iobase2 + ATA_CB_ASTAT); - - return await_ide(when_done, iobase1, timeout); + return await_ide(ATA_CB_STAT_BSY, 0, base, IDE_TIMEOUT); } -// Delay for x nanoseconds -static void -nsleep(u32 delay) +// Wait for the device to be ready. +static int +await_rdy(u16 base) { - // XXX - how to implement ndelay? - while (delay--) - nop(); + return await_ide(ATA_CB_STAT_RDY, ATA_CB_STAT_RDY, base, IDE_TIMEOUT); } -// Wait for ide state - pause for 400ns first. -static __always_inline int -ndelay_await_ide(u8 when_done, u16 iobase1, u16 timeout) +// Wait for ide state - pauses for one ata cycle first. +static inline int +pause_await_not_bsy(u16 iobase1, u16 iobase2) { - nsleep(400); - return await_ide(when_done, iobase1, timeout); + // Wait one PIO transfer cycle. + inb(iobase2 + ATA_CB_ASTAT); + + return await_not_bsy(iobase1); } -// Delay for x milliseconds -static void -msleep(u32 delay) +// Wait for ide state - pause for 400ns first. +static inline int +ndelay_await_not_bsy(u16 iobase1) { - usleep(delay * 1000); + ndelay(400); + return await_not_bsy(iobase1); } // Reset a drive @@ -113,41 +91,48 @@ ata_reset(int driveid) { u8 channel = driveid / 2; u8 slave = driveid % 2; - u16 iobase1 = GET_EBDA(ata.channels[channel].iobase1); - u16 iobase2 = GET_EBDA(ata.channels[channel].iobase2); - - // Reset + u16 iobase1 = GET_GLOBAL(ATA.channels[channel].iobase1); + u16 iobase2 = GET_GLOBAL(ATA.channels[channel].iobase2); - // 8.2.1 (a) -- set SRST in DC + dprintf(6, "ata_reset driveid=%d\n", driveid); + // Pulse SRST outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN | ATA_CB_DC_SRST, iobase2+ATA_CB_DC); - - // 8.2.1 (b) -- wait for BSY - int status = await_ide(BSY, iobase1, 20); - dprintf(6, "ata_reset(1) status=%x\n", status); - - // 8.2.1 (f) -- clear SRST + udelay(5); outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2+ATA_CB_DC); + mdelay(2); - // 8.2.1 (g) -- check for sc==sn==0x01 - // select device - outb(slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0, iobase1+ATA_CB_DH); - msleep(50); - u8 sc = inb(iobase1+ATA_CB_SC); - u8 sn = inb(iobase1+ATA_CB_SN); - - // For predetermined ATA drives - wait for ready. - if (sc==0x01 && sn==0x01) { - u8 type=GET_EBDA(ata.devices[driveid].type); - if (type == ATA_TYPE_ATA) - await_ide(NOT_BSY_RDY, iobase1, IDE_TIMEOUT); + // wait for device to become not busy. + int status = await_not_bsy(iobase1); + if (status < 0) + goto done; + if (slave) { + // Change device. + u64 end = calc_future_tsc(IDE_TIMEOUT); + for (;;) { + outb(ATA_CB_DH_DEV1, iobase1 + ATA_CB_DH); + status = ndelay_await_not_bsy(iobase1); + if (status < 0) + goto done; + if (inb(iobase1 + ATA_CB_DH) == ATA_CB_DH_DEV1) + break; + // Change drive request failed to take effect - retry. + if (rdtscll() > end) { + dprintf(1, "ata_reset slave time out\n"); + goto done; + } + } } - // 8.2.1 (h) -- wait for not BSY - status = await_ide(NOT_BSY, iobase1, IDE_TIMEOUT); - dprintf(6, "ata_reset(2) status=%x\n", status); + // On a user-reset request, wait for RDY if it is an ATA device. + u8 type=GET_GLOBAL(ATA.devices[driveid].type); + if (type == ATA_TYPE_ATA) + status = await_rdy(iobase1); +done: // Enable interrupts outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC); + + dprintf(6, "ata_reset exit status=%x\n", status); } @@ -155,13 +140,6 @@ ata_reset(int driveid) * ATA send command ****************************************************************/ -struct ata_op_s { - u64 lba; - void *far_buffer; - u16 driveid; - u16 count; -}; - struct ata_pio_command { u8 feature; u8 sector_count; @@ -182,22 +160,27 @@ static int send_cmd(int driveid, struct ata_pio_command *cmd) { u8 channel = driveid / 2; - u16 iobase1 = GET_EBDA(ata.channels[channel].iobase1); - u16 iobase2 = GET_EBDA(ata.channels[channel].iobase2); - - int status = inb(iobase1 + ATA_CB_STAT); - if (status & ATA_CB_STAT_BSY) - return -3; + u8 slave = driveid % 2; + u16 iobase1 = GET_GLOBAL(ATA.channels[channel].iobase1); + u16 iobase2 = GET_GLOBAL(ATA.channels[channel].iobase2); // Disable interrupts outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2 + ATA_CB_DC); // Select device - u8 device = inb(iobase1 + ATA_CB_DH); - outb(cmd->device, iobase1 + ATA_CB_DH); - if ((device ^ cmd->device) & (1 << 4)) - // Wait for device to become active. - msleep(50); + int status = await_not_bsy(iobase1); + if (status < 0) + return status; + u8 newdh = ((cmd->device & ~ATA_CB_DH_DEV1) + | (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0)); + u8 olddh = inb(iobase1 + ATA_CB_DH); + outb(newdh, iobase1 + ATA_CB_DH); + if ((olddh ^ newdh) & (1<<4)) { + // Was a device change - wait for device to become not busy. + status = ndelay_await_not_bsy(iobase1); + if (status < 0) + return status; + } if (cmd->command & 0x04) { outb(0x00, iobase1 + ATA_CB_FR); @@ -213,17 +196,17 @@ send_cmd(int driveid, struct ata_pio_command *cmd) outb(cmd->lba_high, iobase1 + ATA_CB_CH); outb(cmd->command, iobase1 + ATA_CB_CMD); - status = ndelay_await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT); + status = ndelay_await_not_bsy(iobase1); if (status < 0) return status; if (status & ATA_CB_STAT_ERR) { - dprintf(6, "send_cmd : read error\n"); + dprintf(6, "send_cmd : read error (status=%02x err=%02x)\n" + , status, inb(iobase1 + ATA_CB_ERR)); return -4; } if (!(status & ATA_CB_STAT_DRQ)) { - dprintf(6, "send_cmd : DRQ not set (status %02x)\n" - , (unsigned) status); + dprintf(6, "send_cmd : DRQ not set (status %02x)\n", status); return -5; } @@ -237,10 +220,10 @@ send_cmd(int driveid, struct ata_pio_command *cmd) // Read and discard x number of bytes from an io channel. static void -insx_discard(int mode, int iobase1, int bytes) +insx_discard(int iobase1, int bytes) { int count, i; - if (mode == ATA_MODE_PIO32) { + if (CONFIG_ATA_PIO32) { count = bytes / 4; for (i=0; idriveid, iswrite, op->count, IDE_SECTOR_SIZE - , 0, 0, op->far_buffer); + return ata_transfer(op->driveid, op->command == ATA_CMD_WRITE_SECTORS + , op->count, IDE_SECTOR_SIZE, 0, 0, op->buf_fl); } static noinline int -ata_transfer_cdrom(const struct ata_op_s *op) +ata_transfer_cdrom(const struct disk_op_s *op) { return ata_transfer(op->driveid, 0, op->count, CDROM_SECTOR_SIZE - , 0, 0, op->far_buffer); + , 0, 0, op->buf_fl); } static noinline int -ata_transfer_emu(const struct ata_op_s *op, int before, int after) +ata_transfer_cdemu(const struct disk_op_s *op, int before, int after) { int vcount = op->count * 4 - before - after; int ret = ata_transfer(op->driveid, 0, op->count, CDROM_SECTOR_SIZE - , before*512, after*512, op->far_buffer); + , before*512, after*512, op->buf_fl); if (ret) { - SET_EBDA(ata.trsfsectors, 0); + SET_EBDA(sector_count, 0); return ret; } - SET_EBDA(ata.trsfsectors, vcount); + SET_EBDA(sector_count, vcount); return 0; } @@ -369,16 +349,15 @@ ata_transfer_emu(const struct ata_op_s *op, int before, int after) * ATA hard drive functions ****************************************************************/ -static noinline int -send_cmd_disk(const struct ata_op_s *op, u16 command) +static int +send_cmd_disk(const struct disk_op_s *op) { - u8 slave = op->driveid % 2; u64 lba = op->lba; struct ata_pio_command cmd; memset(&cmd, 0, sizeof(cmd)); - cmd.command = command; + cmd.command = op->command; if (op->count >= (1<<8) || lba + op->count >= (1<<28)) { cmd.sector_count2 = op->count >> 8; cmd.lba_low2 = lba >> 24; @@ -394,28 +373,19 @@ send_cmd_disk(const struct ata_op_s *op, u16 command) cmd.lba_low = lba; cmd.lba_mid = lba >> 8; cmd.lba_high = lba >> 16; - cmd.device = ((slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) - | ((lba >> 24) & 0xf) | ATA_CB_DH_LBA); + cmd.device = ((lba >> 24) & 0xf) | ATA_CB_DH_LBA; return send_cmd(op->driveid, &cmd); } // Read/write count blocks from a harddrive. -__always_inline int -ata_cmd_data(int driveid, u16 command, u64 lba, u16 count, void *far_buffer) +int +ata_cmd_data(struct disk_op_s *op) { - struct ata_op_s op; - op.driveid = driveid; - op.lba = lba; - op.count = count; - op.far_buffer = far_buffer; - - int ret = send_cmd_disk(&op, command); + int ret = send_cmd_disk(op); if (ret) return ret; - - int iswrite = command == ATA_CMD_WRITE_SECTORS; - return ata_transfer_disk(&op, iswrite); + return ata_transfer_disk(op); } @@ -424,13 +394,12 @@ ata_cmd_data(int driveid, u16 command, u64 lba, u16 count, void *far_buffer) ****************************************************************/ // Low-level atapi command transmit function. -static __always_inline int +static int send_atapi_cmd(int driveid, u8 *cmdbuf, u8 cmdlen, u16 blocksize) { u8 channel = driveid / 2; - u8 slave = driveid % 2; - u16 iobase1 = GET_EBDA(ata.channels[channel].iobase1); - u16 iobase2 = GET_EBDA(ata.channels[channel].iobase2); + u16 iobase1 = GET_GLOBAL(ATA.channels[channel].iobase1); + u16 iobase2 = GET_GLOBAL(ATA.channels[channel].iobase2); struct ata_pio_command cmd; cmd.sector_count = 0; @@ -438,7 +407,7 @@ send_atapi_cmd(int driveid, u8 *cmdbuf, u8 cmdlen, u16 blocksize) cmd.lba_low = 0; cmd.lba_mid = blocksize; cmd.lba_high = blocksize >> 8; - cmd.device = slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0; + cmd.device = 0; cmd.command = ATA_CMD_PACKET; int ret = send_cmd(driveid, &cmd); @@ -446,18 +415,31 @@ send_atapi_cmd(int driveid, u8 *cmdbuf, u8 cmdlen, u16 blocksize) return ret; // Send command to device - outsw_far(iobase1, MAKE_FARPTR(GET_SEG(SS), (u32)cmdbuf), cmdlen / 2); + outsw_fl(iobase1, MAKE_FLATPTR(GET_SEG(SS), cmdbuf), cmdlen / 2); - int status = pause_await_ide(NOT_BSY_DRQ, iobase1, iobase2, IDE_TIMEOUT); + int status = pause_await_not_bsy(iobase1, iobase2); if (status < 0) return status; + if (status & ATA_CB_STAT_ERR) { + u8 err = inb(iobase1 + ATA_CB_ERR); + // skip "Not Ready" + if (err != 0x20) + dprintf(6, "send_atapi_cmd : read error (status=%02x err=%02x)\n" + , status, err); + return -2; + } + if (!(status & ATA_CB_STAT_DRQ)) { + dprintf(6, "send_atapi_cmd : DRQ not set (status %02x)\n", status); + return -3; + } + return 0; } // Low-level cdrom read atapi command transmit function. static int -send_cmd_cdrom(const struct ata_op_s *op) +send_cmd_cdrom(const struct disk_op_s *op) { u8 atacmd[12]; memset(atacmd, 0, sizeof(atacmd)); @@ -475,188 +457,59 @@ send_cmd_cdrom(const struct ata_op_s *op) } // Read sectors from the cdrom. -__always_inline int -cdrom_read(int driveid, u32 lba, u32 count, void *far_buffer) +int +cdrom_read(struct disk_op_s *op) { - struct ata_op_s op; - op.driveid = driveid; - op.lba = lba; - op.count = count; - op.far_buffer = far_buffer; - - int ret = send_cmd_cdrom(&op); + int ret = send_cmd_cdrom(op); if (ret) return ret; - return ata_transfer_cdrom(&op); + return ata_transfer_cdrom(op); } // Pretend the cdrom has 512 byte sectors (instead of 2048) and read // sectors. -__always_inline int -cdrom_read_512(int driveid, u32 vlba, u32 vcount, void *far_buffer) +int +cdrom_read_512(struct disk_op_s *op) { + u32 vlba = op->lba; + u32 vcount = op->count; + u32 lba = op->lba = vlba / 4; u32 velba = vlba + vcount - 1; - u32 lba = vlba / 4; u32 elba = velba / 4; - int count = elba - lba + 1; + op->count = elba - lba + 1; int before = vlba % 4; int after = 3 - (velba % 4); - struct ata_op_s op; - op.driveid = driveid; - op.lba = lba; - op.count = count; - op.far_buffer = far_buffer; - dprintf(16, "cdrom_read_512: id=%d vlba=%d vcount=%d buf=%p lba=%d elba=%d" " count=%d before=%d after=%d\n" - , driveid, vlba, vcount, far_buffer, lba, elba - , count, before, after); + , op->driveid, vlba, vcount, op->buf_fl, lba, elba + , op->count, before, after); - int ret = send_cmd_cdrom(&op); + int ret = send_cmd_cdrom(op); if (ret) return ret; - return ata_transfer_emu(&op, before, after); + return ata_transfer_cdemu(op, before, after); } // Send a simple atapi command to a drive. int ata_cmd_packet(int driveid, u8 *cmdbuf, u8 cmdlen - , u32 length, void *far_buffer) + , u32 length, void *buf_fl) { int ret = send_atapi_cmd(driveid, cmdbuf, cmdlen, length); if (ret) return ret; - return ata_transfer(driveid, 0, 1, length, 0, 0, far_buffer); + return ata_transfer(driveid, 0, 1, length, 0, 0, buf_fl); } /**************************************************************** - * ATA detect and init + * Disk geometry translation ****************************************************************/ -static void -report_model(int driveid, u8 *buffer) -{ - u8 model[41]; - - // Read model name - int i; - for (i=0; i<40; i+=2) { - model[i] = buffer[i+54+1]; - model[i+1] = buffer[i+54]; - } - - // Reformat - model[40] = 0x00; - for (i=39; i>0; i--) { - if (model[i] != 0x20) - break; - model[i] = 0x00; - } - - u8 channel = driveid / 2; - u8 slave = driveid % 2; - // XXX - model on stack not %cs - printf("ata%d %s: %s", channel, slave ? " slave" : "master", model); -} - -static u8 -get_ata_version(u8 *buffer) -{ - u16 ataversion = *(u16*)&buffer[160]; - u8 version; - for (version=15; version>0; version--) - if (ataversion & (1< 1) - return; - - u16 nlc = GET_EBDA(ata.devices[driveid].lchs.cylinders); - u16 nlh = GET_EBDA(ata.devices[driveid].lchs.heads); - u16 nlspt = GET_EBDA(ata.devices[driveid].lchs.spt); - - u16 npc = GET_EBDA(ata.devices[driveid].pchs.cylinders); - u16 nph = GET_EBDA(ata.devices[driveid].pchs.heads); - u16 npspt = GET_EBDA(ata.devices[driveid].pchs.spt); - - SET_EBDA(fdpt[driveid].precompensation, 0xffff); - SET_EBDA(fdpt[driveid].drive_control_byte, 0xc0 | ((nph > 8) << 3)); - SET_EBDA(fdpt[driveid].landing_zone, npc); - SET_EBDA(fdpt[driveid].cylinders, nlc); - SET_EBDA(fdpt[driveid].heads, nlh); - SET_EBDA(fdpt[driveid].sectors, nlspt); - - if (nlc == npc && nlh == nph && nlspt == npspt) - // no logical CHS mapping used, just physical CHS - // use Standard Fixed Disk Parameter Table (FDPT) - return; - - // complies with Phoenix style Translated Fixed Disk Parameter - // Table (FDPT) - SET_EBDA(fdpt[driveid].phys_cylinders, npc); - SET_EBDA(fdpt[driveid].phys_heads, nph); - SET_EBDA(fdpt[driveid].phys_sectors, npspt); - SET_EBDA(fdpt[driveid].a0h_signature, 0xa0); - - // Checksum structure. - u8 *p = MAKE_FARPTR(SEG_EBDA, offsetof(struct extended_bios_data_area_s - , fdpt[driveid])); - u8 sum = checksum(p, FIELD_SIZEOF(struct extended_bios_data_area_s - , fdpt[driveid]) - 1); - SET_EBDA(fdpt[driveid].checksum, -sum); -} - static u8 get_translation(int driveid) { @@ -670,9 +523,9 @@ get_translation(int driveid) } // On COREBOOT, use a heuristic to determine translation type. - u16 heads = GET_EBDA(ata.devices[driveid].pchs.heads); - u16 cylinders = GET_EBDA(ata.devices[driveid].pchs.cylinders); - u16 spt = GET_EBDA(ata.devices[driveid].pchs.spt); + u16 heads = GET_GLOBAL(ATA.devices[driveid].pchs.heads); + u16 cylinders = GET_GLOBAL(ATA.devices[driveid].pchs.cylinders); + u16 spt = GET_GLOBAL(ATA.devices[driveid].pchs.spt); if (cylinders <= 1024 && heads <= 16 && spt <= 63) return ATA_TRANSLATION_NONE; @@ -685,14 +538,14 @@ static void setup_translation(int driveid) { u8 translation = get_translation(driveid); - SET_EBDA(ata.devices[driveid].translation, translation); + SET_GLOBAL(ATA.devices[driveid].translation, translation); u8 channel = driveid / 2; u8 slave = driveid % 2; - u16 heads = GET_EBDA(ata.devices[driveid].pchs.heads); - u16 cylinders = GET_EBDA(ata.devices[driveid].pchs.cylinders); - u16 spt = GET_EBDA(ata.devices[driveid].pchs.spt); - u64 sectors = GET_EBDA(ata.devices[driveid].sectors); + u16 heads = GET_GLOBAL(ATA.devices[driveid].pchs.heads); + u16 cylinders = GET_GLOBAL(ATA.devices[driveid].pchs.cylinders); + u16 spt = GET_GLOBAL(ATA.devices[driveid].pchs.spt); + u64 sectors = GET_GLOBAL(ATA.devices[driveid].sectors); dprintf(1, "ata%d-%d: PCHS=%u/%d/%d translation=" , channel, slave, cylinders, heads, spt); @@ -750,147 +603,237 @@ setup_translation(int driveid) cylinders = 1024; dprintf(1, " LCHS=%d/%d/%d\n", cylinders, heads, spt); - SET_EBDA(ata.devices[driveid].lchs.heads, heads); - SET_EBDA(ata.devices[driveid].lchs.cylinders, cylinders); - SET_EBDA(ata.devices[driveid].lchs.spt, spt); + SET_GLOBAL(ATA.devices[driveid].lchs.heads, heads); + SET_GLOBAL(ATA.devices[driveid].lchs.cylinders, cylinders); + SET_GLOBAL(ATA.devices[driveid].lchs.spt, spt); } + +/**************************************************************** + * ATA detect and init + ****************************************************************/ + +// Extract common information from IDENTIFY commands. static void -init_drive_ata(int driveid) +extract_identify(int driveid, u16 *buffer) +{ + dprintf(3, "Identify w0=%x w2=%x\n", buffer[0], buffer[2]); + + // Read model name + char *model = ATA.devices[driveid].model; + int maxsize = ARRAY_SIZE(ATA.devices[driveid].model); + int i; + for (i=0; i> 8; + model[i*2+1] = v & 0xff; + } + model[maxsize-1] = 0x00; + + // Trim trailing spaces from model name. + for (i=maxsize-2; i>0 && model[i] == 0x20; i--) + model[i] = 0x00; + + // Extract ATA/ATAPI version. + u16 ataversion = buffer[80]; + u8 version; + for (version=15; version>0; version--) + if (ataversion & (1<> 8) & 0x1f); + SET_GLOBAL(ATA.devices[driveid].blksize, CDROM_SECTOR_SIZE); + + // fill cdidmap + u8 cdcount = GET_GLOBAL(ATA.cdcount); + SET_GLOBAL(ATA.idmap[1][cdcount], driveid); + SET_GLOBAL(ATA.cdcount, cdcount+1); + + // Report drive info to user. + u8 channel = driveid / 2; + u8 slave = driveid % 2; + printf("ata%d-%d: %s ATAPI-%d %s\n", channel, slave + , ATA.devices[driveid].model, ATA.devices[driveid].version + , (ATA.devices[driveid].device == ATA_DEVICE_CDROM + ? "CD-Rom/DVD-Rom" : "Device")); + + return 0; +} + +static int +init_drive_ata(int driveid, u16 *buffer) { - SET_EBDA(ata.devices[driveid].type, ATA_TYPE_ATA); - - // Temporary values to do the transfer - SET_EBDA(ata.devices[driveid].device, ATA_DEVICE_HD); - SET_EBDA(ata.devices[driveid].mode, ATA_MODE_PIO16); - - // Now we send a IDENTIFY command to ATA device - u8 buffer[0x0200]; - memset(buffer, 0, sizeof(buffer)); - u16 ret = ata_cmd_data(driveid, ATA_CMD_IDENTIFY_DEVICE - , 1, 1 - , MAKE_FARPTR(GET_SEG(SS), (u32)buffer)); + // Send an IDENTIFY_DEVICE command to device + memset(buffer, 0, IDE_SECTOR_SIZE); + struct disk_op_s dop; + dop.driveid = driveid; + dop.command = ATA_CMD_IDENTIFY_DEVICE; + dop.count = 1; + dop.lba = 1; + dop.buf_fl = MAKE_FLATPTR(GET_SEG(SS), buffer); + int ret = ata_cmd_data(&dop); if (ret) - BX_PANIC("ata-detect: Failed to detect ATA device\n"); + return ret; - u8 removable = (buffer[0] & 0x80) ? 1 : 0; - u8 mode = buffer[48*2] ? ATA_MODE_PIO32 : ATA_MODE_PIO16; - u16 blksize = IDE_SECTOR_SIZE; + // Success - setup as ATA. + extract_identify(driveid, buffer); + SET_GLOBAL(ATA.devices[driveid].type, ATA_TYPE_ATA); + SET_GLOBAL(ATA.devices[driveid].device, ATA_DEVICE_HD); + SET_GLOBAL(ATA.devices[driveid].blksize, IDE_SECTOR_SIZE); - u16 cylinders = *(u16*)&buffer[1*2]; // word 1 - u16 heads = *(u16*)&buffer[3*2]; // word 3 - u16 spt = *(u16*)&buffer[6*2]; // word 6 + SET_GLOBAL(ATA.devices[driveid].pchs.cylinders, buffer[1]); + SET_GLOBAL(ATA.devices[driveid].pchs.heads, buffer[3]); + SET_GLOBAL(ATA.devices[driveid].pchs.spt, buffer[6]); u64 sectors; - if (*(u16*)&buffer[83*2] & (1 << 10)) // word 83 - lba48 support - sectors = *(u64*)&buffer[100*2]; // word 100-103 + if (buffer[83] & (1 << 10)) // word 83 - lba48 support + sectors = *(u64*)&buffer[100]; // word 100-103 else - sectors = *(u32*)&buffer[60*2]; // word 60 and word 61 - - SET_EBDA(ata.devices[driveid].device, ATA_DEVICE_HD); - SET_EBDA(ata.devices[driveid].removable, removable); - SET_EBDA(ata.devices[driveid].mode, mode); - SET_EBDA(ata.devices[driveid].blksize, blksize); - SET_EBDA(ata.devices[driveid].pchs.heads, heads); - SET_EBDA(ata.devices[driveid].pchs.cylinders, cylinders); - SET_EBDA(ata.devices[driveid].pchs.spt, spt); - SET_EBDA(ata.devices[driveid].sectors, sectors); + sectors = *(u32*)&buffer[60]; // word 60 and word 61 + SET_GLOBAL(ATA.devices[driveid].sectors, sectors); // Setup disk geometry translation. setup_translation(driveid); - // fill hdidmap - u8 hdcount = GET_EBDA(ata.hdcount); - SET_EBDA(ata.idmap[0][hdcount], driveid); - SET_EBDA(ata.hdcount, ++hdcount); - - // Fill "fdpt" structure. - fill_fdpt(driveid); - // Report drive info to user. - u64 sizeinmb = GET_EBDA(ata.devices[driveid].sectors) >> 11; - report_model(driveid, buffer); - u8 version = get_ata_version(buffer); + u8 channel = driveid / 2; + u8 slave = driveid % 2; + char *model = ATA.devices[driveid].model; + printf("ata%d-%d: %s ATA-%d Hard-Disk ", channel, slave, model + , ATA.devices[driveid].version); + u64 sizeinmb = sectors >> 11; if (sizeinmb < (1 << 16)) - printf(" ATA-%d Hard-Disk (%u MiBytes)\n", version, (u32)sizeinmb); + printf("(%u MiBytes)\n", (u32)sizeinmb); else - printf(" ATA-%d Hard-Disk (%u GiBytes)\n", version - , (u32)(sizeinmb >> 10)); + printf("(%u GiBytes)\n", (u32)(sizeinmb >> 10)); + + // Register with bcv system. + add_bcv_hd(driveid, model); + + return 0; } -static void -init_drive_unknown(int driveid) +static int +powerup_await_non_bsy(u16 base, u64 end) { - SET_EBDA(ata.devices[driveid].type, ATA_TYPE_UNKNOWN); - - u8 channel = driveid / 2; - u8 slave = driveid % 2; - printf("ata%d %s: Unknown device\n", channel, slave ? " slave" : "master"); + u8 orstatus = 0; + u8 status; + for (;;) { + status = inb(base+ATA_CB_STAT); + if (!(status & ATA_CB_STAT_BSY)) + break; + orstatus |= status; + if (orstatus == 0xff) { + dprintf(1, "powerup IDE floating\n"); + return orstatus; + } + if (rdtscll() > end) { + dprintf(1, "powerup IDE time out\n"); + return -1; + } + } + dprintf(6, "powerup iobase=%x st=%x\n", base, status); + return status; } static void ata_detect() { // Device detection - int driveid; + u64 end = calc_future_tsc(IDE_TIMEOUT); + int driveid, last_reset_driveid=-1; for(driveid=0; driveid= ARRAY_SIZE(ATA.channels)) + break; - u8 irq = pci_config_readb(d, PCI_INTERRUPT_LINE); - SET_EBDA(ata.channels[count].irq, irq); - SET_EBDA(ata.channels[count].pci_bdf, pci_to_bdf(d)); + u8 irq = pci_config_readb(bdf, PCI_INTERRUPT_LINE); + SET_GLOBAL(ATA.channels[count].irq, irq); + SET_GLOBAL(ATA.channels[count].pci_bdf, bdf); - u8 prog_if = pci_config_readb(d, PCI_CLASS_PROG); + u8 prog_if = pci_config_readb(bdf, PCI_CLASS_PROG); u32 port1, port2; - if (classid != 0x0101 || prog_if & 1) { - port1 = pci_config_readl(d, PCI_BASE_ADDR_0) & ~3; - port2 = pci_config_readl(d, PCI_BASE_ADDR_1) & ~3; + if (prog_if & 1) { + port1 = pci_config_readl(bdf, PCI_BASE_ADDRESS_0) & ~3; + port2 = pci_config_readl(bdf, PCI_BASE_ADDRESS_1) & ~3; } else { port1 = 0x1f0; port2 = 0x3f0; } - SET_EBDA(ata.channels[count].iobase1, port1); - SET_EBDA(ata.channels[count].iobase2, port2); - dprintf(1, "ATA controller %d at %x/%x\n", count, port1, port2); + SET_GLOBAL(ATA.channels[count].iobase1, port1); + SET_GLOBAL(ATA.channels[count].iobase2, port2); + dprintf(1, "ATA controller %d at %x/%x (dev %x prog_if %x)\n" + , count, port1, port2, bdf, prog_if); count++; - if (classid != 0x0101 || prog_if & 4) { - port1 = pci_config_readl(d, PCI_BASE_ADDR_2) & ~3; - port2 = pci_config_readl(d, PCI_BASE_ADDR_3) & ~3; + if (prog_if & 4) { + port1 = pci_config_readl(bdf, PCI_BASE_ADDRESS_2) & ~3; + port2 = pci_config_readl(bdf, PCI_BASE_ADDRESS_3) & ~3; } else { port1 = 0x170; port2 = 0x370; } - dprintf(1, "ATA controller %d at %x/%x\n", count, port1, port2); - SET_EBDA(ata.channels[count].iobase1, port1); - SET_EBDA(ata.channels[count].iobase2, port2); + dprintf(1, "ATA controller %d at %x/%x (dev %x prog_if %x)\n" + , count, port1, port2, bdf, prog_if); + SET_GLOBAL(ATA.channels[count].iobase1, port1); + SET_GLOBAL(ATA.channels[count].iobase2, port2); count++; } } @@ -965,11 +905,66 @@ hard_drive_setup() ata_init(); ata_detect(); - // Store the device count - SET_BDA(disk_count, GET_EBDA(ata.hdcount)); - SET_BDA(disk_control_byte, 0xc0); - // Enable IRQ14 (handle_76) - unmask_pic2(PIC2_IRQ14); + enable_hwirq(14, entry_76); +} + + +/**************************************************************** + * Drive mapping + ****************************************************************/ + +// Fill in Fixed Disk Parameter Table (located in ebda). +static void +fill_fdpt(int driveid) +{ + if (driveid > 1) + return; + + u16 nlc = GET_GLOBAL(ATA.devices[driveid].lchs.cylinders); + u16 nlh = GET_GLOBAL(ATA.devices[driveid].lchs.heads); + u16 nlspt = GET_GLOBAL(ATA.devices[driveid].lchs.spt); + + u16 npc = GET_GLOBAL(ATA.devices[driveid].pchs.cylinders); + u16 nph = GET_GLOBAL(ATA.devices[driveid].pchs.heads); + u16 npspt = GET_GLOBAL(ATA.devices[driveid].pchs.spt); + + struct fdpt_s *fdpt = &get_ebda_ptr()->fdpt[driveid]; + fdpt->precompensation = 0xffff; + fdpt->drive_control_byte = 0xc0 | ((nph > 8) << 3); + fdpt->landing_zone = npc; + fdpt->cylinders = nlc; + fdpt->heads = nlh; + fdpt->sectors = nlspt; + + if (nlc == npc && nlh == nph && nlspt == npspt) + // no logical CHS mapping used, just physical CHS + // use Standard Fixed Disk Parameter Table (FDPT) + return; + + // complies with Phoenix style Translated Fixed Disk Parameter + // Table (FDPT) + fdpt->phys_cylinders = npc; + fdpt->phys_heads = nph; + fdpt->phys_sectors = npspt; + fdpt->a0h_signature = 0xa0; + + // Checksum structure. + u8 sum = checksum(fdpt, sizeof(*fdpt)-1); + fdpt->checksum = -sum; +} + +// Map a drive (that was registered via add_bcv_hd) +void +map_drive(int driveid) +{ + // fill hdidmap + u8 hdcount = GET_BDA(hdcount); + dprintf(3, "Mapping driveid %d to %d\n", driveid, hdcount); + SET_GLOBAL(ATA.idmap[0][hdcount], driveid); + SET_BDA(hdcount, hdcount + 1); + + // Fill "fdpt" structure. + fill_fdpt(hdcount); }