[FREELDR] Cache INT13h drive data in pcdisk.c (#2097)
[reactos.git] / boot / freeldr / freeldr / arch / i386 / pcdisk.c
index 7c282a6..37058bc 100644 (file)
 
 #include <freeldr.h>
 
-#define NDEBUG
 #include <debug.h>
-
 DBG_DEFAULT_CHANNEL(DISK);
 
+/* Enable this line if you want to support multi-drive caching (increases FreeLdr size!) */
+// #define CACHE_MULTI_DRIVES
+
 #include <pshpack2.h>
 
 typedef struct
@@ -75,6 +76,105 @@ typedef struct
 
 #include <poppack.h>
 
+typedef struct _PC_DISK_DRIVE
+{
+    /* Disk geometry (legacy BIOS and INT13 extended) */
+    GEOMETRY Geometry;
+    EXTENDED_GEOMETRY ExtGeometry;
+
+    /* TRUE when INT 13h extensions are supported */
+    BOOLEAN Int13ExtensionsSupported;
+
+    /*
+     * 'IsRemovable' flag: TRUE when the drive is removable (e.g. floppy, CD-ROM...).
+     * In that case some of the cached information might need to be refreshed regularly.
+     */
+    BOOLEAN IsRemovable;
+
+#ifdef CACHE_MULTI_DRIVES
+    /*
+     * 'Initialized' flag: if TRUE then the drive has been initialized;
+     * if FALSE then it needs to be initialized; if its high bit is set
+     * then there has been an error; don't try to use it.
+     */
+    BOOLEAN Initialized;
+#endif
+} PC_DISK_DRIVE, *PPC_DISK_DRIVE;
+
+#ifdef CACHE_MULTI_DRIVES
+/* Cache of all possible PC disk drives */
+// Maximum number of disks is 0x100, indexed from 0x00 to 0xFF.
+static PC_DISK_DRIVE PcDiskDrive[0x100];
+#else
+/* Cached data for the last-accessed PC disk drive */
+// We use a USHORT so that we can initialize it with a drive number that cannot exist
+// on the system (they are <= 0xFF), therefore forcing drive caching on first access.
+static USHORT LastDriveNumber = 0xFFFF;
+static PC_DISK_DRIVE PcDiskDrive;
+#endif /* CACHE_MULTI_DRIVES */
+
+/* DISK IO ERROR SUPPORT *****************************************************/
+
+static LONG lReportError = 0; // >= 0: display errors; < 0: hide errors.
+
+LONG DiskReportError(BOOLEAN bShowError)
+{
+    /* Set the reference count */
+    if (bShowError) ++lReportError;
+    else            --lReportError;
+    return lReportError;
+}
+
+static PCSTR DiskGetErrorCodeString(ULONG ErrorCode)
+{
+    switch (ErrorCode)
+    {
+    case 0x00:  return "no error";
+    case 0x01:  return "bad command passed to driver";
+    case 0x02:  return "address mark not found or bad sector";
+    case 0x03:  return "diskette write protect error";
+    case 0x04:  return "sector not found";
+    case 0x05:  return "fixed disk reset failed";
+    case 0x06:  return "diskette changed or removed";
+    case 0x07:  return "bad fixed disk parameter table";
+    case 0x08:  return "DMA overrun";
+    case 0x09:  return "DMA access across 64k boundary";
+    case 0x0A:  return "bad fixed disk sector flag";
+    case 0x0B:  return "bad fixed disk cylinder";
+    case 0x0C:  return "unsupported track/invalid media";
+    case 0x0D:  return "invalid number of sectors on fixed disk format";
+    case 0x0E:  return "fixed disk controlled data address mark detected";
+    case 0x0F:  return "fixed disk DMA arbitration level out of range";
+    case 0x10:  return "ECC/CRC error on disk read";
+    case 0x11:  return "recoverable fixed disk data error, data fixed by ECC";
+    case 0x20:  return "controller error (NEC for floppies)";
+    case 0x40:  return "seek failure";
+    case 0x80:  return "time out, drive not ready";
+    case 0xAA:  return "fixed disk drive not ready";
+    case 0xBB:  return "fixed disk undefined error";
+    case 0xCC:  return "fixed disk write fault on selected drive";
+    case 0xE0:  return "fixed disk status error/Error reg = 0";
+    case 0xFF:  return "sense operation failed";
+
+    default:    return "unknown error code";
+    }
+}
+
+static VOID DiskError(PCSTR ErrorString, ULONG ErrorCode)
+{
+    CHAR ErrorCodeString[200];
+
+    if (lReportError < 0)
+        return;
+
+    sprintf(ErrorCodeString, "%s\n\nError Code: 0x%lx\nError: %s",
+            ErrorString, ErrorCode, DiskGetErrorCodeString(ErrorCode));
+
+    ERR("%s\n", ErrorCodeString);
+
+    UiMessageBox(ErrorCodeString);
+}
+
 /* FUNCTIONS *****************************************************************/
 
 BOOLEAN DiskResetController(UCHAR DriveNumber)
@@ -101,15 +201,370 @@ BOOLEAN DiskResetController(UCHAR DriveNumber)
     return INT386_SUCCESS(RegsOut);
 }
 
-static BOOLEAN PcDiskReadLogicalSectorsLBA(UCHAR DriveNumber, ULONGLONG SectorNumber, ULONG SectorCount, PVOID Buffer)
+static BOOLEAN
+DiskIsDriveRemovable(UCHAR DriveNumber)
+{
+    /*
+     * Hard disks use drive numbers >= 0x80 . So if the drive number
+     * indicates a hard disk then return FALSE.
+     * 0x49 is our magic ramdisk drive, so return FALSE for that too.
+     */
+    if ((DriveNumber >= 0x80) || (DriveNumber == 0x49))
+        return FALSE;
+
+    /* The drive is a floppy diskette so return TRUE */
+    return TRUE;
+}
+
+static BOOLEAN
+DiskInt13ExtensionsSupported(IN UCHAR DriveNumber)
+{
+    REGS RegsIn, RegsOut;
+
+    /*
+     * Some BIOSes report that extended disk access functions are not supported
+     * when booting from a CD (e.g. Phoenix BIOS v6.00PG and Insyde BIOS shipping
+     * with Intel Macs). Therefore we just return TRUE if we're booting from a CD
+     * - we can assume that all El Torito capable BIOSes support INT 13 extensions.
+     * We simply detect whether we're booting from CD by checking whether the drive
+     * number is >= 0x8A. It's 0x90 on the Insyde BIOS, and 0x9F on most other BIOSes.
+     */
+    if (DriveNumber >= 0x8A)
+        return TRUE;
+
+    /*
+     * IBM/MS INT 13 Extensions - INSTALLATION CHECK
+     * AH = 41h
+     * BX = 55AAh
+     * DL = drive (80h-FFh)
+     * Return:
+     * CF set on error (extensions not supported)
+     * AH = 01h (invalid function)
+     * CF clear if successful
+     * BX = AA55h if installed
+     * AH = major version of extensions
+     * 01h = 1.x
+     * 20h = 2.0 / EDD-1.0
+     * 21h = 2.1 / EDD-1.1
+     * 30h = EDD-3.0
+     * AL = internal use
+     * CX = API subset support bitmap
+     * DH = extension version (v2.0+ ??? -- not present in 1.x)
+     *
+     * Bitfields for IBM/MS INT 13 Extensions API support bitmap
+     * Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported
+     * Bit 1, removable drive controller functions (AH=45h,46h,48h,49h,INT 15/AH=52h) supported
+     * Bit 2, enhanced disk drive (EDD) functions (AH=48h,AH=4Eh) supported
+     *        extended drive parameter table is valid
+     * Bits 3-15 reserved
+     */
+    RegsIn.b.ah = 0x41;
+    RegsIn.w.bx = 0x55AA;
+    RegsIn.b.dl = DriveNumber;
+
+    /* Reset the disk controller */
+    Int386(0x13, &RegsIn, &RegsOut);
+    if (!INT386_SUCCESS(RegsOut))
+    {
+        /* CF set on error (extensions not supported) */
+        return FALSE;
+    }
+
+    if (RegsOut.w.bx != 0xAA55)
+    {
+        /* BX = AA55h if installed */
+        return FALSE;
+    }
+
+    if (!(RegsOut.w.cx & 0x0001))
+    {
+        /*
+         * CX = API subset support bitmap.
+         * Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported.
+         */
+        WARN("Suspicious API subset support bitmap 0x%x on device 0x%lx\n",
+             RegsOut.w.cx, DriveNumber);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static BOOLEAN
+DiskGetExtendedDriveParameters(
+    IN UCHAR DriveNumber,
+    IN PPC_DISK_DRIVE DiskDrive,
+    OUT PVOID Buffer,
+    IN USHORT BufferSize)
+{
+    REGS RegsIn, RegsOut;
+    PUSHORT Ptr = (PUSHORT)(BIOSCALLBUFFER);
+
+    TRACE("DiskGetExtendedDriveParameters(0x%x)\n", DriveNumber);
+
+    if (!DiskDrive->Int13ExtensionsSupported)
+        return FALSE;
+
+    /* Initialize transfer buffer */
+    *Ptr = BufferSize;
+
+    /*
+     * BIOS Int 13h, function 48h - Get drive parameters
+     * AH = 48h
+     * DL = drive (bit 7 set for hard disk)
+     * DS:SI = result buffer
+     * Return:
+     * CF set on error
+     * AH = status (07h)
+     * CF clear if successful
+     * AH = 00h
+     * DS:SI -> result buffer
+     */
+    RegsIn.b.ah = 0x48;
+    RegsIn.b.dl = DriveNumber;
+    RegsIn.x.ds = BIOSCALLBUFSEGMENT;   // DS:SI -> result buffer
+    RegsIn.w.si = BIOSCALLBUFOFFSET;
+
+    /* Get drive parameters */
+    Int386(0x13, &RegsIn, &RegsOut);
+    if (!INT386_SUCCESS(RegsOut))
+        return FALSE;
+
+    RtlCopyMemory(Buffer, Ptr, BufferSize);
+
+#if DBG
+    TRACE("size of buffer:                          %x\n", Ptr[0]);
+    TRACE("information flags:                       %x\n", Ptr[1]);
+    TRACE("number of physical cylinders on drive:   %u\n", *(PULONG)&Ptr[2]);
+    TRACE("number of physical heads on drive:       %u\n", *(PULONG)&Ptr[4]);
+    TRACE("number of physical sectors per track:    %u\n", *(PULONG)&Ptr[6]);
+    TRACE("total number of sectors on drive:        %I64u\n", *(unsigned long long*)&Ptr[8]);
+    TRACE("bytes per sector:                        %u\n", Ptr[12]);
+    if (Ptr[0] >= 0x1e)
+    {
+        TRACE("EED configuration parameters:            %x:%x\n", Ptr[13], Ptr[14]);
+        if (Ptr[13] != 0xffff && Ptr[14] != 0xffff)
+        {
+            PUCHAR SpecPtr = (PUCHAR)(ULONG_PTR)((Ptr[13] << 4) + Ptr[14]);
+            TRACE("SpecPtr:                                 %x\n", SpecPtr);
+            TRACE("physical I/O port base address:          %x\n", *(PUSHORT)&SpecPtr[0]);
+            TRACE("disk-drive control port address:         %x\n", *(PUSHORT)&SpecPtr[2]);
+            TRACE("drive flags:                             %x\n", SpecPtr[4]);
+            TRACE("proprietary information:                 %x\n", SpecPtr[5]);
+            TRACE("IRQ for drive:                           %u\n", SpecPtr[6]);
+            TRACE("sector count for multi-sector transfers: %u\n", SpecPtr[7]);
+            TRACE("DMA control:                             %x\n", SpecPtr[8]);
+            TRACE("programmed I/O control:                  %x\n", SpecPtr[9]);
+            TRACE("drive options:                           %x\n", *(PUSHORT)&SpecPtr[10]);
+        }
+    }
+    if (Ptr[0] >= 0x42)
+    {
+        TRACE("signature:                             %x\n", Ptr[15]);
+    }
+#endif
+
+    return TRUE;
+}
+
+static BOOLEAN
+InitDriveGeometry(
+    IN UCHAR DriveNumber,
+    IN PPC_DISK_DRIVE DiskDrive)
+{
+    BOOLEAN Success;
+    REGS RegsIn, RegsOut;
+    ULONG Cylinders;
+
+    /* Get the extended geometry first */
+    DiskDrive->ExtGeometry.Size = sizeof(DiskDrive->ExtGeometry);
+    Success = DiskGetExtendedDriveParameters(DriveNumber, DiskDrive,
+                                             &DiskDrive->ExtGeometry,
+                                             DiskDrive->ExtGeometry.Size);
+    if (!Success)
+    {
+        /* Failed, zero it out */
+        RtlZeroMemory(&DiskDrive->ExtGeometry, sizeof(DiskDrive->ExtGeometry));
+    }
+    else
+    {
+        TRACE("DiskGetExtendedDriveParameters(0x%x) returned:\n"
+              "Cylinders  : 0x%x\n"
+              "Heads      : 0x%x\n"
+              "Sects/Track: 0x%x\n"
+              "Bytes/Sect : 0x%x\n",
+              DriveNumber,
+              DiskDrive->ExtGeometry.Cylinders,
+              DiskDrive->ExtGeometry.Heads,
+              DiskDrive->ExtGeometry.SectorsPerTrack,
+              DiskDrive->ExtGeometry.BytesPerSector);
+    }
+
+    /* Now try the legacy geometry */
+    RtlZeroMemory(&DiskDrive->Geometry, sizeof(DiskDrive->Geometry));
+
+    /*
+     * BIOS Int 13h, function 08h - Get drive parameters
+     * AH = 08h
+     * DL = drive (bit 7 set for hard disk)
+     * ES:DI = 0000h:0000h to guard against BIOS bugs
+     * Return:
+     * CF set on error
+     * AH = status (07h)
+     * CF clear if successful
+     * AH = 00h
+     * AL = 00h on at least some BIOSes
+     * BL = drive type (AT/PS2 floppies only)
+     * CH = low eight bits of maximum cylinder number
+     * CL = maximum sector number (bits 5-0)
+     *      high two bits of maximum cylinder number (bits 7-6)
+     * DH = maximum head number
+     * DL = number of drives
+     * ES:DI -> drive parameter table (floppies only)
+     */
+    RegsIn.b.ah = 0x08;
+    RegsIn.b.dl = DriveNumber;
+    RegsIn.w.es = 0x0000;
+    RegsIn.w.di = 0x0000;
+
+    /* Get drive parameters */
+    Int386(0x13, &RegsIn, &RegsOut);
+    if (!INT386_SUCCESS(RegsOut))
+    {
+        /* We failed, return the result of the previous call (extended geometry) */
+        return Success;
+    }
+    /* OR it with the old result, so that we return TRUE whenever either call succeeded */
+    Success |= TRUE;
+
+    Cylinders = (RegsOut.b.cl & 0xC0) << 2;
+    Cylinders += RegsOut.b.ch;
+    Cylinders++;
+    DiskDrive->Geometry.Cylinders = Cylinders;
+    DiskDrive->Geometry.Heads = RegsOut.b.dh + 1;
+    DiskDrive->Geometry.Sectors = RegsOut.b.cl & 0x3F;
+    DiskDrive->Geometry.BytesPerSector = 512;     /* Just assume 512 bytes per sector */
+
+    TRACE("Regular Int13h(0x%x) returned:\n"
+          "Cylinders  : 0x%x\n"
+          "Heads      : 0x%x\n"
+          "Sects/Track: 0x%x (original 0x%x)\n"
+          "Bytes/Sect : 0x%x\n",
+          DriveNumber,
+          DiskDrive->Geometry.Cylinders,
+          DiskDrive->Geometry.Heads,
+          DiskDrive->Geometry.Sectors, RegsOut.b.cl,
+          DiskDrive->Geometry.BytesPerSector);
+
+    return Success;
+}
+
+static BOOLEAN
+PcDiskDriveInit(
+    IN UCHAR DriveNumber,
+    IN OUT PPC_DISK_DRIVE DiskDrive)
+{
+    DiskDrive->IsRemovable = DiskIsDriveRemovable(DriveNumber);
+
+    /*
+     * Check to see if it is a fixed disk drive.
+     * If so then check to see if INT 13h extensions work.
+     * If they do then use them, otherwise default back to BIOS calls.
+     */
+    DiskDrive->Int13ExtensionsSupported = DiskInt13ExtensionsSupported(DriveNumber);
+
+    if (!InitDriveGeometry(DriveNumber, DiskDrive))
+        return FALSE;
+
+    TRACE("\n"
+          "DriveNumber: 0x%x\n"
+          "IsRemovable              = %s\n"
+          "Int13ExtensionsSupported = %s\n",
+          DriveNumber,
+          DiskDrive->IsRemovable ? "TRUE" : "FALSE",
+          DiskDrive->Int13ExtensionsSupported ? "TRUE" : "FALSE");
+
+    return TRUE;
+}
+
+static inline
+PPC_DISK_DRIVE
+PcDiskDriveNumberToDrive(IN UCHAR DriveNumber)
+{
+#ifdef CACHE_MULTI_DRIVES
+    PPC_DISK_DRIVE DiskDrive;
+
+    ASSERT((0 <= DriveNumber) && (DriveNumber < RTL_NUMBER_OF(PcDiskDrive)));
+
+    /* Retrieve a slot */
+    DiskDrive = &PcDiskDrive[DriveNumber];
+
+    /* If the drive has not been initialized before... */
+    if (!DiskDrive->Initialized)
+    {
+        /* ... try to initialize it now. */
+        if (!PcDiskDriveInit(DriveNumber, DiskDrive))
+        {
+            /*
+             * If we failed, there is no drive at this number
+             * and flag it as such (set its high bit).
+             */
+            DiskDrive->Initialized |= 0x80;
+            return NULL;
+        }
+        DiskDrive->Initialized = TRUE;
+    }
+    else if (DiskDrive->Initialized & 0x80)
+    {
+        /*
+         * The disk failed to be initialized previously, reset its flag to give
+         * it chance to be initialized again later, but just fail for the moment.
+         */
+        DiskDrive->Initialized = FALSE;
+        return NULL;
+    }
+
+    return DiskDrive;
+#else
+    static PC_DISK_DRIVE NewDiskDrive;
+
+    ASSERT((0 <= DriveNumber) && (DriveNumber <= 0xFF));
+
+    /* Update cached information */
+
+    /* If the drive has not been accessed last before... */
+    if ((USHORT)DriveNumber != LastDriveNumber)
+    {
+        /* ... try to (re-)initialize and cache it now. */
+        RtlZeroMemory(&NewDiskDrive, sizeof(NewDiskDrive));
+        if (!PcDiskDriveInit(DriveNumber, &NewDiskDrive))
+        {
+            /*
+             * If we failed, there is no drive at this number.
+             * Keep the last-accessed valid drive cached.
+             */
+            return NULL;
+        }
+        /* We succeeded, cache the drive data */
+        PcDiskDrive = NewDiskDrive;
+        LastDriveNumber = (USHORT)DriveNumber;
+    }
+
+    return &PcDiskDrive;
+#endif /* CACHE_MULTI_DRIVES */
+}
+
+static BOOLEAN
+PcDiskReadLogicalSectorsLBA(
+    IN UCHAR DriveNumber,
+    IN ULONGLONG SectorNumber,
+    IN ULONG SectorCount,
+    OUT PVOID Buffer)
 {
     REGS RegsIn, RegsOut;
     ULONG RetryCount;
     PI386_DISK_ADDRESS_PACKET Packet = (PI386_DISK_ADDRESS_PACKET)(BIOSCALLBUFFER);
 
-    TRACE("PcDiskReadLogicalSectorsLBA() DriveNumber: 0x%x SectorNumber: %I64d SectorCount: %d Buffer: 0x%x\n", DriveNumber, SectorNumber, SectorCount, Buffer);
-    ASSERT(((ULONG_PTR)Buffer) <= 0xFFFFF);
-
     /* Setup disk address packet */
     RtlZeroMemory(Packet, sizeof(*Packet));
     Packet->PacketSize = sizeof(*Packet);
@@ -136,7 +591,7 @@ static BOOLEAN PcDiskReadLogicalSectorsLBA(UCHAR DriveNumber, ULONGLONG SectorNu
     RegsIn.w.si = BIOSCALLBUFOFFSET;
 
     /* Retry 3 times */
-    for (RetryCount=0; RetryCount<3; RetryCount++)
+    for (RetryCount = 0; RetryCount < 3; ++RetryCount)
     {
         Int386(0x13, &RegsIn, &RegsOut);
 
@@ -159,12 +614,21 @@ static BOOLEAN PcDiskReadLogicalSectorsLBA(UCHAR DriveNumber, ULONGLONG SectorNu
     }
 
     /* If we get here then the read failed */
-    ERR("Disk Read Failed in LBA mode: %x (DriveNumber: 0x%x SectorNumber: %I64d SectorCount: %d)\n", RegsOut.b.ah, DriveNumber, SectorNumber, SectorCount);
+    DiskError("Disk Read Failed in LBA mode", RegsOut.b.ah);
+    ERR("Disk Read Failed in LBA mode: %x (%s) (DriveNumber: 0x%x SectorNumber: %I64d SectorCount: %d)\n",
+        RegsOut.b.ah, DiskGetErrorCodeString(RegsOut.b.ah),
+        DriveNumber, SectorNumber, SectorCount);
 
     return FALSE;
 }
 
-static BOOLEAN PcDiskReadLogicalSectorsCHS(UCHAR DriveNumber, ULONGLONG SectorNumber, ULONG SectorCount, PVOID Buffer)
+static BOOLEAN
+PcDiskReadLogicalSectorsCHS(
+    IN UCHAR DriveNumber,
+    IN PPC_DISK_DRIVE DiskDrive,
+    IN ULONGLONG SectorNumber,
+    IN ULONG SectorCount,
+    OUT PVOID Buffer)
 {
     UCHAR PhysicalSector;
     UCHAR PhysicalHead;
@@ -174,17 +638,11 @@ static BOOLEAN PcDiskReadLogicalSectorsCHS(UCHAR DriveNumber, ULONGLONG SectorNu
     REGS RegsIn, RegsOut;
     ULONG RetryCount;
 
-    TRACE("PcDiskReadLogicalSectorsCHS()\n");
-
-    /* Get the drive geometry */
-    if (!MachDiskGetDriveGeometry(DriveNumber, &DriveGeometry) ||
-        DriveGeometry.Sectors == 0 ||
-        DriveGeometry.Heads == 0)
-    {
+    DriveGeometry = DiskDrive->Geometry;
+    if (DriveGeometry.Sectors == 0 || DriveGeometry.Heads == 0)
         return FALSE;
-    }
 
-    while (SectorCount)
+    while (SectorCount > 0)
     {
         /*
          * Calculate the physical disk offsets.
@@ -248,7 +706,7 @@ static BOOLEAN PcDiskReadLogicalSectorsCHS(UCHAR DriveNumber, ULONGLONG SectorNu
         RegsIn.w.bx = ((ULONG_PTR)Buffer) & 0x0F;
 
         /* Perform the read. Retry 3 times. */
-        for (RetryCount=0; RetryCount<3; RetryCount++)
+        for (RetryCount = 0; RetryCount < 3; ++RetryCount)
         {
             Int386(0x13, &RegsIn, &RegsOut);
 
@@ -262,7 +720,7 @@ static BOOLEAN PcDiskReadLogicalSectorsCHS(UCHAR DriveNumber, ULONGLONG SectorNu
             {
                 break;
             }
-            /* If it failed the do the next retry */
+            /* If it failed then do the next retry */
             else
             {
                 DiskResetController(DriveNumber);
@@ -273,15 +731,20 @@ static BOOLEAN PcDiskReadLogicalSectorsCHS(UCHAR DriveNumber, ULONGLONG SectorNu
         /* If we retried 3 times then fail */
         if (RetryCount >= 3)
         {
-            ERR("Disk Read Failed in CHS mode, after retrying 3 times: %x\n", RegsOut.b.ah);
+            DiskError("Disk Read Failed in CHS mode, after retrying 3 times", RegsOut.b.ah);
+            ERR("Disk Read Failed in CHS mode, after retrying 3 times: %x (%s) (DriveNumber: 0x%x SectorNumber: %I64d SectorCount: %d)\n",
+                RegsOut.b.ah, DiskGetErrorCodeString(RegsOut.b.ah),
+                DriveNumber, SectorNumber, SectorCount);
             return FALSE;
         }
 
-        // I have learned that not all BIOSes return
-        // the sector read count in the AL register (at least mine doesn't)
-        // even if the sectors were read correctly. So instead
-        // of checking the sector read count we will rely solely
-        // on the carry flag being set on error
+        /*
+         * I have learned that not all BIOSes return
+         * the sector read count in the AL register (at least mine doesn't)
+         * even if the sectors were read correctly. So instead
+         * of checking the sector read count we will rely solely
+         * on the carry flag being set on error.
+         */
 
         Buffer = (PVOID)((ULONG_PTR)Buffer + (NumberOfSectorsToRead * DriveGeometry.BytesPerSector));
         SectorCount -= NumberOfSectorsToRead;
@@ -291,317 +754,92 @@ static BOOLEAN PcDiskReadLogicalSectorsCHS(UCHAR DriveNumber, ULONGLONG SectorNu
     return TRUE;
 }
 
-static BOOLEAN DiskInt13ExtensionsSupported(UCHAR DriveNumber)
-{
-    static UCHAR LastDriveNumber = 0xff;
-    static BOOLEAN LastSupported;
-    REGS RegsIn, RegsOut;
-
-    TRACE("DiskInt13ExtensionsSupported()\n");
-
-    if (DriveNumber == LastDriveNumber)
-    {
-        TRACE("Using cached value %s for drive 0x%x\n",
-              LastSupported ? "TRUE" : "FALSE", DriveNumber);
-        return LastSupported;
-    }
-
-    /*
-     * Some BIOSes report that extended disk access functions are not supported
-     * when booting from a CD (e.g. Phoenix BIOS v6.00PG and Insyde BIOS shipping
-     * with Intel Macs). Therefore we just return TRUE if we're booting from a CD -
-     * we can assume that all El Torito capable BIOSes support INT 13 extensions.
-     * We simply detect whether we're booting from CD by checking whether the drive
-     * number is >= 0x8A. It's 0x90 on the Insyde BIOS, and 0x9F on most other BIOSes.
-     */
-    if (DriveNumber >= 0x8A)
-    {
-        LastSupported = TRUE;
-        return TRUE;
-    }
-
-    LastDriveNumber = DriveNumber;
-
-    /*
-     * IBM/MS INT 13 Extensions - INSTALLATION CHECK
-     * AH = 41h
-     * BX = 55AAh
-     * DL = drive (80h-FFh)
-     * Return:
-     * CF set on error (extensions not supported)
-     * AH = 01h (invalid function)
-     * CF clear if successful
-     * BX = AA55h if installed
-     * AH = major version of extensions
-     * 01h = 1.x
-     * 20h = 2.0 / EDD-1.0
-     * 21h = 2.1 / EDD-1.1
-     * 30h = EDD-3.0
-     * AL = internal use
-     * CX = API subset support bitmap
-     * DH = extension version (v2.0+ ??? -- not present in 1.x)
-     *
-     * Bitfields for IBM/MS INT 13 Extensions API support bitmap
-     * Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported
-     * Bit 1, removable drive controller functions (AH=45h,46h,48h,49h,INT 15/AH=52h) supported
-     * Bit 2, enhanced disk drive (EDD) functions (AH=48h,AH=4Eh) supported
-     *        extended drive parameter table is valid
-     * Bits 3-15 reserved
-     */
-    RegsIn.b.ah = 0x41;
-    RegsIn.w.bx = 0x55AA;
-    RegsIn.b.dl = DriveNumber;
-
-    /* Reset the disk controller */
-    Int386(0x13, &RegsIn, &RegsOut);
-
-    if (!INT386_SUCCESS(RegsOut))
-    {
-        /* CF set on error (extensions not supported) */
-        LastSupported = FALSE;
-        return FALSE;
-    }
-
-    if (RegsOut.w.bx != 0xAA55)
-    {
-        /* BX = AA55h if installed */
-        LastSupported = FALSE;
-        return FALSE;
-    }
-
-    if (!(RegsOut.w.cx & 0x0001))
-    {
-        /*
-         * CX = API subset support bitmap.
-         * Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported.
-         */
-        DbgPrint("Suspicious API subset support bitmap 0x%x on device 0x%lx\n",
-                 RegsOut.w.cx, DriveNumber);
-        LastSupported = FALSE;
-        return FALSE;
-    }
-
-    LastSupported = TRUE;
-    return TRUE;
-}
-
-BOOLEAN PcDiskReadLogicalSectors(UCHAR DriveNumber, ULONGLONG SectorNumber, ULONG SectorCount, PVOID Buffer)
+BOOLEAN
+PcDiskReadLogicalSectors(
+    IN UCHAR DriveNumber,
+    IN ULONGLONG SectorNumber,
+    IN ULONG SectorCount,
+    OUT PVOID Buffer)
 {
-    BOOLEAN ExtensionsSupported;
+    PPC_DISK_DRIVE DiskDrive;
 
     TRACE("PcDiskReadLogicalSectors() DriveNumber: 0x%x SectorNumber: %I64d SectorCount: %d Buffer: 0x%x\n",
           DriveNumber, SectorNumber, SectorCount, Buffer);
 
-    /*
-     * Check to see if it is a fixed disk drive.
-     * If so then check to see if Int13 extensions work.
-     * If they do then use them, otherwise default back to BIOS calls.
-     */
-    ExtensionsSupported = DiskInt13ExtensionsSupported(DriveNumber);
+    /* 16-bit BIOS addressing limitation */
+    ASSERT(((ULONG_PTR)Buffer) <= 0xFFFFF);
 
-    if ((DriveNumber >= 0x80) && ExtensionsSupported)
-    {
-        TRACE("Using Int 13 Extensions for read. DiskInt13ExtensionsSupported(%d) = %s\n", DriveNumber, ExtensionsSupported ? "TRUE" : "FALSE");
+    DiskDrive = PcDiskDriveNumberToDrive(DriveNumber);
+    if (!DiskDrive)
+        return FALSE;
 
+    if ((DriveNumber >= 0x80) && DiskDrive->Int13ExtensionsSupported)
+    {
         /* LBA is easy, nothing to calculate. Just do the read. */
+        TRACE("--> Using LBA\n");
         return PcDiskReadLogicalSectorsLBA(DriveNumber, SectorNumber, SectorCount, Buffer);
     }
     else
     {
-        /* LBA is not supported default to the CHS calls */
-        return PcDiskReadLogicalSectorsCHS(DriveNumber, SectorNumber, SectorCount, Buffer);
+        /* LBA is not supported, default to CHS */
+        TRACE("--> Using CHS\n");
+        return PcDiskReadLogicalSectorsCHS(DriveNumber, DiskDrive, SectorNumber, SectorCount, Buffer);
     }
-
-    return TRUE;
 }
 
-VOID DiskStopFloppyMotor(VOID)
+#if defined(__i386__) || defined(_M_AMD64)
+VOID __cdecl DiskStopFloppyMotor(VOID)
 {
-    WRITE_PORT_UCHAR((PUCHAR)0x3F2, 0);
+    WRITE_PORT_UCHAR((PUCHAR)0x3F2, 0x0C); // DOR_FDC_ENABLE | DOR_DMA_IO_INTERFACE_ENABLE
 }
+#endif // defined __i386__ || defined(_M_AMD64)
 
-BOOLEAN DiskGetExtendedDriveParameters(UCHAR DriveNumber, PVOID Buffer, USHORT BufferSize)
+BOOLEAN
+PcDiskGetDriveGeometry(UCHAR DriveNumber, PGEOMETRY Geometry)
 {
-    REGS RegsIn, RegsOut;
-    PUSHORT Ptr = (PUSHORT)(BIOSCALLBUFFER);
+    PPC_DISK_DRIVE DiskDrive;
 
-    TRACE("DiskGetExtendedDriveParameters()\n");
+    TRACE("PcDiskGetDriveGeometry(0x%x)\n", DriveNumber);
 
-    if (!DiskInt13ExtensionsSupported(DriveNumber))
+    DiskDrive = PcDiskDriveNumberToDrive(DriveNumber);
+    if (!DiskDrive)
         return FALSE;
 
-    /* Initialize transfer buffer */
-    *Ptr = BufferSize;
-
-    /*
-     * BIOS Int 13h, function 48h - Get drive parameters
-     * AH = 48h
-     * DL = drive (bit 7 set for hard disk)
-     * DS:SI = result buffer
-     * Return:
-     * CF set on error
-     * AH = status (07h)
-     * CF clear if successful
-     * AH = 00h
-     * DS:SI -> result buffer
-     */
-    RegsIn.b.ah = 0x48;
-    RegsIn.b.dl = DriveNumber;
-    RegsIn.x.ds = BIOSCALLBUFSEGMENT;   // DS:SI -> result buffer
-    RegsIn.w.si = BIOSCALLBUFOFFSET;
-
-    /* Get drive parameters */
-    Int386(0x13, &RegsIn, &RegsOut);
-    if (!INT386_SUCCESS(RegsOut))
-        return FALSE;
-
-    memcpy(Buffer, Ptr, BufferSize);
-
-#if DBG
-    TRACE("size of buffer:                          %x\n", Ptr[0]);
-    TRACE("information flags:                       %x\n", Ptr[1]);
-    TRACE("number of physical cylinders on drive:   %u\n", *(PULONG)&Ptr[2]);
-    TRACE("number of physical heads on drive:       %u\n", *(PULONG)&Ptr[4]);
-    TRACE("number of physical sectors per track:    %u\n", *(PULONG)&Ptr[6]);
-    TRACE("total number of sectors on drive:        %I64u\n", *(unsigned long long*)&Ptr[8]);
-    TRACE("bytes per sector:                        %u\n", Ptr[12]);
-    if (Ptr[0] >= 0x1e)
+    /* Try to get the extended geometry first */
+    if (DiskDrive->ExtGeometry.Size == sizeof(DiskDrive->ExtGeometry))
     {
-        TRACE("EED configuration parameters:            %x:%x\n", Ptr[13], Ptr[14]);
-        if (Ptr[13] != 0xffff && Ptr[14] != 0xffff)
-        {
-           PUCHAR SpecPtr = (PUCHAR)(ULONG_PTR)((Ptr[13] << 4) + Ptr[14]);
-           TRACE("SpecPtr:                                 %x\n", SpecPtr);
-           TRACE("physical I/O port base address:          %x\n", *(PUSHORT)&SpecPtr[0]);
-           TRACE("disk-drive control port address:         %x\n", *(PUSHORT)&SpecPtr[2]);
-           TRACE("drive flags:                             %x\n", SpecPtr[4]);
-           TRACE("proprietary information:                 %x\n", SpecPtr[5]);
-           TRACE("IRQ for drive:                           %u\n", SpecPtr[6]);
-           TRACE("sector count for multi-sector transfers: %u\n", SpecPtr[7]);
-           TRACE("DMA control:                             %x\n", SpecPtr[8]);
-           TRACE("programmed I/O control:                  %x\n", SpecPtr[9]);
-           TRACE("drive options:                           %x\n", *(PUSHORT)&SpecPtr[10]);
-        }
+        /* Extended geometry has been initialized, return it */
+        Geometry->Cylinders = DiskDrive->ExtGeometry.Cylinders;
+        Geometry->Heads = DiskDrive->ExtGeometry.Heads;
+        Geometry->Sectors = DiskDrive->ExtGeometry.SectorsPerTrack;
+        Geometry->BytesPerSector = DiskDrive->ExtGeometry.BytesPerSector;
     }
-    if (Ptr[0] >= 0x42)
+    else
+    /* Fall back to legacy BIOS geometry */
     {
-        TRACE("signature:                             %x\n", Ptr[15]);
+        *Geometry = DiskDrive->Geometry;
     }
-#endif
 
     return TRUE;
 }
 
-BOOLEAN
-PcDiskGetDriveGeometry(UCHAR DriveNumber, PGEOMETRY Geometry)
+ULONG
+PcDiskGetCacheableBlockCount(UCHAR DriveNumber)
 {
-    EXTENDED_GEOMETRY ExtGeometry;
-    REGS RegsIn, RegsOut;
-    ULONG Cylinders;
+    PPC_DISK_DRIVE DiskDrive;
 
-    TRACE("DiskGetDriveGeometry()\n");
-
-    /* Try to get the extended geometry first */
-    ExtGeometry.Size = sizeof(ExtGeometry);
-    if (DiskGetExtendedDriveParameters(DriveNumber, &ExtGeometry, ExtGeometry.Size))
-    {
-        Geometry->Cylinders = ExtGeometry.Cylinders;
-        Geometry->Heads = ExtGeometry.Heads;
-        Geometry->Sectors = ExtGeometry.SectorsPerTrack;
-        Geometry->BytesPerSector = ExtGeometry.BytesPerSector;
-        return TRUE;
-    }
+    DiskDrive = PcDiskDriveNumberToDrive(DriveNumber);
+    if (!DiskDrive)
+        return 1; // Unknown count.
 
     /*
-     * BIOS Int 13h, function 08h - Get drive parameters
-     * AH = 08h
-     * DL = drive (bit 7 set for hard disk)
-     * ES:DI = 0000h:0000h to guard against BIOS bugs
-     * Return:
-     * CF set on error
-     * AH = status (07h)
-     * CF clear if successful
-     * AH = 00h
-     * AL = 00h on at least some BIOSes
-     * BL = drive type (AT/PS2 floppies only)
-     * CH = low eight bits of maximum cylinder number
-     * CL = maximum sector number (bits 5-0)
-     *      high two bits of maximum cylinder number (bits 7-6)
-     * DH = maximum head number
-     * DL = number of drives
-     * ES:DI -> drive parameter table (floppies only)
+     * If LBA is supported then the block size will be 64 sectors (32k).
+     * If not then the block size is the size of one track.
      */
-    RegsIn.b.ah = 0x08;
-    RegsIn.b.dl = DriveNumber;
-    RegsIn.w.es = 0x0000;
-    RegsIn.w.di = 0x0000;
-
-    /* Get drive parameters */
-    Int386(0x13, &RegsIn, &RegsOut);
-    if (!INT386_SUCCESS(RegsOut))
-        return FALSE;
-
-    Cylinders = (RegsOut.b.cl & 0xC0) << 2;
-    Cylinders += RegsOut.b.ch;
-    Cylinders++;
-    Geometry->Cylinders = Cylinders;
-    Geometry->Heads = RegsOut.b.dh + 1;
-    Geometry->Sectors = RegsOut.b.cl & 0x3F;
-    Geometry->BytesPerSector = 512;     /* Just assume 512 bytes per sector */
-
-    return TRUE;
-}
-
-ULONG
-PcDiskGetCacheableBlockCount(UCHAR DriveNumber)
-{
-    GEOMETRY Geometry;
-
-    /* If LBA is supported then the block size will be 64 sectors (32k)
-     * If not then the block size is the size of one track. */
-    if (DiskInt13ExtensionsSupported(DriveNumber))
-    {
+    if (DiskDrive->Int13ExtensionsSupported)
         return 64;
-    }
-    /* Get the disk geometry. If this fails then we will
-     * just return 1 sector to be safe. */
-    else if (! PcDiskGetDriveGeometry(DriveNumber, &Geometry))
-    {
-        return 1;
-    }
     else
-    {
-        return Geometry.Sectors;
-    }
-}
-
-BOOLEAN
-PcDiskGetBootPath(OUT PCHAR BootPath, IN ULONG Size)
-{
-    // FIXME: Keep it there, or put it in DiskGetBootPath?
-    // Or, abstract the notion of network booting to make
-    // sense for other platforms than the PC (and this idea
-    // already exists), then we would need to check whether
-    // we were booting from network (and: PC --> PXE, etc...)
-    // and if so, set the correct ARC path. But then this new
-    // logic could be moved back to DiskGetBootPath...
-
-    if (*FrldrBootPath)
-    {
-        /* Copy back the buffer */
-        if (Size < strlen(FrldrBootPath) + 1)
-            return FALSE;
-        strncpy(BootPath, FrldrBootPath, Size);
-        return TRUE;
-    }
-
-    // FIXME! FIXME! Do this in some drive recognition procedure!!!!
-    if (PxeInit())
-    {
-        strcpy(BootPath, "net(0)");
-        return TRUE;
-    }
-    return DiskGetBootPath(BootPath, Size);
+        return DiskDrive->Geometry.Sectors;
 }
 
 /* EOF */