Add full support for drives with more that 2<<32 sectors.
authorKevin O'Connor <kevin@koconnor.net>
Wed, 14 May 2008 04:43:13 +0000 (00:43 -0400)
committerKevin O'Connor <kevin@koconnor.net>
Wed, 14 May 2008 04:43:13 +0000 (00:43 -0400)
Use 64bit integers for sector and lba values.

src/ata.c
src/ata.h
src/biosvar.h
src/disk.c
src/disk.h
src/farptr.h
src/types.h

index 0b48dcddc1affb9f72931e4608c3401ac2d4efa3..e8a4a287df3d265673ebbf71763fd25c5fa7e305 100644 (file)
--- a/src/ata.c
+++ b/src/ata.c
@@ -147,7 +147,7 @@ ata_reset(int driveid)
  ****************************************************************/
 
 struct ata_op_s {
-    u32 lba;
+    u64 lba;
     void *far_buffer;
     u16 driveid;
     u16 count;
@@ -358,7 +358,7 @@ static noinline int
 send_cmd_disk(const struct ata_op_s *op, u16 command)
 {
     u8 slave = op->driveid % 2;
-    u32 lba = op->lba;
+    u64 lba = op->lba;
 
     struct ata_pio_command cmd;
     memset(&cmd, 0, sizeof(cmd));
@@ -367,8 +367,8 @@ send_cmd_disk(const struct ata_op_s *op, u16 command)
     if (op->count >= (1<<8) || lba + op->count >= (1<<28)) {
         cmd.sector_count2 = op->count >> 8;
         cmd.lba_low2 = lba >> 24;
-        cmd.lba_mid2 = 0;
-        cmd.lba_high2 = 0;
+        cmd.lba_mid2 = lba >> 32;
+        cmd.lba_high2 = lba >> 40;
 
         cmd.command |= 0x04;
         lba &= 0xffffff;
@@ -387,7 +387,7 @@ send_cmd_disk(const struct ata_op_s *op, u16 command)
 
 // Read/write count blocks from a harddrive.
 __always_inline int
-ata_cmd_data(int driveid, u16 command, u32 lba, u16 count, void *far_buffer)
+ata_cmd_data(int driveid, u16 command, u64 lba, u16 count, void *far_buffer)
 {
     struct ata_op_s op;
     op.driveid = driveid;
@@ -627,7 +627,11 @@ init_drive_ata(int driveid)
     u16 heads     = *(u16*)&buffer[3*2]; // word 3
     u16 spt       = *(u16*)&buffer[6*2]; // word 6
 
-    u32 sectors   = *(u32*)&buffer[60*2]; // word 60 and word 61
+    u64 sectors;
+    if (*(u16*)&buffer[83*2] & (1 << 10)) // word 83 - lba48 support
+        sectors  = *(u64*)&buffer[100*2]; // 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);
@@ -657,8 +661,13 @@ init_drive_ata(int driveid)
     case ATA_TRANSLATION_LBA:
         BX_INFO("lba");
         spt = 63;
-        sectors /= 63;
-        heads = sectors / 1024;
+        if (sectors > 63*255*1024) {
+            heads = 255;
+            cylinders = 1024;
+            break;
+        }
+        u32 sect = (u32)sectors / 63;
+        heads = sect / 1024;
         if (heads>128)
             heads = 255;
         else if (heads>64)
@@ -669,7 +678,7 @@ init_drive_ata(int driveid)
             heads = 32;
         else
             heads = 16;
-        cylinders = sectors / heads;
+        cylinders = sect / heads;
         break;
     case ATA_TRANSLATION_RECHS:
         BX_INFO("r-echs");
@@ -708,15 +717,16 @@ init_drive_ata(int driveid)
     SET_EBDA(ata.idmap[0][hdcount], driveid);
     SET_EBDA(ata.hdcount, ++hdcount);
 
-    u32 sizeinmb = GET_EBDA(ata.devices[driveid].sectors);
+    u64 sizeinmb = GET_EBDA(ata.devices[driveid].sectors);
     sizeinmb >>= 11;
 
     report_model(driveid, buffer);
     u8 version = get_ata_version(buffer);
     if (sizeinmb < (1 << 16))
-        printf(" ATA-%d Hard-Disk (%u MBytes)\n", version, sizeinmb);
+        printf(" ATA-%d Hard-Disk (%u MBytes)\n", version, (u32)sizeinmb);
     else
-        printf(" ATA-%d Hard-Disk (%u GBytes)\n", version, sizeinmb >> 10);
+        printf(" ATA-%d Hard-Disk (%u GBytes)\n", version
+               , (u32)(sizeinmb >> 10));
 }
 
 static void
index a778120bcc8fd878d1d35714c4ba0e9b35a8618d..59b01d3093ae31c3a5770cb60d255a67ec21c818 100644 (file)
--- a/src/ata.h
+++ b/src/ata.h
@@ -13,7 +13,7 @@
 
 // Function definitions
 void ata_reset(int driveid);
-int ata_cmd_data(int driveid, u16 command, u32 lba, u16 count, void *far_buffer);
+int ata_cmd_data(int driveid, u16 command, u64 lba, u16 count, void *far_buffer);
 int ata_cmd_packet(int driveid, u8 *cmdbuf, u8 cmdlen
                    , u32 length, void *far_buffer);
 int cdrom_read(int driveid, u32 lba, u32 count, void *far_buffer);
index 9528523da5127801c97c2cc717efdf076a1623d5..8f694e4b4851114fcae4d3c58421b1f438607709 100644 (file)
@@ -160,7 +160,7 @@ struct ata_device_s {
     struct chs_s  lchs;         // Logical CHS
     struct chs_s  pchs;         // Physical CHS
 
-    u32 sectors;      // Total sectors count
+    u64 sectors;      // Total sectors count
 };
 
 struct ata_s {
index 722852927b4a975774acd915709ce2133c60ae43..152b811eb47f09a6db5988262301d06929509641 100644 (file)
@@ -115,21 +115,9 @@ basic_access(struct bregs *regs, u8 device, u16 command)
 static void
 extended_access(struct bregs *regs, u8 device, u16 command)
 {
-    u16 count = GET_INT13EXT(regs, count);
-
-    // Can't use 64 bits lba
-    u32 lba = GET_INT13EXT(regs, lba2);
-    if (lba != 0L) {
-        BX_PANIC("int13_harddisk: function %02x. Can't use 64bits lba\n"
-                 , regs->ah);
-        disk_ret(regs, DISK_RET_EPARAM);
-        return;
-    }
-
+    // Get lba and check.
+    u64 lba = GET_INT13EXT(regs, lba);
     u8 type = GET_EBDA(ata.devices[device].type);
-
-    // Get 32 bits lba and check
-    lba = GET_INT13EXT(regs, lba1);
     if (type == ATA_TYPE_ATA
         && lba >= GET_EBDA(ata.devices[device].sectors)) {
         BX_INFO("int13_harddisk: function %02x. LBA out of range\n", regs->ah);
@@ -146,6 +134,7 @@ extended_access(struct bregs *regs, u8 device, u16 command)
     u16 segment = GET_INT13EXT(regs, segment);
     u16 offset = GET_INT13EXT(regs, offset);
     void *far_buffer = MAKE_FARPTR(segment, offset);
+    u16 count = GET_INT13EXT(regs, count);
 
     irq_enable();
 
@@ -379,12 +368,12 @@ disk_1348(struct bregs *regs, u8 device)
     u16 npc     = GET_EBDA(ata.devices[device].pchs.cylinders);
     u16 nph     = GET_EBDA(ata.devices[device].pchs.heads);
     u16 npspt   = GET_EBDA(ata.devices[device].pchs.spt);
-    u32 lba     = GET_EBDA(ata.devices[device].sectors);
+    u64 lba     = GET_EBDA(ata.devices[device].sectors);
     u16 blksize = GET_EBDA(ata.devices[device].blksize);
 
     SET_INT13DPT(regs, size, 0x1a);
     if (type == ATA_TYPE_ATA) {
-        if ((lba/npspt)/nph > 0x3fff) {
+        if (lba > (u64)npspt*nph*0x3fff) {
             SET_INT13DPT(regs, infos, 0x00); // geometry is invalid
             SET_INT13DPT(regs, cylinders, 0x3fff);
         } else {
@@ -393,8 +382,7 @@ disk_1348(struct bregs *regs, u8 device)
         }
         SET_INT13DPT(regs, heads, (u32)nph);
         SET_INT13DPT(regs, spt, (u32)npspt);
-        SET_INT13DPT(regs, sector_count1, lba);  // FIXME should be Bit64
-        SET_INT13DPT(regs, sector_count2, 0L);
+        SET_INT13DPT(regs, sector_count, lba);
     } else {
         // ATAPI
         // 0x74 = removable, media change, lockable, max values
@@ -402,8 +390,7 @@ disk_1348(struct bregs *regs, u8 device)
         SET_INT13DPT(regs, cylinders, 0xffffffff);
         SET_INT13DPT(regs, heads, 0xffffffff);
         SET_INT13DPT(regs, spt, 0xffffffff);
-        SET_INT13DPT(regs, sector_count1, 0xffffffff);  // FIXME should be Bit64
-        SET_INT13DPT(regs, sector_count2, 0xffffffff);
+        SET_INT13DPT(regs, sector_count, (u64)-1);
     }
     SET_INT13DPT(regs, blksize, blksize);
 
index 30ef0bb3e4ff4a60a3d076385c224ada5421f6cc..e70d3a70d150b64e950f7e544d41f3de9d2b1b74 100644 (file)
@@ -33,8 +33,7 @@ struct int13ext_s {
     u16 count;
     u16 offset;
     u16 segment;
-    u32 lba1;
-    u32 lba2;
+    u64 lba;
 } PACKED;
 
 #define GET_INT13EXT(regs,var)                                          \
@@ -49,8 +48,7 @@ struct int13dpt_s {
     u32 cylinders;
     u32 heads;
     u32 spt;
-    u32 sector_count1;
-    u32 sector_count2;
+    u64 sector_count;
     u16 blksize;
     u16 dpte_offset;
     u16 dpte_segment;
index 56219d290134678c1e955aeab87e4842c6921add..ef0aa423e69bfe26c3720abec2c24c67b13b61e1 100644 (file)
@@ -28,6 +28,12 @@ extern u16 __segment_ES, __segment_CS, __segment_DS, __segment_SS;
     __asm__("movl %%" #SEG ":%1, %0" : "=ri"(__value)   \
             : "m"(var), "m"(__segment_ ## SEG));        \
     __value; })
+#define READ64_SEG(SEG, var) ({                                 \
+    union u64_u32_u __value;                                    \
+    union u64_u32_u *__r64_ptr = (union u64_u32_u *)&(var);     \
+    __value.hi = READ32_SEG(SEG, __r64_ptr->hi);                \
+    __value.lo = READ32_SEG(SEG, __r64_ptr->lo);                \
+    __value.val; })
 #define WRITE8_SEG(SEG, var, value)                             \
     __asm__("movb %b1, %%" #SEG ":%0" : "=m"(var)               \
             : "Q"(value), "m"(__segment_ ## SEG))
@@ -37,6 +43,13 @@ extern u16 __segment_ES, __segment_CS, __segment_DS, __segment_SS;
 #define WRITE32_SEG(SEG, var, value)                            \
     __asm__("movl %1, %%" #SEG ":%0" : "=m"(var)                \
             : "r"(value), "m"(__segment_ ## SEG))
+#define WRITE64_SEG(SEG, var, value) do {                       \
+        union u64_u32_u __value;                                \
+        union u64_u32_u *__w64_ptr = (union u64_u32_u *)&(var); \
+        __value.val = (value);                                  \
+        WRITE32_SEG(SEG, __w64_ptr->hi, __value.hi);            \
+        WRITE32_SEG(SEG, __w64_ptr->lo, __value.lo);            \
+    } while (0)
 
 // Low level macros for getting/setting a segment register.
 #define __SET_SEG(SEG, value)                                   \
@@ -63,6 +76,9 @@ extern void __force_link_error__unknown_type();
     else if (__builtin_types_compatible_p(typeof(__val), u32)           \
              || __builtin_types_compatible_p(typeof(__val), s32))       \
         __val = READ32_SEG(seg, var);                                   \
+    else if (__builtin_types_compatible_p(typeof(__val), u64)           \
+             || __builtin_types_compatible_p(typeof(__val), s64))       \
+        __val = READ64_SEG(seg, var);                                   \
     else                                                                \
         __force_link_error__unknown_type();                             \
     __val; })
@@ -77,6 +93,9 @@ extern void __force_link_error__unknown_type();
         else if (__builtin_types_compatible_p(typeof(var), u32)         \
                  || __builtin_types_compatible_p(typeof(var), s32))     \
             WRITE32_SEG(seg, var, (val));                               \
+        else if (__builtin_types_compatible_p(typeof(var), u64)         \
+                 || __builtin_types_compatible_p(typeof(var), s64))     \
+            WRITE64_SEG(seg, var, (val));                               \
         else                                                            \
             __force_link_error__unknown_type();                         \
     } while (0)
index eb130114735cfd6a99be0fc77de0259beaafee64..d356451c182a635dd8b89a3f333a6f66f056a56b 100644 (file)
@@ -16,6 +16,11 @@ typedef unsigned long long u64;
 typedef signed long long s64;
 typedef u32 size_t;
 
+union u64_u32_u {
+    struct { u32 hi, lo; };
+    u64 val;
+};
+
 #define __VISIBLE __attribute__((externally_visible))
 #ifdef MODE16
 // Notes a function as externally visible in the 16bit code chunk.