size_t done = 0;
size_t bytes;
int retry;
+ uint32_t maxtransfer = disk->maxtransfer;
+
+ if (maxtransfer > 63)
+ maxtransfer = 63;
memset(&ireg, 0, sizeof ireg);
while (count) {
chunk = count;
- if (chunk > disk->maxtransfer)
- chunk = disk->maxtransfer;
+ if (chunk > maxtransfer)
+ chunk = maxtransfer;
freeseg = (0x10000 - ((size_t)ptr & 0xffff)) >> sector_shift;
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);
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 */
}
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;
size_t done = 0;
size_t bytes;
int retry;
+ uint32_t maxtransfer = disk->maxtransfer;
memset(&ireg, 0, sizeof ireg);
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;
/* 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);
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;
}
return done;
}
+
struct edd_disk_params {
uint16_t len;
uint16_t flags;