grml...
[seabios.git] / src / block.c
index 2df2cc45bb265f281111dc50cdc5db34d5d7b566..eeebd83b82091881d6ab65d59f90b4f5167c9886 100644 (file)
@@ -9,21 +9,61 @@
 #include "biosvar.h" // GET_GLOBAL
 #include "cmos.h" // inb_cmos
 #include "util.h" // dprintf
+#include "ata.h" // process_ata_op
+#include "ahci.h" // process_ahci_op
+#include "virtio-blk.h" // process_virtio_blk_op
+#include "blockcmd.h" // cdb_*
 
-struct drives_s Drives VAR16_32;
+u8 FloppyCount VAR16VISIBLE;
+u8 CDCount;
+struct drive_s *IDMap[3][CONFIG_MAX_EXTDRIVE] VAR16VISIBLE;
+u8 *bounce_buf_fl VAR16VISIBLE;
 
+struct drive_s *
+getDrive(u8 exttype, u8 extdriveoffset)
+{
+    if (extdriveoffset >= ARRAY_SIZE(IDMap[0]))
+        return NULL;
+    struct drive_s *drive_gf = GET_GLOBAL(IDMap[exttype][extdriveoffset]);
+    if (!drive_gf)
+        return NULL;
+    return GLOBALFLAT2GLOBAL(drive_gf);
+}
+
+int getDriveId(u8 exttype, struct drive_s *drive_g)
+{
+    int i;
+    for (i = 0; i < ARRAY_SIZE(IDMap[0]); i++)
+        if (getDrive(exttype, i) == drive_g)
+            return i;
+    return -1;
+}
+
+int bounce_buf_init(void)
+{
+    if (bounce_buf_fl)
+        return 0;
+
+    u8 *buf = malloc_low(CDROM_SECTOR_SIZE);
+    if (!buf) {
+        warn_noalloc();
+        return -1;
+    }
+    bounce_buf_fl = buf;
+    return 0;
+}
 
 /****************************************************************
  * Disk geometry translation
  ****************************************************************/
 
 static u8
-get_translation(int driveid)
+get_translation(struct drive_s *drive_g)
 {
-    u8 type = GET_GLOBAL(Drives.drives[driveid].type);
+    u8 type = GET_GLOBAL(drive_g->type);
     if (! CONFIG_COREBOOT && type == DTYPE_ATA) {
         // Emulators pass in the translation info via nvram.
-        u8 ataid = GET_GLOBAL(Drives.drives[driveid].cntl_id);
+        u8 ataid = GET_GLOBAL(drive_g->cntl_id);
         u8 channel = ataid / 2;
         u8 translation = inb_cmos(CMOS_BIOS_DISKTRANSFLAG + channel/2);
         translation >>= 2 * (ataid % 4);
@@ -31,10 +71,15 @@ get_translation(int driveid)
         return translation;
     }
 
-    // On COREBOOT, use a heuristic to determine translation type.
-    u16 heads = GET_GLOBAL(Drives.drives[driveid].pchs.heads);
-    u16 cylinders = GET_GLOBAL(Drives.drives[driveid].pchs.cylinders);
-    u16 spt = GET_GLOBAL(Drives.drives[driveid].pchs.spt);
+    // Otherwise use a heuristic to determine translation type.
+    u16 heads = GET_GLOBAL(drive_g->pchs.heads);
+    u16 cylinders = GET_GLOBAL(drive_g->pchs.cylinders);
+    u16 spt = GET_GLOBAL(drive_g->pchs.spt);
+    u64 sectors = GET_GLOBAL(drive_g->sectors);
+    u64 psectors = (u64)heads * cylinders * spt;
+    if (!heads || !cylinders || !spt || psectors > sectors)
+        // pchs doesn't look valid - use LBA.
+        return TRANSLATION_LBA;
 
     if (cylinders <= 1024 && heads <= 16 && spt <= 63)
         return TRANSLATION_NONE;
@@ -43,28 +88,25 @@ get_translation(int driveid)
     return TRANSLATION_LBA;
 }
 
-void
-setup_translation(int driveid)
+static void
+setup_translation(struct drive_s *drive_g)
 {
-    u8 translation = get_translation(driveid);
-    SET_GLOBAL(Drives.drives[driveid].translation, translation);
-
-    u8 ataid = GET_GLOBAL(Drives.drives[driveid].cntl_id);
-    u8 channel = ataid / 2;
-    u8 slave = ataid % 2;
-    u16 heads = GET_GLOBAL(Drives.drives[driveid].pchs.heads);
-    u16 cylinders = GET_GLOBAL(Drives.drives[driveid].pchs.cylinders);
-    u16 spt = GET_GLOBAL(Drives.drives[driveid].pchs.spt);
-    u64 sectors = GET_GLOBAL(Drives.drives[driveid].sectors);
-
-    dprintf(1, "ata%d-%d: PCHS=%u/%d/%d translation="
-            , channel, slave, cylinders, heads, spt);
+    u8 translation = get_translation(drive_g);
+    SET_GLOBAL(drive_g->translation, translation);
+
+    u16 heads = GET_GLOBAL(drive_g->pchs.heads);
+    u16 cylinders = GET_GLOBAL(drive_g->pchs.cylinders);
+    u16 spt = GET_GLOBAL(drive_g->pchs.spt);
+    u64 sectors = GET_GLOBAL(drive_g->sectors);
+    const char *desc = NULL;
+
     switch (translation) {
+    default:
     case TRANSLATION_NONE:
-        dprintf(1, "none");
+        desc = "none";
         break;
     case TRANSLATION_LBA:
-        dprintf(1, "lba");
+        desc = "lba";
         spt = 63;
         if (sectors > 63*255*1024) {
             heads = 255;
@@ -86,7 +128,7 @@ setup_translation(int driveid)
         cylinders = sect / heads;
         break;
     case TRANSLATION_RECHS:
-        dprintf(1, "r-echs");
+        desc = "r-echs";
         // Take care not to overflow
         if (heads==16) {
             if (cylinders>61439)
@@ -97,7 +139,7 @@ setup_translation(int driveid)
         // then go through the large bitshift process
     case TRANSLATION_LARGE:
         if (translation == TRANSLATION_LARGE)
-            dprintf(1, "large");
+            desc = "large";
         while (cylinders > 1024) {
             cylinders >>= 1;
             heads <<= 1;
@@ -111,11 +153,16 @@ setup_translation(int driveid)
     // clip to 1024 cylinders in lchs
     if (cylinders > 1024)
         cylinders = 1024;
-    dprintf(1, " LCHS=%d/%d/%d\n", cylinders, heads, spt);
+    dprintf(1, "drive %p: PCHS=%u/%d/%d translation=%s LCHS=%d/%d/%d s=%d\n"
+            , drive_g
+            , drive_g->pchs.cylinders, drive_g->pchs.heads, drive_g->pchs.spt
+            , desc
+            , cylinders, heads, spt
+            , (u32)sectors);
 
-    SET_GLOBAL(Drives.drives[driveid].lchs.heads, heads);
-    SET_GLOBAL(Drives.drives[driveid].lchs.cylinders, cylinders);
-    SET_GLOBAL(Drives.drives[driveid].lchs.spt, spt);
+    SET_GLOBAL(drive_g->lchs.heads, heads);
+    SET_GLOBAL(drive_g->lchs.cylinders, cylinders);
+    SET_GLOBAL(drive_g->lchs.spt, spt);
 }
 
 
@@ -125,20 +172,20 @@ setup_translation(int driveid)
 
 // Fill in Fixed Disk Parameter Table (located in ebda).
 static void
-fill_fdpt(int driveid)
+fill_fdpt(struct drive_s *drive_g, int hdid)
 {
-    if (driveid > 1)
+    if (hdid > 1)
         return;
 
-    u16 nlc   = GET_GLOBAL(Drives.drives[driveid].lchs.cylinders);
-    u16 nlh   = GET_GLOBAL(Drives.drives[driveid].lchs.heads);
-    u16 nlspt = GET_GLOBAL(Drives.drives[driveid].lchs.spt);
+    u16 nlc   = GET_GLOBAL(drive_g->lchs.cylinders);
+    u16 nlh   = GET_GLOBAL(drive_g->lchs.heads);
+    u16 nlspt = GET_GLOBAL(drive_g->lchs.spt);
 
-    u16 npc   = GET_GLOBAL(Drives.drives[driveid].pchs.cylinders);
-    u16 nph   = GET_GLOBAL(Drives.drives[driveid].pchs.heads);
-    u16 npspt = GET_GLOBAL(Drives.drives[driveid].pchs.spt);
+    u16 npc   = GET_GLOBAL(drive_g->pchs.cylinders);
+    u16 nph   = GET_GLOBAL(drive_g->pchs.heads);
+    u16 npspt = GET_GLOBAL(drive_g->pchs.spt);
 
-    struct fdpt_s *fdpt = &get_ebda_ptr()->fdpt[driveid];
+    struct fdpt_s *fdpt = &get_ebda_ptr()->fdpt[hdid];
     fdpt->precompensation = 0xffff;
     fdpt->drive_control_byte = 0xc0 | ((nph > 8) << 3);
     fdpt->landing_zone = npc;
@@ -146,66 +193,168 @@ fill_fdpt(int driveid)
     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;
+    if (nlc != npc || nlh != nph || nlspt != npspt) {
+        // Logical mapping present - use extended structure.
 
-    // 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;
+        // 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.
-    fdpt->checksum -= checksum(fdpt, sizeof(*fdpt));
+        // Checksum structure.
+        fdpt->checksum -= checksum(fdpt, sizeof(*fdpt));
+    }
 
-    if (driveid == 0)
-        SET_IVT(0x41, get_ebda_seg()
-                , offsetof(struct extended_bios_data_area_s, fdpt[0]));
+    if (hdid == 0)
+        SET_IVT(0x41, SEGOFF(get_ebda_seg(), offsetof(
+                                 struct extended_bios_data_area_s, fdpt[0])));
     else
-        SET_IVT(0x46, get_ebda_seg()
-                , offsetof(struct extended_bios_data_area_s, fdpt[1]));
+        SET_IVT(0x46, SEGOFF(get_ebda_seg(), offsetof(
+                                 struct extended_bios_data_area_s, fdpt[1])));
 }
 
-// Map a drive (that was registered via add_bcv_hd)
-void
-map_hd_drive(int driveid)
+// Find spot to add a drive
+static void
+add_drive(struct drive_s **idmap, u8 *count, struct drive_s *drive_g)
 {
-    // fill hdidmap
-    u8 hdcount = GET_BDA(hdcount);
-    if (hdcount >= ARRAY_SIZE(Drives.idmap[0]))
+    if (*count >= ARRAY_SIZE(IDMap[0])) {
+        warn_noalloc();
         return;
-    dprintf(3, "Mapping hd driveid %d to %d\n", driveid, hdcount);
-    SET_GLOBAL(Drives.idmap[0][hdcount], driveid);
-    SET_BDA(hdcount, hdcount + 1);
+    }
+    idmap[*count] = drive_g;
+    *count = *count + 1;
+}
+
+// Map a hard drive
+void
+map_hd_drive(struct drive_s *drive_g)
+{
+    ASSERT32FLAT();
+    struct bios_data_area_s *bda = MAKE_FLATPTR(SEG_BDA, 0);
+    int hdid = bda->hdcount;
+    dprintf(3, "Mapping hd drive %p to %d\n", drive_g, hdid);
+    add_drive(IDMap[EXTTYPE_HD], &bda->hdcount, drive_g);
+
+    // Setup disk geometry translation.
+    setup_translation(drive_g);
 
     // Fill "fdpt" structure.
-    fill_fdpt(hdcount);
+    fill_fdpt(drive_g, hdid);
 }
 
 // Map a cd
 void
-map_cd_drive(int driveid)
+map_cd_drive(struct drive_s *drive_g)
 {
-    // fill cdidmap
-    u8 cdcount = GET_GLOBAL(Drives.cdcount);
-    if (cdcount >= ARRAY_SIZE(Drives.idmap[1]))
-        return;
-    dprintf(3, "Mapping cd driveid %d to %d\n", driveid, cdcount);
-    SET_GLOBAL(Drives.idmap[1][cdcount], driveid);
-    SET_GLOBAL(Drives.cdcount, cdcount+1);
+    dprintf(3, "Mapping cd drive %p\n", drive_g);
+    add_drive(IDMap[EXTTYPE_CD], &CDCount, drive_g);
+}
+
+// Map a floppy
+void
+map_floppy_drive(struct drive_s *drive_g)
+{
+    dprintf(3, "Mapping floppy drive %p\n", drive_g);
+    add_drive(IDMap[EXTTYPE_FLOPPY], &FloppyCount, drive_g);
+
+    // Update equipment word bits for floppy
+    if (FloppyCount == 1) {
+        // 1 drive, ready for boot
+        SETBITS_BDA(equipment_list_flags, 0x01);
+        SET_BDA(floppy_harddisk_info, 0x07);
+    } else if (FloppyCount >= 2) {
+        // 2 drives, ready for boot
+        SETBITS_BDA(equipment_list_flags, 0x41);
+        SET_BDA(floppy_harddisk_info, 0x77);
+    }
 }
 
 
 /****************************************************************
- * Setup
+ * 16bit calling interface
  ****************************************************************/
 
-void
-drive_setup()
+int
+process_scsi_op(struct disk_op_s *op)
+{
+    if (!CONFIG_USB_MSC)
+        return 0;
+    switch (op->command) {
+    case CMD_READ:
+        return cdb_read(op);
+    case CMD_WRITE:
+        return cdb_write(op);
+    case CMD_FORMAT:
+    case CMD_RESET:
+    case CMD_ISREADY:
+    case CMD_VERIFY:
+    case CMD_SEEK:
+        return DISK_RET_SUCCESS;
+    default:
+        op->count = 0;
+        return DISK_RET_EPARAM;
+    }
+}
+
+// Execute a disk_op request.
+int
+process_op(struct disk_op_s *op)
 {
-    memset(&Drives, 0, sizeof(Drives));
-    memset(&Drives.idmap, 0xff, sizeof(Drives.idmap));
+    ASSERT16();
+    u8 type = GET_GLOBAL(op->drive_g->type);
+    switch (type) {
+    case DTYPE_FLOPPY:
+        return process_floppy_op(op);
+    case DTYPE_ATA:
+        return process_ata_op(op);
+    case DTYPE_ATAPI:
+        return process_atapi_op(op);
+    case DTYPE_RAMDISK:
+        return process_ramdisk_op(op);
+    case DTYPE_CDEMU:
+        return process_cdemu_op(op);
+    case DTYPE_VIRTIO_BLK:
+        return process_virtio_blk_op(op);
+    case DTYPE_AHCI:
+       return process_ahci_op(op);
+    case DTYPE_USB:
+        return process_scsi_op(op);
+    default:
+        op->count = 0;
+        return DISK_RET_EPARAM;
+    }
+}
+
+// Execute a "disk_op_s" request - this runs on a stack in the ebda.
+static int
+__send_disk_op(struct disk_op_s *op_far, u16 op_seg)
+{
+    struct disk_op_s dop;
+    memcpy_far(GET_SEG(SS), &dop
+               , op_seg, op_far
+               , sizeof(dop));
+
+    dprintf(DEBUG_HDL_13, "disk_op d=%p lba=%d buf=%p count=%d cmd=%d\n"
+            , dop.drive_g, (u32)dop.lba, dop.buf_fl
+            , dop.count, dop.command);
+
+    int status = process_op(&dop);
+
+    // Update count with total sectors transferred.
+    SET_FARVAR(op_seg, op_far->count, dop.count);
+
+    return status;
+}
+
+// Execute a "disk_op_s" request by jumping to a stack in the ebda.
+int
+send_disk_op(struct disk_op_s *op)
+{
+    ASSERT16();
+    if (! CONFIG_DRIVES)
+        return -1;
+
+    return stack_hop((u32)op, GET_SEG(SS), __send_disk_op);
 }