Initial support for ATA disks.
authorKevin O'Connor <kevin@koconnor.net>
Fri, 29 Feb 2008 05:22:27 +0000 (00:22 -0500)
committerKevin O'Connor <kevin@koconnor.net>
Fri, 29 Feb 2008 05:22:27 +0000 (00:22 -0500)
Makefile
src/ata.c [new file with mode: 0644]
src/ata.h [new file with mode: 0644]
src/biosvar.h
src/boot.c
src/config.h
src/disk.c
src/disk.h
src/ioport.h

index ba2a80e..8d97a00 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -8,7 +8,8 @@
 OUT=out/
 
 # Source files
-SRC16=floppy.c disk.c system.c clock.c serial.c kbd.c mouse.c output.c boot.c
+SRC16=floppy.c disk.c system.c clock.c serial.c kbd.c mouse.c output.c \
+      boot.c ata.c
 SRC32=post.c output.c
 
 cc-option = $(shell if test -z "`$(1) $(2) -S -o /dev/null -xc \
diff --git a/src/ata.c b/src/ata.c
new file mode 100644 (file)
index 0000000..3818fb6
--- /dev/null
+++ b/src/ata.c
@@ -0,0 +1,616 @@
+#include "ata.h" // ATA_*
+#include "types.h" // u8
+#include "ioport.h" // inb
+#include "util.h" // BX_INFO
+
+#define TIMEOUT 0
+#define BSY 1
+#define NOT_BSY 2
+#define NOT_BSY_DRQ 3
+#define NOT_BSY_NOT_DRQ 4
+#define NOT_BSY_RDY 5
+
+#define IDE_TIMEOUT 32000u //32 seconds max for IDE ops
+
+#define BX_DEBUG_ATA BX_INFO
+
+static int
+await_ide(u8 when_done, u16 base, u16 timeout)
+{
+    u32 time=0,last=0;
+    // for the times you're supposed to throw one away
+    u16 status = inb(base + ATA_CB_STAT);
+    for (;;) {
+        status = inb(base+ATA_CB_STAT);
+        time++;
+        u8 result;
+        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);
+        else if (when_done == TIMEOUT)
+            result = 0;
+
+        if (result)
+            return 0;
+        // mod 2048 each 16 ms
+        if (time>>16 != last) {
+            last = time >>16;
+            BX_DEBUG_ATA("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) {
+            BX_DEBUG_ATA("await_ide: ERROR (TIMEOUT,BSY,!BSY,!BSY_DRQ,!BSY_!DRQ,!BSY_RDY) %d time= %d timeout= %d\n",when_done,time>>11, timeout);
+            return -1;
+        }
+        if ((timeout == 0) || ((time>>11) > timeout))
+            break;
+    }
+    BX_INFO("IDE time out\n");
+    return -1;
+}
+
+
+// ---------------------------------------------------------------------------
+// ATA/ATAPI driver : software reset
+// ---------------------------------------------------------------------------
+// ATA-3
+// 8.2.1 Software reset - Device 0
+
+void
+ata_reset(u16 device)
+{
+    u16 iobase1, iobase2;
+    u8  channel, slave, sn, sc;
+    u8  type;
+
+    channel = device / 2;
+    slave = device % 2;
+
+    iobase1 = GET_EBDA(ata.channels[channel].iobase1);
+    iobase2 = GET_EBDA(ata.channels[channel].iobase2);
+
+    // Reset
+
+    // 8.2.1 (a) -- set SRST in DC
+    outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN | ATA_CB_DC_SRST, iobase2+ATA_CB_DC);
+
+    // 8.2.1 (b) -- wait for BSY
+    await_ide(BSY, iobase1, 20);
+
+    // 8.2.1 (f) -- clear SRST
+    outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2+ATA_CB_DC);
+
+    type=GET_EBDA(ata.devices[device].type);
+    if (type != ATA_TYPE_NONE) {
+
+        // 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);
+        sc = inb(iobase1+ATA_CB_SC);
+        sn = inb(iobase1+ATA_CB_SN);
+
+        if ( (sc==0x01) && (sn==0x01) ) {
+            if (type == ATA_TYPE_ATA) //ATA
+                await_ide(NOT_BSY_RDY, iobase1, IDE_TIMEOUT);
+            else //ATAPI
+                await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
+        }
+
+        // 8.2.1 (h) -- wait for not BSY
+        await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
+    }
+
+    // Enable interrupts
+    outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC);
+}
+
+static void
+insw(u16 port, u16 segment, u16 offset, u16 count)
+{
+    u16 i;
+    for (i=0; i<count; i++) {
+        u16 d = inw(port);
+        SET_FARVAR(segment, *(u16*)(offset + i), d);
+    }
+}
+
+static void
+insl(u16 port, u16 segment, u16 offset, u16 count)
+{
+    u16 i;
+    for (i=0; i<count; i++) {
+        u32 d = inl(port);
+        SET_FARVAR(segment, *(u32*)(offset + i), d);
+    }
+}
+
+static void
+outsw(u16 port, u16 segment, u16 offset, u16 count)
+{
+    u16 i;
+    for (i=0; i<count; i++) {
+        u16 d = GET_FARVAR(segment, *(u16*)(offset + i));
+        outw(d, port);
+    }
+}
+
+static void
+outsl(u16 port, u16 segment, u16 offset, u16 count)
+{
+    u16 i;
+    for (i=0; i<count; i++) {
+        u32 d = GET_FARVAR(segment, *(u32*)(offset + i));
+        outl(d, port);
+    }
+}
+
+
+// ---------------------------------------------------------------------------
+// ATA/ATAPI driver : execute a data-in command
+// ---------------------------------------------------------------------------
+      // returns
+      // 0 : no error
+      // 1 : BUSY bit set
+      // 2 : read error
+      // 3 : expected DRQ=1
+      // 4 : no sectors left to read/verify
+      // 5 : more sectors to read/verify
+      // 6 : no sectors left to write
+      // 7 : more sectors to write
+u16
+ata_cmd_data_in(u16 device, u16 command, u16 count, u16 cylinder
+                , u16 head, u16 sector, u32 lba, u16 segment, u16 offset)
+{
+    u16 iobase1, iobase2, blksize;
+    u8  channel, slave;
+    u8  status, current, mode;
+
+    channel = device / 2;
+    slave   = device % 2;
+
+    iobase1 = GET_EBDA(ata.channels[channel].iobase1);
+    iobase2 = GET_EBDA(ata.channels[channel].iobase2);
+    mode    = GET_EBDA(ata.devices[device].mode);
+    blksize = 0x200;
+    if (mode == ATA_MODE_PIO32) blksize>>=2;
+    else blksize>>=1;
+
+    // Reset count of transferred data
+    SET_EBDA(ata.trsfsectors,0);
+    SET_EBDA(ata.trsfbytes,0L);
+    current = 0;
+
+    status = inb(iobase1 + ATA_CB_STAT);
+    if (status & ATA_CB_STAT_BSY)
+        return 1;
+
+    outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2 + ATA_CB_DC);
+
+    // sector will be 0 only on lba access. Convert to lba-chs
+    if (sector == 0) {
+        if ((count >= 1 << 8) || (lba + count >= 1UL << 28)) {
+            outb(0x00, iobase1 + ATA_CB_FR);
+            outb((count >> 8) & 0xff, iobase1 + ATA_CB_SC);
+            outb(lba >> 24, iobase1 + ATA_CB_SN);
+            outb(0, iobase1 + ATA_CB_CL);
+            outb(0, iobase1 + ATA_CB_CH);
+            command |= 0x04;
+            count &= (1UL << 8) - 1;
+            lba &= (1UL << 24) - 1;
+        }
+        sector = (u16) (lba & 0x000000ffL);
+        cylinder = (u16) ((lba>>8) & 0x0000ffffL);
+        head = ((u16) ((lba>>24) & 0x0000000fL)) | ATA_CB_DH_LBA;
+    }
+
+    outb(0x00, iobase1 + ATA_CB_FR);
+    outb(count, iobase1 + ATA_CB_SC);
+    outb(sector, iobase1 + ATA_CB_SN);
+    outb(cylinder & 0x00ff, iobase1 + ATA_CB_CL);
+    outb(cylinder >> 8, iobase1 + ATA_CB_CH);
+    outb((slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (u8) head
+         , iobase1 + ATA_CB_DH);
+    outb(command, iobase1 + ATA_CB_CMD);
+
+    await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
+    status = inb(iobase1 + ATA_CB_STAT);
+
+    if (status & ATA_CB_STAT_ERR) {
+        BX_DEBUG_ATA("ata_cmd_data_in : read error\n");
+        return 2;
+    } else if ( !(status & ATA_CB_STAT_DRQ) ) {
+        BX_DEBUG_ATA("ata_cmd_data_in : DRQ not set (status %02x)\n"
+                     , (unsigned) status);
+        return 3;
+    }
+
+    // FIXME : move seg/off translation here
+
+    irq_enable();
+
+    while (1) {
+
+        if (offset > 0xf800) {
+            offset -= 0x800;
+            segment += 0x80;
+        }
+
+        if (mode == ATA_MODE_PIO32)
+            insw(iobase1, segment, offset, blksize);
+        else
+            insl(iobase1, segment, offset, blksize);
+
+        current++;
+        SET_EBDA(ata.trsfsectors,current);
+        count--;
+        await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
+        status = inb(iobase1 + ATA_CB_STAT);
+        if (count == 0) {
+            if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ
+                            | ATA_CB_STAT_ERR) )
+                 != ATA_CB_STAT_RDY ) {
+                BX_DEBUG_ATA("ata_cmd_data_in : no sectors left (status %02x)\n"
+                             , (unsigned) status);
+                return 4;
+            }
+            break;
+        }
+        else {
+            if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ
+                            | ATA_CB_STAT_ERR) )
+                 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
+                BX_DEBUG_ATA("ata_cmd_data_in : more sectors left (status %02x)\n"
+                             , (unsigned) status);
+                return 5;
+            }
+            continue;
+        }
+    }
+    // Enable interrupts
+    outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC);
+    return 0;
+}
+
+// ---------------------------------------------------------------------------
+// ATA/ATAPI driver : execute a data-out command
+// ---------------------------------------------------------------------------
+      // returns
+      // 0 : no error
+      // 1 : BUSY bit set
+      // 2 : read error
+      // 3 : expected DRQ=1
+      // 4 : no sectors left to read/verify
+      // 5 : more sectors to read/verify
+      // 6 : no sectors left to write
+      // 7 : more sectors to write
+u16
+ata_cmd_data_out(u16 device, u16 command, u16 count, u16 cylinder
+                 , u16 head, u16 sector, u32 lba, u16 segment, u16 offset)
+{
+    u16 iobase1, iobase2, blksize;
+    u8  channel, slave;
+    u8  status, current, mode;
+
+    channel = device / 2;
+    slave   = device % 2;
+
+    iobase1 = GET_EBDA(ata.channels[channel].iobase1);
+    iobase2 = GET_EBDA(ata.channels[channel].iobase2);
+    mode    = GET_EBDA(ata.devices[device].mode);
+    blksize = 0x200;
+    if (mode == ATA_MODE_PIO32)
+        blksize>>=2;
+    else
+        blksize>>=1;
+
+    // Reset count of transferred data
+    SET_EBDA(ata.trsfsectors,0);
+    SET_EBDA(ata.trsfbytes,0L);
+    current = 0;
+
+    status = inb(iobase1 + ATA_CB_STAT);
+    if (status & ATA_CB_STAT_BSY)
+        return 1;
+
+    outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2 + ATA_CB_DC);
+
+    // sector will be 0 only on lba access. Convert to lba-chs
+    if (sector == 0) {
+        if ((count >= 1 << 8) || (lba + count >= 1UL << 28)) {
+            outb(0x00, iobase1 + ATA_CB_FR);
+            outb((count >> 8) & 0xff, iobase1 + ATA_CB_SC);
+            outb(lba >> 24, iobase1 + ATA_CB_SN);
+            outb(0, iobase1 + ATA_CB_CL);
+            outb(0, iobase1 + ATA_CB_CH);
+            command |= 0x04;
+            count &= (1UL << 8) - 1;
+            lba &= (1UL << 24) - 1;
+        }
+        sector = (u16) (lba & 0x000000ffL);
+        cylinder = (u16) ((lba>>8) & 0x0000ffffL);
+        head = ((u16) ((lba>>24) & 0x0000000fL)) | ATA_CB_DH_LBA;
+    }
+
+    outb(0x00, iobase1 + ATA_CB_FR);
+    outb(count, iobase1 + ATA_CB_SC);
+    outb(sector, iobase1 + ATA_CB_SN);
+    outb(cylinder & 0x00ff, iobase1 + ATA_CB_CL);
+    outb(cylinder >> 8, iobase1 + ATA_CB_CH);
+    outb((slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (u8) head
+         , iobase1 + ATA_CB_DH);
+    outb(command, iobase1 + ATA_CB_CMD);
+
+    await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
+    status = inb(iobase1 + ATA_CB_STAT);
+
+    if (status & ATA_CB_STAT_ERR) {
+        BX_DEBUG_ATA("ata_cmd_data_out : read error\n");
+        return 2;
+    } else if ( !(status & ATA_CB_STAT_DRQ) ) {
+        BX_DEBUG_ATA("ata_cmd_data_out : DRQ not set (status %02x)\n"
+                     , (unsigned) status);
+        return 3;
+    }
+
+    // FIXME : move seg/off translation here
+
+    irq_enable();
+
+    while (1) {
+
+        if (offset > 0xf800) {
+            offset -= 0x800;
+            segment += 0x80;
+        }
+
+        if (mode == ATA_MODE_PIO32)
+            outsw(iobase1, segment, offset, blksize);
+        else
+            outsl(iobase1, segment, offset, blksize);
+
+        current++;
+        SET_EBDA(ata.trsfsectors,current);
+        count--;
+        status = inb(iobase1 + ATA_CB_STAT);
+        if (count == 0) {
+            if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF
+                            | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
+                 != ATA_CB_STAT_RDY ) {
+                BX_DEBUG_ATA("ata_cmd_data_out : no sectors left (status %02x)\n"
+                             , (unsigned) status);
+                return 6;
+            }
+            break;
+        } else {
+            if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ
+                            | ATA_CB_STAT_ERR) )
+                 != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
+                BX_DEBUG_ATA("ata_cmd_data_out : more sectors left (status %02x)\n"
+                             , (unsigned) status);
+                return 7;
+            }
+            continue;
+        }
+    }
+    // Enable interrupts
+    outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC);
+    return 0;
+}
+
+// ---------------------------------------------------------------------------
+// ATA/ATAPI driver : execute a packet command
+// ---------------------------------------------------------------------------
+      // returns
+      // 0 : no error
+      // 1 : error in parameters
+      // 2 : BUSY bit set
+      // 3 : error
+      // 4 : not ready
+u16
+ata_cmd_packet(u16 device, u8 cmdlen, u16 cmdseg, u16 cmdoff, u16 header
+               , u32 length, u8 inout, u16 bufseg, u16 bufoff)
+{
+    u16 iobase1, iobase2;
+    u16 lcount, lbefore, lafter, count;
+    u8  channel, slave;
+    u8  status, mode, lmode;
+    u32 transfer;
+
+    channel = device / 2;
+    slave = device % 2;
+
+    // Data out is not supported yet
+    if (inout == ATA_DATA_OUT) {
+        BX_INFO("ata_cmd_packet: DATA_OUT not supported yet\n");
+        return 1;
+    }
+
+    // The header length must be even
+    if (header & 1) {
+        BX_DEBUG_ATA("ata_cmd_packet : header must be even (%04x)\n",header);
+        return 1;
+    }
+
+    iobase1 = GET_EBDA(ata.channels[channel].iobase1);
+    iobase2 = GET_EBDA(ata.channels[channel].iobase2);
+    mode    = GET_EBDA(ata.devices[device].mode);
+    transfer= 0L;
+
+    if (cmdlen < 12)
+        cmdlen=12;
+    if (cmdlen > 12)
+        cmdlen=16;
+    cmdlen>>=1;
+
+    // Reset count of transferred data
+    SET_EBDA(ata.trsfsectors,0);
+    SET_EBDA(ata.trsfbytes,0L);
+
+    status = inb(iobase1 + ATA_CB_STAT);
+    if (status & ATA_CB_STAT_BSY)
+        return 2;
+
+    outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2 + ATA_CB_DC);
+    outb(0x00, iobase1 + ATA_CB_FR);
+    outb(0x00, iobase1 + ATA_CB_SC);
+    outb(0x00, iobase1 + ATA_CB_SN);
+    outb(0xfff0 & 0x00ff, iobase1 + ATA_CB_CL);
+    outb(0xfff0 >> 8, iobase1 + ATA_CB_CH);
+    outb(slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0, iobase1 + ATA_CB_DH);
+    outb(ATA_CMD_PACKET, iobase1 + ATA_CB_CMD);
+
+    // Device should ok to receive command
+    await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
+    status = inb(iobase1 + ATA_CB_STAT);
+
+    if (status & ATA_CB_STAT_ERR) {
+        BX_DEBUG_ATA("ata_cmd_packet : error, status is %02x\n",status);
+        return 3;
+    } else if ( !(status & ATA_CB_STAT_DRQ) ) {
+        BX_DEBUG_ATA("ata_cmd_packet : DRQ not set (status %02x)\n"
+                     , (unsigned) status);
+        return 4;
+    }
+
+    // Normalize address
+    cmdseg += (cmdoff / 16);
+    cmdoff %= 16;
+
+    // Send command to device
+    irq_enable();
+
+    outsw(iobase1, cmdseg, cmdoff, cmdlen);
+
+    if (inout == ATA_DATA_NO) {
+        await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
+        status = inb(iobase1 + ATA_CB_STAT);
+    }
+    else {
+        u16 loops = 0;
+        u8 sc;
+        while (1) {
+
+            if (loops == 0) {//first time through
+                status = inb(iobase2 + ATA_CB_ASTAT);
+                await_ide(NOT_BSY_DRQ, iobase1, IDE_TIMEOUT);
+            }
+            else
+                await_ide(NOT_BSY, iobase1, IDE_TIMEOUT);
+            loops++;
+
+            status = inb(iobase1 + ATA_CB_STAT);
+            sc = inb(iobase1 + ATA_CB_SC);
+
+            // Check if command completed
+            if(((inb(iobase1 + ATA_CB_SC)&0x7)==0x3) &&
+               ((status & (ATA_CB_STAT_RDY | ATA_CB_STAT_ERR)) == ATA_CB_STAT_RDY))
+                break;
+
+            if (status & ATA_CB_STAT_ERR) {
+                BX_DEBUG_ATA("ata_cmd_packet : error (status %02x)\n",status);
+                return 3;
+            }
+
+            // Normalize address
+            bufseg += (bufoff / 16);
+            bufoff %= 16;
+
+            // Get the byte count
+            lcount =  ((u16)(inb(iobase1 + ATA_CB_CH))<<8)+inb(iobase1 + ATA_CB_CL);
+
+            // adjust to read what we want
+            if(header>lcount) {
+                lbefore=lcount;
+                header-=lcount;
+                lcount=0;
+            }
+            else {
+                lbefore=header;
+                header=0;
+                lcount-=lbefore;
+            }
+
+            if(lcount>length) {
+                lafter=lcount-length;
+                lcount=length;
+                length=0;
+            }
+            else {
+                lafter=0;
+                length-=lcount;
+            }
+
+            // Save byte count
+            count = lcount;
+
+            BX_DEBUG_ATA("Trying to read %04x bytes (%04x %04x %04x) "
+                         ,lbefore+lcount+lafter,lbefore,lcount,lafter);
+            BX_DEBUG_ATA("to 0x%04x:0x%04x\n",bufseg,bufoff);
+
+            // If counts not dividable by 4, use 16bits mode
+            lmode = mode;
+            if (lbefore & 0x03) lmode=ATA_MODE_PIO16;
+            if (lcount  & 0x03) lmode=ATA_MODE_PIO16;
+            if (lafter  & 0x03) lmode=ATA_MODE_PIO16;
+
+            // adds an extra byte if count are odd. before is always even
+            if (lcount & 0x01) {
+                lcount+=1;
+                if ((lafter > 0) && (lafter & 0x01)) {
+                    lafter-=1;
+                }
+            }
+
+            if (lmode == ATA_MODE_PIO32) {
+                lcount>>=2; lbefore>>=2; lafter>>=2;
+            }
+            else {
+                lcount>>=1; lbefore>>=1; lafter>>=1;
+            }
+
+            int i;
+            for (i=0; i<lbefore; i++)
+                if (lmode == ATA_MODE_PIO32)
+                    inl(iobase1);
+                else
+                    inw(iobase1);
+
+            if (lmode == ATA_MODE_PIO32)
+                insl(iobase1, bufoff, bufseg, lcount);
+            else
+                insw(iobase1, bufoff, bufseg, lcount);
+
+            for (i=0; i<lafter; i++)
+                if (lmode == ATA_MODE_PIO32)
+                    inl(iobase1);
+                else
+                    inw(iobase1);
+
+            // Compute new buffer address
+            bufoff += count;
+
+            // Save transferred bytes count
+            transfer += count;
+            SET_EBDA(ata.trsfbytes,transfer);
+        }
+    }
+
+    // Final check, device must be ready
+    if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF
+                    | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) )
+         != ATA_CB_STAT_RDY ) {
+        BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n"
+                     , (unsigned) status);
+        return 4;
+    }
+
+    // Enable interrupts
+    outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC);
+    return 0;
+}
diff --git a/src/ata.h b/src/ata.h
new file mode 100644 (file)
index 0000000..35903df
--- /dev/null
+++ b/src/ata.h
@@ -0,0 +1,148 @@
+#include "types.h" // u16
+
+// Function definitions
+void ata_reset(u16 device);
+u16 ata_cmd_data_in(u16 device, u16 command, u16 count, u16 cylinder
+                    , u16 head, u16 sector, u32 lba, u16 segment, u16 offset);
+u16 ata_cmd_data_out(u16 device, u16 command, u16 count, u16 cylinder
+                     , u16 head, u16 sector, u32 lba, u16 segment, u16 offset);
+u16 ata_cmd_packet(u16 device, u8 cmdlen, u16 cmdseg, u16 cmdoff, u16 header
+                   , u32 length, u8 inout, u16 bufseg, u16 bufoff);
+
+// Global defines -- ATA register and register bits.
+// command block & control block regs
+#define ATA_CB_DATA  0   // data reg         in/out pio_base_addr1+0
+#define ATA_CB_ERR   1   // error            in     pio_base_addr1+1
+#define ATA_CB_FR    1   // feature reg         out pio_base_addr1+1
+#define ATA_CB_SC    2   // sector count     in/out pio_base_addr1+2
+#define ATA_CB_SN    3   // sector number    in/out pio_base_addr1+3
+#define ATA_CB_CL    4   // cylinder low     in/out pio_base_addr1+4
+#define ATA_CB_CH    5   // cylinder high    in/out pio_base_addr1+5
+#define ATA_CB_DH    6   // device head      in/out pio_base_addr1+6
+#define ATA_CB_STAT  7   // primary status   in     pio_base_addr1+7
+#define ATA_CB_CMD   7   // command             out pio_base_addr1+7
+#define ATA_CB_ASTAT 6   // alternate status in     pio_base_addr2+6
+#define ATA_CB_DC    6   // device control      out pio_base_addr2+6
+#define ATA_CB_DA    7   // device address   in     pio_base_addr2+7
+
+#define ATA_CB_ER_ICRC 0x80    // ATA Ultra DMA bad CRC
+#define ATA_CB_ER_BBK  0x80    // ATA bad block
+#define ATA_CB_ER_UNC  0x40    // ATA uncorrected error
+#define ATA_CB_ER_MC   0x20    // ATA media change
+#define ATA_CB_ER_IDNF 0x10    // ATA id not found
+#define ATA_CB_ER_MCR  0x08    // ATA media change request
+#define ATA_CB_ER_ABRT 0x04    // ATA command aborted
+#define ATA_CB_ER_NTK0 0x02    // ATA track 0 not found
+#define ATA_CB_ER_NDAM 0x01    // ATA address mark not found
+
+#define ATA_CB_ER_P_SNSKEY 0xf0   // ATAPI sense key (mask)
+#define ATA_CB_ER_P_MCR    0x08   // ATAPI Media Change Request
+#define ATA_CB_ER_P_ABRT   0x04   // ATAPI command abort
+#define ATA_CB_ER_P_EOM    0x02   // ATAPI End of Media
+#define ATA_CB_ER_P_ILI    0x01   // ATAPI Illegal Length Indication
+
+// ATAPI Interrupt Reason bits in the Sector Count reg (CB_SC)
+#define ATA_CB_SC_P_TAG    0xf8   // ATAPI tag (mask)
+#define ATA_CB_SC_P_REL    0x04   // ATAPI release
+#define ATA_CB_SC_P_IO     0x02   // ATAPI I/O
+#define ATA_CB_SC_P_CD     0x01   // ATAPI C/D
+
+// bits 7-4 of the device/head (CB_DH) reg
+#define ATA_CB_DH_DEV0 0xa0    // select device 0
+#define ATA_CB_DH_DEV1 0xb0    // select device 1
+#define ATA_CB_DH_LBA 0x40    // use LBA
+
+// status reg (CB_STAT and CB_ASTAT) bits
+#define ATA_CB_STAT_BSY  0x80  // busy
+#define ATA_CB_STAT_RDY  0x40  // ready
+#define ATA_CB_STAT_DF   0x20  // device fault
+#define ATA_CB_STAT_WFT  0x20  // write fault (old name)
+#define ATA_CB_STAT_SKC  0x10  // seek complete
+#define ATA_CB_STAT_SERV 0x10  // service
+#define ATA_CB_STAT_DRQ  0x08  // data request
+#define ATA_CB_STAT_CORR 0x04  // corrected
+#define ATA_CB_STAT_IDX  0x02  // index
+#define ATA_CB_STAT_ERR  0x01  // error (ATA)
+#define ATA_CB_STAT_CHK  0x01  // check (ATAPI)
+
+// device control reg (CB_DC) bits
+#define ATA_CB_DC_HD15   0x08  // bit should always be set to one
+#define ATA_CB_DC_SRST   0x04  // soft reset
+#define ATA_CB_DC_NIEN   0x02  // disable interrupts
+
+// Most mandtory and optional ATA commands (from ATA-3),
+#define ATA_CMD_CFA_ERASE_SECTORS            0xC0
+#define ATA_CMD_CFA_REQUEST_EXT_ERR_CODE     0x03
+#define ATA_CMD_CFA_TRANSLATE_SECTOR         0x87
+#define ATA_CMD_CFA_WRITE_MULTIPLE_WO_ERASE  0xCD
+#define ATA_CMD_CFA_WRITE_SECTORS_WO_ERASE   0x38
+#define ATA_CMD_CHECK_POWER_MODE1            0xE5
+#define ATA_CMD_CHECK_POWER_MODE2            0x98
+#define ATA_CMD_DEVICE_RESET                 0x08
+#define ATA_CMD_EXECUTE_DEVICE_DIAGNOSTIC    0x90
+#define ATA_CMD_FLUSH_CACHE                  0xE7
+#define ATA_CMD_FORMAT_TRACK                 0x50
+#define ATA_CMD_IDENTIFY_DEVICE              0xEC
+#define ATA_CMD_IDENTIFY_DEVICE_PACKET       0xA1
+#define ATA_CMD_IDENTIFY_PACKET_DEVICE       0xA1
+#define ATA_CMD_IDLE1                        0xE3
+#define ATA_CMD_IDLE2                        0x97
+#define ATA_CMD_IDLE_IMMEDIATE1              0xE1
+#define ATA_CMD_IDLE_IMMEDIATE2              0x95
+#define ATA_CMD_INITIALIZE_DRIVE_PARAMETERS  0x91
+#define ATA_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91
+#define ATA_CMD_NOP                          0x00
+#define ATA_CMD_PACKET                       0xA0
+#define ATA_CMD_READ_BUFFER                  0xE4
+#define ATA_CMD_READ_DMA                     0xC8
+#define ATA_CMD_READ_DMA_QUEUED              0xC7
+#define ATA_CMD_READ_MULTIPLE                0xC4
+#define ATA_CMD_READ_SECTORS                 0x20
+#define ATA_CMD_READ_VERIFY_SECTORS          0x40
+#define ATA_CMD_RECALIBRATE                  0x10
+#define ATA_CMD_REQUEST_SENSE                0x03
+#define ATA_CMD_SEEK                         0x70
+#define ATA_CMD_SET_FEATURES                 0xEF
+#define ATA_CMD_SET_MULTIPLE_MODE            0xC6
+#define ATA_CMD_SLEEP1                       0xE6
+#define ATA_CMD_SLEEP2                       0x99
+#define ATA_CMD_STANDBY1                     0xE2
+#define ATA_CMD_STANDBY2                     0x96
+#define ATA_CMD_STANDBY_IMMEDIATE1           0xE0
+#define ATA_CMD_STANDBY_IMMEDIATE2           0x94
+#define ATA_CMD_WRITE_BUFFER                 0xE8
+#define ATA_CMD_WRITE_DMA                    0xCA
+#define ATA_CMD_WRITE_DMA_QUEUED             0xCC
+#define ATA_CMD_WRITE_MULTIPLE               0xC5
+#define ATA_CMD_WRITE_SECTORS                0x30
+#define ATA_CMD_WRITE_VERIFY                 0x3C
+
+#define ATA_IFACE_NONE    0x00
+#define ATA_IFACE_ISA     0x00
+#define ATA_IFACE_PCI     0x01
+
+#define ATA_TYPE_NONE     0x00
+#define ATA_TYPE_UNKNOWN  0x01
+#define ATA_TYPE_ATA      0x02
+#define ATA_TYPE_ATAPI    0x03
+
+#define ATA_DEVICE_NONE  0x00
+#define ATA_DEVICE_HD    0xFF
+#define ATA_DEVICE_CDROM 0x05
+
+#define ATA_MODE_NONE    0x00
+#define ATA_MODE_PIO16   0x00
+#define ATA_MODE_PIO32   0x01
+#define ATA_MODE_ISADMA  0x02
+#define ATA_MODE_PCIDMA  0x03
+#define ATA_MODE_USEIRQ  0x10
+
+#define ATA_TRANSLATION_NONE  0
+#define ATA_TRANSLATION_LBA   1
+#define ATA_TRANSLATION_LARGE 2
+#define ATA_TRANSLATION_RECHS 3
+
+#define ATA_DATA_NO      0x00
+#define ATA_DATA_IN      0x01
+#define ATA_DATA_OUT     0x02
+
index d4c04c7..0dd603b 100644 (file)
@@ -57,7 +57,9 @@ struct bios_data_area_s {
     u32 timer_counter;
     // 40:70
     u8 timer_rollover;
-    u8 other4[0x04];
+    u8 break_flag;
+    u16 soft_reset_flag;
+    u8 disk_last_status;
     u8 disk_count;
     u8 disk_control_byte;
     u8 port_disk;
@@ -68,7 +70,9 @@ struct bios_data_area_s {
     u16 kbd_buf_end_offset;
     u8 other5[7];
     u8 floppy_last_data_rate;
-    u8 other6[3];
+    u8 disk_status_controller;
+    u8 disk_error_controller;
+    u8 disk_interrupt_flag;
     u8 floppy_harddisk_info;
     // 40:90
     u8 floppy_media_state[4];
index 2a27086..867b2fe 100644 (file)
@@ -9,6 +9,7 @@
 #include "biosvar.h" // struct bregs
 #include "config.h" // CONFIG_*
 #include "cmos.h" // inb_cmos
+#include "disk.h" // ata_detect
 
 //--------------------------------------------------------------------------
 // print_boot_device
@@ -177,6 +178,7 @@ handle_19()
 void VISIBLE
 begin_boot()
 {
+    ata_detect();
     irq_enable();
     struct bregs br;
     memset(&br, 0, sizeof(br));
index 5d91c60..feb8f27 100644 (file)
@@ -4,7 +4,7 @@
 
 #define CONFIG_FLOPPY_SUPPORT 1
 #define CONFIG_PS2_MOUSE 0
-#define CONFIG_ATA 0
+#define CONFIG_ATA 1
 #define CONFIG_KBD_CALL_INT15_4F 1
 #define CONFIG_ELTORITO_BOOT 0
 #define CONFIG_MAX_ATA_INTERFACES 4
index 675e9bf..02fb756 100644 (file)
 
 #include "disk.h" // floppy_13
 #include "biosvar.h" // struct bregs
+#include "config.h" // CONFIG_*
+#include "cmos.h" // inb_cmos
 #include "util.h" // debug_enter
+#include "ata.h" // ATA_*
+
+
+// ---------------------------------------------------------------------------
+// ATA/ATAPI driver : device detection
+// ---------------------------------------------------------------------------
+
+void
+ata_detect()
+{
+    u8  hdcount, cdcount, device, type;
+    u8  buffer[0x0200];
+
+#if CONFIG_MAX_ATA_INTERFACES > 0
+    SET_EBDA(ata.channels[0].iface,ATA_IFACE_ISA);
+    SET_EBDA(ata.channels[0].iobase1,0x1f0);
+    SET_EBDA(ata.channels[0].iobase2,0x3f0);
+    SET_EBDA(ata.channels[0].irq,14);
+#endif
+#if CONFIG_MAX_ATA_INTERFACES > 1
+    SET_EBDA(ata.channels[1].iface,ATA_IFACE_ISA);
+    SET_EBDA(ata.channels[1].iobase1,0x170);
+    SET_EBDA(ata.channels[1].iobase2,0x370);
+    SET_EBDA(ata.channels[1].irq,15);
+#endif
+#if CONFIG_MAX_ATA_INTERFACES > 2
+    SET_EBDA(ata.channels[2].iface,ATA_IFACE_ISA);
+    SET_EBDA(ata.channels[2].iobase1,0x1e8);
+    SET_EBDA(ata.channels[2].iobase2,0x3e0);
+    SET_EBDA(ata.channels[2].irq,12);
+#endif
+#if CONFIG_MAX_ATA_INTERFACES > 3
+    SET_EBDA(ata.channels[3].iface,ATA_IFACE_ISA);
+    SET_EBDA(ata.channels[3].iobase1,0x168);
+    SET_EBDA(ata.channels[3].iobase2,0x360);
+    SET_EBDA(ata.channels[3].irq,11);
+#endif
+#if CONFIG_MAX_ATA_INTERFACES > 4
+#error Please fill the ATA interface informations
+#endif
+
+    // Device detection
+    hdcount=cdcount=0;
+
+    for(device=0; device<CONFIG_MAX_ATA_DEVICES; device++) {
+        u16 iobase1, iobase2;
+        u8  channel, slave, shift;
+        u8  sc, sn, cl, ch, st;
+
+        channel = device / 2;
+        slave = device % 2;
+
+        iobase1 =GET_EBDA(ata.channels[channel].iobase1);
+        iobase2 =GET_EBDA(ata.channels[channel].iobase2);
+
+        // Disable interrupts
+        outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2+ATA_CB_DC);
+
+        // Look for device
+        outb(slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0, iobase1+ATA_CB_DH);
+        outb(0x55, iobase1+ATA_CB_SC);
+        outb(0xaa, iobase1+ATA_CB_SN);
+        outb(0xaa, iobase1+ATA_CB_SC);
+        outb(0x55, iobase1+ATA_CB_SN);
+        outb(0x55, iobase1+ATA_CB_SC);
+        outb(0xaa, iobase1+ATA_CB_SN);
+
+        // If we found something
+        sc = inb(iobase1+ATA_CB_SC);
+        sn = inb(iobase1+ATA_CB_SN);
+
+        if ( (sc == 0x55) && (sn == 0xaa) ) {
+            SET_EBDA(ata.devices[device].type,ATA_TYPE_UNKNOWN);
+
+            // reset the channel
+            ata_reset(device);
+
+            // check for ATA or ATAPI
+            outb(slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0, iobase1+ATA_CB_DH);
+            sc = inb(iobase1+ATA_CB_SC);
+            sn = inb(iobase1+ATA_CB_SN);
+            if ((sc==0x01) && (sn==0x01)) {
+                cl = inb(iobase1+ATA_CB_CL);
+                ch = inb(iobase1+ATA_CB_CH);
+                st = inb(iobase1+ATA_CB_STAT);
+
+                if ((cl==0x14) && (ch==0xeb)) {
+                    SET_EBDA(ata.devices[device].type,ATA_TYPE_ATAPI);
+                } else if ((cl==0x00) && (ch==0x00) && (st!=0x00)) {
+                    SET_EBDA(ata.devices[device].type,ATA_TYPE_ATA);
+                } else if ((cl==0xff) && (ch==0xff)) {
+                    SET_EBDA(ata.devices[device].type,ATA_TYPE_NONE);
+                }
+            }
+        }
+
+        type=GET_EBDA(ata.devices[device].type);
+
+        // Now we send a IDENTIFY command to ATA device
+        if(type == ATA_TYPE_ATA) {
+            u32 sectors;
+            u16 cylinders, heads, spt, blksize;
+            u8  translation, removable, mode;
+
+            //Temporary values to do the transfer
+            SET_EBDA(ata.devices[device].device,ATA_DEVICE_HD);
+            SET_EBDA(ata.devices[device].mode, ATA_MODE_PIO16);
+
+            u16 ret = ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE
+                                      , 1, 0, 0, 0, 0L
+                                      , GET_SEG(SS), (u32)buffer);
+            if (ret !=0)
+                BX_PANIC("ata-detect: Failed to detect ATA device\n");
+
+            removable = (buffer[0] & 0x80) ? 1 : 0;
+            mode      = buffer[96] ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
+            blksize   = *(u16*)&buffer[10];
+
+            cylinders = *(u16*)&buffer[1*2]; // word 1
+            heads     = *(u16*)&buffer[3*2]; // word 3
+            spt       = *(u16*)&buffer[6*2]; // word 6
+
+            sectors   = *(u32*)&buffer[60*2]; // word 60 and word 61
+
+            SET_EBDA(ata.devices[device].device,ATA_DEVICE_HD);
+            SET_EBDA(ata.devices[device].removable, removable);
+            SET_EBDA(ata.devices[device].mode, mode);
+            SET_EBDA(ata.devices[device].blksize, blksize);
+            SET_EBDA(ata.devices[device].pchs.heads, heads);
+            SET_EBDA(ata.devices[device].pchs.cylinders, cylinders);
+            SET_EBDA(ata.devices[device].pchs.spt, spt);
+            SET_EBDA(ata.devices[device].sectors, sectors);
+            BX_INFO("ata%d-%d: PCHS=%u/%d/%d translation=", channel, slave,cylinders, heads, spt);
+
+            translation = inb_cmos(0x39 + channel/2);
+            for (shift=device%4; shift>0; shift--)
+                translation >>= 2;
+            translation &= 0x03;
+
+            SET_EBDA(ata.devices[device].translation, translation);
+
+            switch (translation) {
+            case ATA_TRANSLATION_NONE:
+                BX_INFO("none");
+                break;
+            case ATA_TRANSLATION_LBA:
+                BX_INFO("lba");
+                break;
+            case ATA_TRANSLATION_LARGE:
+                BX_INFO("large");
+                break;
+            case ATA_TRANSLATION_RECHS:
+                BX_INFO("r-echs");
+                break;
+            }
+            switch (translation) {
+            case ATA_TRANSLATION_NONE:
+                break;
+            case ATA_TRANSLATION_LBA:
+                spt = 63;
+                sectors /= 63;
+                heads = sectors / 1024;
+                if (heads>128) heads = 255;
+                else if (heads>64) heads = 128;
+                else if (heads>32) heads = 64;
+                else if (heads>16) heads = 32;
+                else heads=16;
+                cylinders = sectors / heads;
+                break;
+            case ATA_TRANSLATION_RECHS:
+                // Take care not to overflow
+                if (heads==16) {
+                    if(cylinders>61439) cylinders=61439;
+                    heads=15;
+                    cylinders = (u16)((u32)(cylinders)*16/15);
+                }
+                // then go through the large bitshift process
+            case ATA_TRANSLATION_LARGE:
+                while(cylinders > 1024) {
+                    cylinders >>= 1;
+                    heads <<= 1;
+
+                    // If we max out the head count
+                    if (heads > 127) break;
+                }
+                break;
+            }
+            // clip to 1024 cylinders in lchs
+            if (cylinders > 1024) cylinders=1024;
+            BX_INFO(" LCHS=%d/%d/%d\n", cylinders, heads, spt);
+
+            SET_EBDA(ata.devices[device].lchs.heads, heads);
+            SET_EBDA(ata.devices[device].lchs.cylinders, cylinders);
+            SET_EBDA(ata.devices[device].lchs.spt, spt);
+
+            // fill hdidmap
+            SET_EBDA(ata.hdidmap[hdcount], device);
+            hdcount++;
+        }
+
+        // Now we send a IDENTIFY command to ATAPI device
+        if(type == ATA_TYPE_ATAPI) {
+
+            u8  type, removable, mode;
+            u16 blksize;
+
+            //Temporary values to do the transfer
+            SET_EBDA(ata.devices[device].device,ATA_DEVICE_CDROM);
+            SET_EBDA(ata.devices[device].mode, ATA_MODE_PIO16);
+
+            u16 ret = ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE_PACKET
+                                      , 1, 0, 0, 0, 0L
+                                      , GET_SEG(SS), (u32)buffer);
+            if (ret != 0)
+                BX_PANIC("ata-detect: Failed to detect ATAPI device\n");
+
+            type      = buffer[1] & 0x1f;
+            removable = (buffer[0] & 0x80) ? 1 : 0;
+            mode      = buffer[96] ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
+            blksize   = 2048;
+
+            SET_EBDA(ata.devices[device].device, type);
+            SET_EBDA(ata.devices[device].removable, removable);
+            SET_EBDA(ata.devices[device].mode, mode);
+            SET_EBDA(ata.devices[device].blksize, blksize);
+
+            // fill cdidmap
+            SET_EBDA(ata.cdidmap[cdcount], device);
+            cdcount++;
+        }
+
+        u32 sizeinmb = 0;
+        u16 ataversion;
+        u8  c, i, version=0, model[41];
+
+        switch (type) {
+        case ATA_TYPE_ATA:
+            sizeinmb = GET_EBDA(ata.devices[device].sectors);
+            sizeinmb >>= 11;
+        case ATA_TYPE_ATAPI:
+            // Read ATA/ATAPI version
+            ataversion=((u16)(buffer[161])<<8) | buffer[160];
+            for(version=15;version>0;version--) {
+                if ((ataversion&(1<<version))!=0)
+                    break;
+            }
+
+            // Read model name
+            for (i=0;i<20;i++) {
+                model[i*2] = buffer[(i*2)+54+1];
+                model[(i*2)+1] = buffer[(i*2)+54];
+            }
+
+            // Reformat
+            model[40] = 0x00;
+            for (i=39;i>0;i--) {
+                if (model[i]==0x20)
+                    model[i] = 0x00;
+                else
+                    break;
+            }
+            break;
+        }
+
+        switch (type) {
+        case ATA_TYPE_ATA:
+            printf("ata%d %s: ",channel,slave?" slave":"master");
+            i=0;
+            while ((c=model[i++]))
+                printf("%c",c);
+            if (sizeinmb < (1UL<<16))
+                printf(" ATA-%d Hard-Disk (%4u MBytes)\n", version, (u16)sizeinmb);
+            else
+                printf(" ATA-%d Hard-Disk (%4u GBytes)\n", version, (u16)(sizeinmb>>10));
+            break;
+        case ATA_TYPE_ATAPI:
+            printf("ata%d %s: ",channel,slave?" slave":"master");
+            i=0;
+            while ((c=model[i++]))
+                printf("%c",c);
+            if (GET_EBDA(ata.devices[device].device)==ATA_DEVICE_CDROM)
+                printf(" ATAPI-%d CD-Rom/DVD-Rom\n",version);
+            else
+                printf(" ATAPI-%d Device\n",version);
+            break;
+        case ATA_TYPE_UNKNOWN:
+            printf("ata%d %s: Unknown device\n",channel,slave?" slave":"master");
+            break;
+        }
+    }
+
+    // Store the devices counts
+    SET_EBDA(ata.hdcount, hdcount);
+    SET_EBDA(ata.cdcount, cdcount);
+    SET_BDA(disk_count, hdcount);
+
+    printf("\n");
+
+    // FIXME : should use bios=cmos|auto|disable bits
+    // FIXME : should know about translation bits
+    // FIXME : move hard_drive_post here
+
+}
+
+
+static inline void
+disk_ret(struct bregs *regs, u8 code)
+{
+    regs->ah = code;
+    SET_BDA(disk_last_status, code);
+    set_cf(regs, code);
+}
+
+// disk controller reset
+static void
+disk_1300(struct bregs *regs, u8 device)
+{
+    ata_reset(device);
+}
+
+// read disk status
+static void
+disk_1301(struct bregs *regs, u8 device)
+{
+    regs->ah = GET_BDA(disk_last_status);
+    disk_ret(regs, DISK_RET_SUCCESS);
+}
+
+static int
+check_params(struct bregs *regs, u8 device)
+{
+    debug_stub(regs);
+
+    u16 count       = regs->al;
+    u16 cylinder    = regs->ch | ((((u16) regs->cl) << 2) & 0x300);
+    u16 sector      = regs->cl & 0x3f;
+    u16 head        = regs->dh;
+
+    if ((count > 128) || (count == 0) || (sector == 0)) {
+        BX_INFO("int13_harddisk: function %02x, parameter out of range!\n"
+                , regs->ah);
+        disk_ret(regs, DISK_RET_EPARAM);
+        return -1;
+    }
+
+    u16 nlc   = GET_EBDA(ata.devices[device].lchs.cylinders);
+    u16 nlh   = GET_EBDA(ata.devices[device].lchs.heads);
+    u16 nlspt = GET_EBDA(ata.devices[device].lchs.spt);
+
+    bprintf(0, "dev=%d c=%d h=%d s=%d nc=%d nh=%d ns=%d\n"
+            , device, cylinder, head, sector
+            , nlc, nlh, nlspt);
+
+    // sanity check on cyl heads, sec
+    if ( (cylinder >= nlc) || (head >= nlh) || (sector > nlspt )) {
+        BX_INFO("int13_harddisk: function %02x, parameters out of"
+                " range %04x/%04x/%04x!\n"
+                , regs->ah, cylinder, head, sector);
+        disk_ret(regs, DISK_RET_EPARAM);
+        return -1;
+    }
+    return 0;
+}
+
+static void
+disk_1302(struct bregs *regs, u8 device)
+{
+    debug_stub(regs);
+    int ret = check_params(regs, device);
+    if (ret)
+        return;
+    u16 count       = regs->al;
+    u16 cylinder    = regs->ch | ((((u16) regs->cl) << 2) & 0x300);
+    u16 sector      = regs->cl & 0x3f;
+    u16 head        = regs->dh;
+    u16 nph   = GET_EBDA(ata.devices[device].pchs.heads);
+    u16 npspt = GET_EBDA(ata.devices[device].pchs.spt);
+
+    u16 nlh   = GET_EBDA(ata.devices[device].lchs.heads);
+    u16 nlspt = GET_EBDA(ata.devices[device].lchs.spt);
+
+    u32 lba = 0;
+    // if needed, translate lchs to lba, and execute command
+    if ( (nph != nlh) || (npspt != nlspt)) {
+        lba = (((((u32)cylinder * (u32)nlh) + (u32)head) * (u32)nlspt)
+               + (u32)sector - 1);
+        sector = 0; // this forces the command to be lba
+    }
+
+    u16 segment = regs->es;
+    u16 offset  = regs->bx;
+
+    u8 status = ata_cmd_data_in(device, ATA_CMD_READ_SECTORS
+                                , count, cylinder, head, sector
+                                , lba, segment, offset);
+
+    // Set nb of sector transferred
+    regs->al = GET_EBDA(ata.trsfsectors);
+
+    if (status != 0) {
+        BX_INFO("int13_harddisk: function %02x, error %02x !\n",regs->ah,status);
+        disk_ret(regs, DISK_RET_EBADTRACK);
+    }
+    disk_ret(regs, DISK_RET_SUCCESS);
+}
+
+static void
+disk_1303(struct bregs *regs, u8 device)
+{
+    debug_stub(regs);
+    int ret = check_params(regs, device);
+    if (ret)
+        return;
+    u16 count       = regs->al;
+    u16 cylinder    = regs->ch | ((((u16) regs->cl) << 2) & 0x300);
+    u16 sector      = regs->cl & 0x3f;
+    u16 head        = regs->dh;
+    u16 nph   = GET_EBDA(ata.devices[device].pchs.heads);
+    u16 npspt = GET_EBDA(ata.devices[device].pchs.spt);
+
+    u16 nlh   = GET_EBDA(ata.devices[device].lchs.heads);
+    u16 nlspt = GET_EBDA(ata.devices[device].lchs.spt);
+
+    u32 lba = 0;
+    // if needed, translate lchs to lba, and execute command
+    if ( (nph != nlh) || (npspt != nlspt)) {
+        lba = (((((u32)cylinder * (u32)nlh) + (u32)head) * (u32)nlspt)
+               + (u32)sector - 1);
+        sector = 0; // this forces the command to be lba
+    }
+
+    u16 segment = regs->es;
+    u16 offset  = regs->bx;
+
+    u8 status = ata_cmd_data_out(device, ATA_CMD_READ_SECTORS
+                                 , count, cylinder, head, sector
+                                 , lba, segment, offset);
+
+    // Set nb of sector transferred
+    regs->al = GET_EBDA(ata.trsfsectors);
+
+    if (status != 0) {
+        BX_INFO("int13_harddisk: function %02x, error %02x !\n",regs->ah,status);
+        disk_ret(regs, DISK_RET_EBADTRACK);
+    }
+    disk_ret(regs, DISK_RET_SUCCESS);
+}
+
+static void
+disk_1304(struct bregs *regs, u8 device)
+{
+    int ret = check_params(regs, device);
+    if (ret)
+        return;
+    // FIXME verify
+    disk_ret(regs, DISK_RET_SUCCESS);
+}
+
+#define DISK_STUB(regs) do {                    \
+        struct bregs *__regs = (regs);          \
+        debug_stub(__regs);                     \
+        disk_ret(__regs, DISK_RET_SUCCESS);     \
+    } while (0)
+
+// format disk track
+static void
+disk_1305(struct bregs *regs, u8 device)
+{
+    DISK_STUB(regs);
+}
+
+// read disk drive parameters
+static void
+disk_1308(struct bregs *regs, u8 device)
+{
+    // Get logical geometry from table
+    u16 nlc = GET_EBDA(ata.devices[device].lchs.cylinders);
+    u16 nlh = GET_EBDA(ata.devices[device].lchs.heads);
+    u16 nlspt = GET_EBDA(ata.devices[device].lchs.spt);
+    u16 count = GET_EBDA(ata.hdcount);
+
+    nlc = nlc - 2; /* 0 based , last sector not used */
+    regs->al = 0;
+    regs->ch = nlc & 0xff;
+    regs->cl = ((nlc >> 2) & 0xc0) | (nlspt & 0x3f);
+    regs->dh = nlh - 1;
+    regs->dl = count; /* FIXME returns 0, 1, or n hard drives */
+
+    // FIXME should set ES & DI
+    disk_ret(regs, DISK_RET_SUCCESS);
+
+    debug_stub(regs);
+}
+
+// check drive ready
+static void
+disk_1310(struct bregs *regs, u8 device)
+{
+    // should look at 40:8E also???
+
+    // Read the status from controller
+    u8 status = inb(GET_EBDA(ata.channels[device/2].iobase1) + ATA_CB_STAT);
+    if ( (status & ( ATA_CB_STAT_BSY | ATA_CB_STAT_RDY )) == ATA_CB_STAT_RDY )
+        disk_ret(regs, DISK_RET_SUCCESS);
+    else
+        disk_ret(regs, DISK_RET_ENOTREADY);
+}
+
+// read disk drive size
+static void
+disk_1315(struct bregs *regs, u8 device)
+{
+    // Get logical geometry from table
+    u16 nlc   = GET_EBDA(ata.devices[device].lchs.cylinders);
+    u16 nlh   = GET_EBDA(ata.devices[device].lchs.heads);
+    u16 nlspt = GET_EBDA(ata.devices[device].lchs.spt);
+
+    // Compute sector count seen by int13
+    u32 lba = (u32)(nlc - 1) * (u32)nlh * (u32)nlspt;
+    regs->cx = lba >> 16;
+    regs->dx = lba & 0xffff;
+
+    disk_ret(regs, 0);
+    regs->ah = 3; // hard disk accessible
+}
+
+static void
+disk_13XX(struct bregs *regs, u8 device)
+{
+    BX_INFO("int13_harddisk: function %02xh unsupported, returns fail\n", regs->ah);
+    disk_ret(regs, DISK_RET_EPARAM);
+}
 
 static void
 disk_13(struct bregs *regs, u8 drive)
 {
-    // XXX
-    set_cf(regs, 1);
+    if (! CONFIG_ATA) {
+        disk_13XX(regs, drive);
+        return;
+    }
+
+    debug_stub(regs);
+
+    // clear completion flag
+    SET_BDA(disk_interrupt_flag, 0);
+
+    // basic check : device has to be defined
+    if (drive < 0x80 || drive >= 0x80 + CONFIG_MAX_ATA_DEVICES) {
+        disk_13XX(regs, drive);
+        return;
+    }
+
+    // Get the ata channel
+    u8 device = GET_EBDA(ata.hdidmap[drive-0x80]);
+
+    // basic check : device has to be valid
+    if (device >= CONFIG_MAX_ATA_DEVICES) {
+        disk_13XX(regs, drive);
+        return;
+    }
+
+    switch (regs->ah) {
+    case 0x00: disk_1300(regs, device); break;
+    case 0x01: disk_1301(regs, device); break;
+    case 0x02: disk_1302(regs, device); break;
+    case 0x03: disk_1303(regs, device); break;
+    case 0x04: disk_1304(regs, device); break;
+    case 0x05: disk_1305(regs, device); break;
+    case 0x08: disk_1308(regs, device); break;
+    case 0x10: disk_1310(regs, device); break;
+    case 0x15: disk_1315(regs, device); break;
+    // XXX - several others defined
+    default:   disk_13XX(regs, device); break;
+    }
 }
 
 static void
index 358347a..34fef1a 100644 (file)
 #define DISK_RET_EPARAM      0x01
 #define DISK_RET_ECHANGED    0x06
 #define DISK_RET_EBOUNDARY   0x09
+#define DISK_RET_EBADTRACK   0x0c
 #define DISK_RET_ECONTROLLER 0x20
 #define DISK_RET_ETIMEOUT    0x80
 #define DISK_RET_EMEDIA      0xC0
+#define DISK_RET_ENOTREADY   0xAA
 
 // floppy.c
 void floppy_13(struct bregs *regs, u8 drive);
 void floppy_tick();
 
+// disk.c
+void ata_detect();
+
 #endif // disk.h
index eed676b..b88f351 100644 (file)
 static inline void outb(u8 value, u16 port) {
     __asm__ __volatile__("outb %b0, %w1" : : "a"(value), "Nd"(port));
 }
+static inline void outw(u16 value, u16 port) {
+    __asm__ __volatile__("outw %w0, %w1" : : "a"(value), "Nd"(port));
+}
+static inline void outl(u32 value, u16 port) {
+    __asm__ __volatile__("outl %0, %w1" : : "a"(value), "Nd"(port));
+}
 static inline u8 inb(u16 port) {
     u8 value;
     __asm__ __volatile__("inb %w1, %b0" : "=a"(value) : "Nd"(port));
     return value;
 }
+static inline u16 inw(u16 port) {
+    u16 value;
+    __asm__ __volatile__("inw %w1, %w0" : "=a"(value) : "Nd"(port));
+    return value;
+}
+static inline u32 inl(u16 port) {
+    u32 value;
+    __asm__ __volatile__("inl %w1, %0" : "=a"(value) : "Nd"(port));
+    return value;
+}
 
 #endif // ioport.h