#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
#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)
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);
RegsIn.w.si = BIOSCALLBUFOFFSET;
/* Retry 3 times */
- for (RetryCount=0; RetryCount<3; RetryCount++)
+ for (RetryCount = 0; RetryCount < 3; ++RetryCount)
{
Int386(0x13, &RegsIn, &RegsOut);
}
/* 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;
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.
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);
{
break;
}
- /* If it failed the do the next retry */
+ /* If it failed then do the next retry */
else
{
DiskResetController(DriveNumber);
/* 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;
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 */