[syslinux:master] diskio: allow fallback from EDD to CHS

syslinux-bot for H. Peter Anvin hpa at linux.intel.com
Fri Jul 16 16:21:18 PDT 2010


Commit-ID:  22c96d213d282f715043770bf347a99c81299e8f
Gitweb:     http://syslinux.zytor.com/commit/22c96d213d282f715043770bf347a99c81299e8f
Author:     H. Peter Anvin <hpa at linux.intel.com>
AuthorDate: Fri, 16 Jul 2010 16:18:16 -0700
Committer:  H. Peter Anvin <hpa at linux.intel.com>
CommitDate: Fri, 16 Jul 2010 16:19:22 -0700

diskio: allow fallback from EDD to CHS

At least one system has been found which require fallback from EDD to
CHS, so actually make it doable.

Signed-off-by: H. Peter Anvin <hpa at linux.intel.com>


---
 core/fs/diskio.c |  111 +++++++++++++++++++++++++++++++++++-------------------
 1 files changed, 72 insertions(+), 39 deletions(-)

diff --git a/core/fs/diskio.c b/core/fs/diskio.c
index d4901c3..746335f 100644
--- a/core/fs/diskio.c
+++ b/core/fs/diskio.c
@@ -23,6 +23,10 @@ static int chs_rdwr_sectors(struct disk *disk, void *buf,
     size_t done = 0;
     size_t bytes;
     int retry;
+    uint32_t maxtransfer = disk->maxtransfer;
+
+    if (maxtransfer > 63)
+	maxtransfer = 63;
 
     memset(&ireg, 0, sizeof ireg);
 
@@ -31,8 +35,8 @@ static int chs_rdwr_sectors(struct disk *disk, void *buf,
 
     while (count) {
 	chunk = count;
-	if (chunk > disk->maxtransfer)
-	    chunk = disk->maxtransfer;
+	if (chunk > maxtransfer)
+	    chunk = maxtransfer;
 
 	freeseg = (0x10000 - ((size_t)ptr & 0xffff)) >> sector_shift;
 
@@ -47,16 +51,19 @@ static int chs_rdwr_sectors(struct disk *disk, void *buf,
 	if (chunk > freeseg)
 	    chunk = freeseg;
 
-	bytes = chunk << sector_shift;
-
-	if (tptr != ptr && is_write)
-	    memcpy(tptr, ptr, bytes);
-
 	s = xlba % disk->s;
 	t = xlba / disk->s;
 	h = t % disk->h;
 	c = t / disk->h;
 
+	if (chunk > (disk->s - s))
+	    chunk = disk->s - s;
+
+	bytes = chunk << sector_shift;
+
+	if (tptr != ptr && is_write)
+	    memcpy(tptr, ptr, bytes);
+
 	ireg.eax.b[0] = chunk;
 	ireg.ecx.b[1] = c;
 	ireg.ecx.b[0] = ((c & 0x300) >> 2) | (s+1);
@@ -67,34 +74,39 @@ static int chs_rdwr_sectors(struct disk *disk, void *buf,
 	retry = RETRY_COUNT;
 
         for (;;) {
-	    dprintf("CHS[%02x]: %u @ %llu (%u/%u/%u) %04x:%04x %s %p\n",
-		    ireg.edx.b[0], chunk, xlba, c, h, s+1,
-		    ireg.es, ireg.ebx.w[0],
-		    (ireg.eax.b[1] & 1) ? "<-" : "->",
-		    ptr);
-
-	    __intcall(0x13, &ireg, &oreg);
-	    if (!(oreg.eflags.l & EFLAGS_CF))
-		break;
-
-	    dprintf("CHS: error AX = %04x\n", oreg.eax.w[0]);
-
-	    if (retry--)
-		continue;
-
-	    /* For any starting value, this will always end with ..., 1, 0 */
-	    chunk >>= 1;
-            if (chunk) {
-		disk->maxtransfer = chunk;
-		retry = RETRY_COUNT;
-                ireg.eax.b[0] = chunk;
-                continue;
-	    } else {
-		printf("CHS: Error %04x %s sector %llu (%u/%u/%u)\n",
-		       oreg.eax.w[0],
-		       is_write ? "writing" : "reading",
-		       lba, c, h, s+1);
+	    if (c < 1024) {
+		dprintf("CHS[%02x]: %u @ %llu (%u/%u/%u) %04x:%04x %s %p\n",
+			ireg.edx.b[0], chunk, xlba, c, h, s+1,
+			ireg.es, ireg.ebx.w[0],
+			(ireg.eax.b[1] & 1) ? "<-" : "->",
+			ptr);
+
+		__intcall(0x13, &ireg, &oreg);
+		if (!(oreg.eflags.l & EFLAGS_CF))
+		    break;
+
+		dprintf("CHS: error AX = %04x\n", oreg.eax.w[0]);
+
+		if (retry--)
+		    continue;
+
+		/*
+		 * For any starting value, this will always end with
+		 * ..., 1, 0
+		 */
+		chunk >>= 1;
+		if (chunk) {
+		    maxtransfer = chunk;
+		    retry = RETRY_COUNT;
+		    ireg.eax.b[0] = chunk;
+		    continue;
+		}
 	    }
+
+	    printf("CHS: Error %04x %s sector %llu (%u/%u/%u)\n",
+		   oreg.eax.w[0],
+		   is_write ? "writing" : "reading",
+		   lba, c, h, s+1);
 	    return done;	/* Failure */
 	}
 
@@ -103,6 +115,9 @@ static int chs_rdwr_sectors(struct disk *disk, void *buf,
 	if (tptr != ptr && !is_write)
 	    memcpy(ptr, tptr, bytes);
 
+	/* If we dropped maxtransfer, it eventually worked, so remember it */
+	disk->maxtransfer = maxtransfer;
+
 	ptr   += bytes;
 	xlba  += chunk;
 	count -= chunk;
@@ -131,6 +146,7 @@ static int edd_rdwr_sectors(struct disk *disk, void *buf,
     size_t done = 0;
     size_t bytes;
     int retry;
+    uint32_t maxtransfer = disk->maxtransfer;
 
     memset(&ireg, 0, sizeof ireg);
 
@@ -146,8 +162,8 @@ static int edd_rdwr_sectors(struct disk *disk, void *buf,
     lba += disk->part_start;
     while (count) {
 	chunk = count;
-	if (chunk > disk->maxtransfer)
-	    chunk = disk->maxtransfer;
+	if (chunk > maxtransfer)
+	    chunk = maxtransfer;
 
 	freeseg = (0x10000 - ((size_t)ptr & 0xffff)) >> sector_shift;
 
@@ -201,13 +217,26 @@ static int edd_rdwr_sectors(struct disk *disk, void *buf,
 	    /* For any starting value, this will always end with ..., 1, 0 */
 	    chunk >>= 1;
 	    if (chunk) {
-		disk->maxtransfer = chunk;
+		maxtransfer = chunk;
 		retry = RETRY_COUNT;
 		continue;
 	    }
 
-	    /*** XXX: Consider falling back to CHS here?! ***/
-            printf("EDD: Error %04x %s sector %llu\n",
+	    /*
+	     * Total failure.  There are systems which identify as
+	     * EDD-capable but aren't; the known such systems return
+	     * error code AH=1 (invalid function), but let's not
+	     * assume that for now.
+	     */
+	    if (lba < ((disk->h * disk->s) << 10)) {
+		done = chs_rdwr_sectors(disk, buf, lba, count, is_write);
+		if (done == (count << sector_shift)) {
+		    /* Successful, assume this is a CHS disk */
+		    disk->rdwr_sectors = chs_rdwr_sectors;
+		    return done;
+		}
+	    }
+	    printf("EDD: Error %04x %s sector %llu\n",
 		   oreg.eax.w[0],
 		   is_write ? "writing" : "reading",
 		   lba);
@@ -219,6 +248,9 @@ static int edd_rdwr_sectors(struct disk *disk, void *buf,
 	if (tptr != ptr && !is_write)
 	    memcpy(ptr, tptr, bytes);
 
+	/* If we dropped maxtransfer, it eventually worked, so remember it */
+	disk->maxtransfer = maxtransfer;
+
 	ptr   += bytes;
 	lba   += chunk;
 	count -= chunk;
@@ -226,6 +258,7 @@ static int edd_rdwr_sectors(struct disk *disk, void *buf,
     }
     return done;
 }
+
 struct edd_disk_params {
     uint16_t  len;
     uint16_t  flags;



More information about the Syslinux-commits mailing list