Make disk access work.
[people/mcb30/legacybios.git] / src / ata.c
index 3818fb6..4e3857a 100644 (file)
--- a/src/ata.c
+++ b/src/ata.c
@@ -2,6 +2,7 @@
 #include "types.h" // u8
 #include "ioport.h" // inb
 #include "util.h" // BX_INFO
+#include "cmos.h" // inb_cmos
 
 #define TIMEOUT 0
 #define BSY 1
@@ -14,6 +15,8 @@
 
 #define BX_DEBUG_ATA BX_INFO
 
+// XXX - lots of redundancy in this file.
+
 static int
 await_ide(u8 when_done, u16 base, u16 timeout)
 {
@@ -116,7 +119,7 @@ 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);
+        SET_FARVAR(segment, *(u16*)(offset + 2*i), d);
     }
 }
 
@@ -126,7 +129,7 @@ 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);
+        SET_FARVAR(segment, *(u32*)(offset + 4*i), d);
     }
 }
 
@@ -135,7 +138,7 @@ 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));
+        u16 d = GET_FARVAR(segment, *(u16*)(offset + 2*i));
         outw(d, port);
     }
 }
@@ -145,7 +148,7 @@ 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));
+        u32 d = GET_FARVAR(segment, *(u32*)(offset + 4*i));
         outl(d, port);
     }
 }
@@ -178,8 +181,10 @@ ata_cmd_data_in(u16 device, u16 command, u16 count, u16 cylinder
     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;
+    if (mode == ATA_MODE_PIO32)
+        blksize>>=2;
+    else
+        blksize>>=1;
 
     // Reset count of transferred data
     SET_EBDA(ata.trsfsectors,0);
@@ -242,9 +247,9 @@ ata_cmd_data_in(u16 device, u16 command, u16 count, u16 cylinder
         }
 
         if (mode == ATA_MODE_PIO32)
-            insw(iobase1, segment, offset, blksize);
-        else
             insl(iobase1, segment, offset, blksize);
+        else
+            insw(iobase1, segment, offset, blksize);
 
         current++;
         SET_EBDA(ata.trsfsectors,current);
@@ -370,9 +375,9 @@ ata_cmd_data_out(u16 device, u16 command, u16 count, u16 cylinder
         }
 
         if (mode == ATA_MODE_PIO32)
-            outsw(iobase1, segment, offset, blksize);
-        else
             outsl(iobase1, segment, offset, blksize);
+        else
+            outsw(iobase1, segment, offset, blksize);
 
         current++;
         SET_EBDA(ata.trsfsectors,current);
@@ -569,8 +574,7 @@ ata_cmd_packet(u16 device, u8 cmdlen, u16 cmdseg, u16 cmdoff, u16 header
 
             if (lmode == ATA_MODE_PIO32) {
                 lcount>>=2; lbefore>>=2; lafter>>=2;
-            }
-            else {
+            } else {
                 lcount>>=1; lbefore>>=1; lafter>>=1;
             }
 
@@ -614,3 +618,306 @@ ata_cmd_packet(u16 device, u8 cmdlen, u16 cmdseg, u16 cmdoff, u16 header
     outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC);
     return 0;
 }
+
+// ---------------------------------------------------------------------------
+// ATA/ATAPI driver : device detection
+// ---------------------------------------------------------------------------
+
+void
+ata_detect()
+{
+    u8  hdcount, cdcount, device, type;
+    u8  buffer[0x0200];
+    memset(buffer, 0, sizeof(buffer));
+
+#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)
+                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(CMOS_BIOS_DISKTRANSFLAG + 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 (%u MBytes)\n", version, (u16)sizeinmb);
+            else
+                printf(" ATA-%d Hard-Disk (%u 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
+
+}