f9fc4f0c1ba3ce94f7e3ced60228f208dfae1ff1
[reactos.git] / boot / freeldr / freeldr / arch / i386 / pcdisk.c
1 /*
2 * FreeLoader
3 * Copyright (C) 1998-2003 Brian Palmer <brianp@sginet.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 // #if defined(__i386__) || defined(_M_AMD64)
21
22 #include <freeldr.h>
23
24 #include <debug.h>
25 DBG_DEFAULT_CHANNEL(DISK);
26
27 #include <pshpack2.h>
28
29 typedef struct
30 {
31 UCHAR PacketSize; // 00h - Size of packet (10h or 18h)
32 UCHAR Reserved; // 01h - Reserved (0)
33 USHORT LBABlockCount; // 02h - Number of blocks to transfer (max 007Fh for Phoenix EDD)
34 USHORT TransferBufferOffset; // 04h - Transfer buffer offset (seg:off)
35 USHORT TransferBufferSegment; // Transfer buffer segment (seg:off)
36 ULONGLONG LBAStartBlock; // 08h - Starting absolute block number
37 // ULONGLONG TransferBuffer64; // 10h - (EDD-3.0, optional) 64-bit flat address of transfer buffer
38 // used if DWORD at 04h is FFFFh:FFFFh
39 // Commented since some earlier BIOSes refuse to work with
40 // such extended structure
41 } I386_DISK_ADDRESS_PACKET, *PI386_DISK_ADDRESS_PACKET;
42
43 typedef struct
44 {
45 UCHAR PacketSize; // 00h - Size of packet in bytes (13h)
46 UCHAR MediaType; // 01h - Boot media type (see #00282)
47 UCHAR DriveNumber; /* 02h - Drive number:
48 * 00h Floppy image
49 * 80h Bootable hard disk
50 * 81h-FFh Nonbootable or no emulation
51 */
52 UCHAR Controller; // 03h - CD-ROM controller number
53 ULONG LBAImage; // 04h - Logical Block Address of disk image to emulate
54 USHORT DeviceSpec; /* 08h - Device specification (see also #00282)
55 * (IDE) Bit 0:
56 * Drive is slave instead of master
57 * (SCSI) Bits 7-0:
58 * LUN and PUN
59 * Bits 15-8:
60 * Bus number
61 */
62 USHORT Buffer; // 0Ah - Segment of 3K buffer for caching CD-ROM reads
63 USHORT LoadSeg; // 0Ch - Load segment for initial boot image.
64 // If 0000h, load at segment 07C0h.
65 USHORT SectorCount; // 0Eh - Number of 512-byte virtual sectors to load
66 // (only valid for AH=4Ch).
67 UCHAR CHSGeometry[3]; /* 10h - Low byte of cylinder count (for INT 13/AH=08h)
68 * 11h - Sector count, high bits of cylinder count (for INT 13/AH=08h)
69 * 12h - Head count (for INT 13/AH=08h)
70 */
71 UCHAR Reserved;
72 } I386_CDROM_SPEC_PACKET, *PI386_CDROM_SPEC_PACKET;
73
74 #include <poppack.h>
75
76 /* DISK IO ERROR SUPPORT *****************************************************/
77
78 static LONG lReportError = 0; // >= 0: display errors; < 0: hide errors.
79
80 LONG DiskReportError(BOOLEAN bShowError)
81 {
82 /* Set the reference count */
83 if (bShowError) ++lReportError;
84 else --lReportError;
85 return lReportError;
86 }
87
88 static PCSTR DiskGetErrorCodeString(ULONG ErrorCode)
89 {
90 switch (ErrorCode)
91 {
92 case 0x00: return "no error";
93 case 0x01: return "bad command passed to driver";
94 case 0x02: return "address mark not found or bad sector";
95 case 0x03: return "diskette write protect error";
96 case 0x04: return "sector not found";
97 case 0x05: return "fixed disk reset failed";
98 case 0x06: return "diskette changed or removed";
99 case 0x07: return "bad fixed disk parameter table";
100 case 0x08: return "DMA overrun";
101 case 0x09: return "DMA access across 64k boundary";
102 case 0x0A: return "bad fixed disk sector flag";
103 case 0x0B: return "bad fixed disk cylinder";
104 case 0x0C: return "unsupported track/invalid media";
105 case 0x0D: return "invalid number of sectors on fixed disk format";
106 case 0x0E: return "fixed disk controlled data address mark detected";
107 case 0x0F: return "fixed disk DMA arbitration level out of range";
108 case 0x10: return "ECC/CRC error on disk read";
109 case 0x11: return "recoverable fixed disk data error, data fixed by ECC";
110 case 0x20: return "controller error (NEC for floppies)";
111 case 0x40: return "seek failure";
112 case 0x80: return "time out, drive not ready";
113 case 0xAA: return "fixed disk drive not ready";
114 case 0xBB: return "fixed disk undefined error";
115 case 0xCC: return "fixed disk write fault on selected drive";
116 case 0xE0: return "fixed disk status error/Error reg = 0";
117 case 0xFF: return "sense operation failed";
118
119 default: return "unknown error code";
120 }
121 }
122
123 static VOID DiskError(PCSTR ErrorString, ULONG ErrorCode)
124 {
125 CHAR ErrorCodeString[200];
126
127 if (lReportError < 0)
128 return;
129
130 sprintf(ErrorCodeString, "%s\n\nError Code: 0x%lx\nError: %s",
131 ErrorString, ErrorCode, DiskGetErrorCodeString(ErrorCode));
132
133 TRACE("%s\n", ErrorCodeString);
134
135 UiMessageBox(ErrorCodeString);
136 }
137
138 /* FUNCTIONS *****************************************************************/
139
140 BOOLEAN DiskResetController(UCHAR DriveNumber)
141 {
142 REGS RegsIn, RegsOut;
143
144 WARN("DiskResetController(0x%x) DISK OPERATION FAILED -- RESETTING CONTROLLER\n", DriveNumber);
145
146 /*
147 * BIOS Int 13h, function 0 - Reset disk system
148 * AH = 00h
149 * DL = drive (if bit 7 is set both hard disks and floppy disks reset)
150 * Return:
151 * AH = status
152 * CF clear if successful
153 * CF set on error
154 */
155 RegsIn.b.ah = 0x00;
156 RegsIn.b.dl = DriveNumber;
157
158 /* Reset the disk controller */
159 Int386(0x13, &RegsIn, &RegsOut);
160
161 return INT386_SUCCESS(RegsOut);
162 }
163
164 static BOOLEAN PcDiskReadLogicalSectorsLBA(UCHAR DriveNumber, ULONGLONG SectorNumber, ULONG SectorCount, PVOID Buffer)
165 {
166 REGS RegsIn, RegsOut;
167 ULONG RetryCount;
168 PI386_DISK_ADDRESS_PACKET Packet = (PI386_DISK_ADDRESS_PACKET)(BIOSCALLBUFFER);
169
170 TRACE("PcDiskReadLogicalSectorsLBA() DriveNumber: 0x%x SectorNumber: %I64d SectorCount: %d Buffer: 0x%x\n", DriveNumber, SectorNumber, SectorCount, Buffer);
171 ASSERT(((ULONG_PTR)Buffer) <= 0xFFFFF);
172
173 /* Setup disk address packet */
174 RtlZeroMemory(Packet, sizeof(*Packet));
175 Packet->PacketSize = sizeof(*Packet);
176 Packet->Reserved = 0;
177 Packet->LBABlockCount = (USHORT)SectorCount;
178 ASSERT(Packet->LBABlockCount == SectorCount);
179 Packet->TransferBufferOffset = ((ULONG_PTR)Buffer) & 0x0F;
180 Packet->TransferBufferSegment = (USHORT)(((ULONG_PTR)Buffer) >> 4);
181 Packet->LBAStartBlock = SectorNumber;
182
183 /*
184 * BIOS int 0x13, function 42h - IBM/MS INT 13 Extensions - EXTENDED READ
185 * Return:
186 * CF clear if successful
187 * AH = 00h
188 * CF set on error
189 * AH = error code
190 * Disk address packet's block count field set to the
191 * number of blocks successfully transferred.
192 */
193 RegsIn.b.ah = 0x42; // Subfunction 42h
194 RegsIn.b.dl = DriveNumber; // Drive number in DL (0 - floppy, 0x80 - harddisk)
195 RegsIn.x.ds = BIOSCALLBUFSEGMENT; // DS:SI -> disk address packet
196 RegsIn.w.si = BIOSCALLBUFOFFSET;
197
198 /* Retry 3 times */
199 for (RetryCount=0; RetryCount<3; RetryCount++)
200 {
201 Int386(0x13, &RegsIn, &RegsOut);
202
203 /* If it worked return TRUE */
204 if (INT386_SUCCESS(RegsOut))
205 {
206 return TRUE;
207 }
208 /* If it was a corrected ECC error then the data is still good */
209 else if (RegsOut.b.ah == 0x11)
210 {
211 return TRUE;
212 }
213 /* If it failed then do the next retry */
214 else
215 {
216 DiskResetController(DriveNumber);
217 continue;
218 }
219 }
220
221 /* If we get here then the read failed */
222 DiskError("Disk Read Failed in LBA mode", RegsOut.b.ah);
223 ERR("Disk Read Failed in LBA mode: %x (%s) (DriveNumber: 0x%x SectorNumber: %I64d SectorCount: %d)\n",
224 RegsOut.b.ah, DiskGetErrorCodeString(RegsOut.b.ah),
225 DriveNumber, SectorNumber, SectorCount);
226
227 return FALSE;
228 }
229
230 static BOOLEAN PcDiskReadLogicalSectorsCHS(UCHAR DriveNumber, ULONGLONG SectorNumber, ULONG SectorCount, PVOID Buffer)
231 {
232 UCHAR PhysicalSector;
233 UCHAR PhysicalHead;
234 ULONG PhysicalTrack;
235 GEOMETRY DriveGeometry;
236 ULONG NumberOfSectorsToRead;
237 REGS RegsIn, RegsOut;
238 ULONG RetryCount;
239
240 TRACE("PcDiskReadLogicalSectorsCHS()\n");
241
242 /* Get the drive geometry */
243 //
244 // TODO: Cache this information for the given drive.
245 //
246 if (!PcDiskGetDriveGeometry(DriveNumber, &DriveGeometry) ||
247 DriveGeometry.Sectors == 0 || DriveGeometry.Heads == 0)
248 {
249 return FALSE;
250 }
251
252 while (SectorCount)
253 {
254 /*
255 * Calculate the physical disk offsets.
256 * Note: DriveGeometry.Sectors < 64
257 */
258 PhysicalSector = 1 + (UCHAR)(SectorNumber % DriveGeometry.Sectors);
259 PhysicalHead = (UCHAR)((SectorNumber / DriveGeometry.Sectors) % DriveGeometry.Heads);
260 PhysicalTrack = (ULONG)((SectorNumber / DriveGeometry.Sectors) / DriveGeometry.Heads);
261
262 /* Calculate how many sectors we need to read this round */
263 if (PhysicalSector > 1)
264 {
265 if (SectorCount >= (DriveGeometry.Sectors - (PhysicalSector - 1)))
266 NumberOfSectorsToRead = (DriveGeometry.Sectors - (PhysicalSector - 1));
267 else
268 NumberOfSectorsToRead = SectorCount;
269 }
270 else
271 {
272 if (SectorCount >= DriveGeometry.Sectors)
273 NumberOfSectorsToRead = DriveGeometry.Sectors;
274 else
275 NumberOfSectorsToRead = SectorCount;
276 }
277
278 /* Make sure the read is within the geometry boundaries */
279 if ((PhysicalHead >= DriveGeometry.Heads) ||
280 (PhysicalTrack >= DriveGeometry.Cylinders) ||
281 ((NumberOfSectorsToRead + PhysicalSector) > (DriveGeometry.Sectors + 1)) ||
282 (PhysicalSector > DriveGeometry.Sectors))
283 {
284 DiskError("Disk read exceeds drive geometry limits.", 0);
285 return FALSE;
286 }
287
288 /*
289 * BIOS Int 13h, function 2 - Read Disk Sectors
290 * AH = 02h
291 * AL = number of sectors to read (must be nonzero)
292 * CH = low eight bits of cylinder number
293 * CL = sector number 1-63 (bits 0-5)
294 * high two bits of cylinder (bits 6-7, hard disk only)
295 * DH = head number
296 * DL = drive number (bit 7 set for hard disk)
297 * ES:BX -> data buffer
298 * Return:
299 * CF set on error
300 * if AH = 11h (corrected ECC error), AL = burst length
301 * CF clear if successful
302 * AH = status
303 * AL = number of sectors transferred
304 * (only valid if CF set for some BIOSes)
305 */
306 RegsIn.b.ah = 0x02;
307 RegsIn.b.al = (UCHAR)NumberOfSectorsToRead;
308 RegsIn.b.ch = (PhysicalTrack & 0xFF);
309 RegsIn.b.cl = (UCHAR)(PhysicalSector + ((PhysicalTrack & 0x300) >> 2));
310 RegsIn.b.dh = PhysicalHead;
311 RegsIn.b.dl = DriveNumber;
312 RegsIn.w.es = (USHORT)(((ULONG_PTR)Buffer) >> 4);
313 RegsIn.w.bx = ((ULONG_PTR)Buffer) & 0x0F;
314
315 /* Perform the read. Retry 3 times. */
316 for (RetryCount=0; RetryCount<3; RetryCount++)
317 {
318 Int386(0x13, &RegsIn, &RegsOut);
319
320 /* If it worked break out */
321 if (INT386_SUCCESS(RegsOut))
322 {
323 break;
324 }
325 /* If it was a corrected ECC error then the data is still good */
326 else if (RegsOut.b.ah == 0x11)
327 {
328 break;
329 }
330 /* If it failed the do the next retry */
331 else
332 {
333 DiskResetController(DriveNumber);
334 continue;
335 }
336 }
337
338 /* If we retried 3 times then fail */
339 if (RetryCount >= 3)
340 {
341 DiskError("Disk Read Failed in CHS mode, after retrying 3 times", RegsOut.b.ah);
342 ERR("Disk Read Failed in CHS mode, after retrying 3 times: %x (%s) (DriveNumber: 0x%x SectorNumber: %I64d SectorCount: %d)\n",
343 RegsOut.b.ah, DiskGetErrorCodeString(RegsOut.b.ah),
344 DriveNumber, SectorNumber, SectorCount);
345 return FALSE;
346 }
347
348 // I have learned that not all BIOSes return
349 // the sector read count in the AL register (at least mine doesn't)
350 // even if the sectors were read correctly. So instead
351 // of checking the sector read count we will rely solely
352 // on the carry flag being set on error
353
354 Buffer = (PVOID)((ULONG_PTR)Buffer + (NumberOfSectorsToRead * DriveGeometry.BytesPerSector));
355 SectorCount -= NumberOfSectorsToRead;
356 SectorNumber += NumberOfSectorsToRead;
357 }
358
359 return TRUE;
360 }
361
362 static BOOLEAN DiskInt13ExtensionsSupported(UCHAR DriveNumber)
363 {
364 static UCHAR LastDriveNumber = 0xff;
365 static BOOLEAN LastSupported;
366 REGS RegsIn, RegsOut;
367
368 TRACE("DiskInt13ExtensionsSupported()\n");
369
370 if (DriveNumber == LastDriveNumber)
371 {
372 TRACE("Using cached value %s for drive 0x%x\n",
373 LastSupported ? "TRUE" : "FALSE", DriveNumber);
374 return LastSupported;
375 }
376
377 /*
378 * Some BIOSes report that extended disk access functions are not supported
379 * when booting from a CD (e.g. Phoenix BIOS v6.00PG and Insyde BIOS shipping
380 * with Intel Macs). Therefore we just return TRUE if we're booting from a CD -
381 * we can assume that all El Torito capable BIOSes support INT 13 extensions.
382 * We simply detect whether we're booting from CD by checking whether the drive
383 * number is >= 0x8A. It's 0x90 on the Insyde BIOS, and 0x9F on most other BIOSes.
384 */
385 if (DriveNumber >= 0x8A)
386 {
387 LastSupported = TRUE;
388 return TRUE;
389 }
390
391 LastDriveNumber = DriveNumber;
392
393 /*
394 * IBM/MS INT 13 Extensions - INSTALLATION CHECK
395 * AH = 41h
396 * BX = 55AAh
397 * DL = drive (80h-FFh)
398 * Return:
399 * CF set on error (extensions not supported)
400 * AH = 01h (invalid function)
401 * CF clear if successful
402 * BX = AA55h if installed
403 * AH = major version of extensions
404 * 01h = 1.x
405 * 20h = 2.0 / EDD-1.0
406 * 21h = 2.1 / EDD-1.1
407 * 30h = EDD-3.0
408 * AL = internal use
409 * CX = API subset support bitmap
410 * DH = extension version (v2.0+ ??? -- not present in 1.x)
411 *
412 * Bitfields for IBM/MS INT 13 Extensions API support bitmap
413 * Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported
414 * Bit 1, removable drive controller functions (AH=45h,46h,48h,49h,INT 15/AH=52h) supported
415 * Bit 2, enhanced disk drive (EDD) functions (AH=48h,AH=4Eh) supported
416 * extended drive parameter table is valid
417 * Bits 3-15 reserved
418 */
419 RegsIn.b.ah = 0x41;
420 RegsIn.w.bx = 0x55AA;
421 RegsIn.b.dl = DriveNumber;
422
423 /* Reset the disk controller */
424 Int386(0x13, &RegsIn, &RegsOut);
425
426 if (!INT386_SUCCESS(RegsOut))
427 {
428 /* CF set on error (extensions not supported) */
429 LastSupported = FALSE;
430 return FALSE;
431 }
432
433 if (RegsOut.w.bx != 0xAA55)
434 {
435 /* BX = AA55h if installed */
436 LastSupported = FALSE;
437 return FALSE;
438 }
439
440 if (!(RegsOut.w.cx & 0x0001))
441 {
442 /*
443 * CX = API subset support bitmap.
444 * Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported.
445 */
446 DbgPrint("Suspicious API subset support bitmap 0x%x on device 0x%lx\n",
447 RegsOut.w.cx, DriveNumber);
448 LastSupported = FALSE;
449 return FALSE;
450 }
451
452 LastSupported = TRUE;
453 return TRUE;
454 }
455
456 BOOLEAN PcDiskReadLogicalSectors(UCHAR DriveNumber, ULONGLONG SectorNumber, ULONG SectorCount, PVOID Buffer)
457 {
458 BOOLEAN ExtensionsSupported;
459
460 TRACE("PcDiskReadLogicalSectors() DriveNumber: 0x%x SectorNumber: %I64d SectorCount: %d Buffer: 0x%x\n",
461 DriveNumber, SectorNumber, SectorCount, Buffer);
462
463 /*
464 * Check to see if it is a fixed disk drive.
465 * If so then check to see if Int13 extensions work.
466 * If they do then use them, otherwise default back to BIOS calls.
467 */
468 ExtensionsSupported = DiskInt13ExtensionsSupported(DriveNumber);
469
470 if ((DriveNumber >= 0x80) && ExtensionsSupported)
471 {
472 TRACE("Using Int 13 Extensions for read. DiskInt13ExtensionsSupported(%d) = %s\n", DriveNumber, ExtensionsSupported ? "TRUE" : "FALSE");
473
474 /* LBA is easy, nothing to calculate. Just do the read. */
475 return PcDiskReadLogicalSectorsLBA(DriveNumber, SectorNumber, SectorCount, Buffer);
476 }
477 else
478 {
479 /* LBA is not supported default to the CHS calls */
480 return PcDiskReadLogicalSectorsCHS(DriveNumber, SectorNumber, SectorCount, Buffer);
481 }
482
483 return TRUE;
484 }
485
486 #if defined(__i386__) || defined(_M_AMD64)
487 VOID __cdecl DiskStopFloppyMotor(VOID)
488 {
489 WRITE_PORT_UCHAR((PUCHAR)0x3F2, 0x0C); // DOR_FDC_ENABLE | DOR_DMA_IO_INTERFACE_ENABLE
490 }
491 #endif // defined __i386__ || defined(_M_AMD64)
492
493 BOOLEAN DiskGetExtendedDriveParameters(UCHAR DriveNumber, PVOID Buffer, USHORT BufferSize)
494 {
495 REGS RegsIn, RegsOut;
496 PUSHORT Ptr = (PUSHORT)(BIOSCALLBUFFER);
497
498 TRACE("DiskGetExtendedDriveParameters()\n");
499
500 if (!DiskInt13ExtensionsSupported(DriveNumber))
501 return FALSE;
502
503 /* Initialize transfer buffer */
504 *Ptr = BufferSize;
505
506 /*
507 * BIOS Int 13h, function 48h - Get drive parameters
508 * AH = 48h
509 * DL = drive (bit 7 set for hard disk)
510 * DS:SI = result buffer
511 * Return:
512 * CF set on error
513 * AH = status (07h)
514 * CF clear if successful
515 * AH = 00h
516 * DS:SI -> result buffer
517 */
518 RegsIn.b.ah = 0x48;
519 RegsIn.b.dl = DriveNumber;
520 RegsIn.x.ds = BIOSCALLBUFSEGMENT; // DS:SI -> result buffer
521 RegsIn.w.si = BIOSCALLBUFOFFSET;
522
523 /* Get drive parameters */
524 Int386(0x13, &RegsIn, &RegsOut);
525 if (!INT386_SUCCESS(RegsOut))
526 return FALSE;
527
528 memcpy(Buffer, Ptr, BufferSize);
529
530 #if DBG
531 TRACE("size of buffer: %x\n", Ptr[0]);
532 TRACE("information flags: %x\n", Ptr[1]);
533 TRACE("number of physical cylinders on drive: %u\n", *(PULONG)&Ptr[2]);
534 TRACE("number of physical heads on drive: %u\n", *(PULONG)&Ptr[4]);
535 TRACE("number of physical sectors per track: %u\n", *(PULONG)&Ptr[6]);
536 TRACE("total number of sectors on drive: %I64u\n", *(unsigned long long*)&Ptr[8]);
537 TRACE("bytes per sector: %u\n", Ptr[12]);
538 if (Ptr[0] >= 0x1e)
539 {
540 TRACE("EED configuration parameters: %x:%x\n", Ptr[13], Ptr[14]);
541 if (Ptr[13] != 0xffff && Ptr[14] != 0xffff)
542 {
543 PUCHAR SpecPtr = (PUCHAR)(ULONG_PTR)((Ptr[13] << 4) + Ptr[14]);
544 TRACE("SpecPtr: %x\n", SpecPtr);
545 TRACE("physical I/O port base address: %x\n", *(PUSHORT)&SpecPtr[0]);
546 TRACE("disk-drive control port address: %x\n", *(PUSHORT)&SpecPtr[2]);
547 TRACE("drive flags: %x\n", SpecPtr[4]);
548 TRACE("proprietary information: %x\n", SpecPtr[5]);
549 TRACE("IRQ for drive: %u\n", SpecPtr[6]);
550 TRACE("sector count for multi-sector transfers: %u\n", SpecPtr[7]);
551 TRACE("DMA control: %x\n", SpecPtr[8]);
552 TRACE("programmed I/O control: %x\n", SpecPtr[9]);
553 TRACE("drive options: %x\n", *(PUSHORT)&SpecPtr[10]);
554 }
555 }
556 if (Ptr[0] >= 0x42)
557 {
558 TRACE("signature: %x\n", Ptr[15]);
559 }
560 #endif
561
562 return TRUE;
563 }
564
565 BOOLEAN
566 PcDiskGetDriveGeometry(UCHAR DriveNumber, PGEOMETRY Geometry)
567 {
568 EXTENDED_GEOMETRY ExtGeometry;
569 REGS RegsIn, RegsOut;
570 ULONG Cylinders;
571
572 TRACE("DiskGetDriveGeometry()\n");
573
574 /* Try to get the extended geometry first */
575 ExtGeometry.Size = sizeof(ExtGeometry);
576 if (DiskGetExtendedDriveParameters(DriveNumber, &ExtGeometry, ExtGeometry.Size))
577 {
578 Geometry->Cylinders = ExtGeometry.Cylinders;
579 Geometry->Heads = ExtGeometry.Heads;
580 Geometry->Sectors = ExtGeometry.SectorsPerTrack;
581 Geometry->BytesPerSector = ExtGeometry.BytesPerSector;
582 return TRUE;
583 }
584
585 /*
586 * BIOS Int 13h, function 08h - Get drive parameters
587 * AH = 08h
588 * DL = drive (bit 7 set for hard disk)
589 * ES:DI = 0000h:0000h to guard against BIOS bugs
590 * Return:
591 * CF set on error
592 * AH = status (07h)
593 * CF clear if successful
594 * AH = 00h
595 * AL = 00h on at least some BIOSes
596 * BL = drive type (AT/PS2 floppies only)
597 * CH = low eight bits of maximum cylinder number
598 * CL = maximum sector number (bits 5-0)
599 * high two bits of maximum cylinder number (bits 7-6)
600 * DH = maximum head number
601 * DL = number of drives
602 * ES:DI -> drive parameter table (floppies only)
603 */
604 RegsIn.b.ah = 0x08;
605 RegsIn.b.dl = DriveNumber;
606 RegsIn.w.es = 0x0000;
607 RegsIn.w.di = 0x0000;
608
609 /* Get drive parameters */
610 Int386(0x13, &RegsIn, &RegsOut);
611 if (!INT386_SUCCESS(RegsOut))
612 return FALSE;
613
614 Cylinders = (RegsOut.b.cl & 0xC0) << 2;
615 Cylinders += RegsOut.b.ch;
616 Cylinders++;
617 Geometry->Cylinders = Cylinders;
618 Geometry->Heads = RegsOut.b.dh + 1;
619 Geometry->Sectors = RegsOut.b.cl & 0x3F;
620 Geometry->BytesPerSector = 512; /* Just assume 512 bytes per sector */
621
622 return TRUE;
623 }
624
625 ULONG
626 PcDiskGetCacheableBlockCount(UCHAR DriveNumber)
627 {
628 GEOMETRY Geometry;
629
630 /* If LBA is supported then the block size will be 64 sectors (32k)
631 * If not then the block size is the size of one track. */
632 if (DiskInt13ExtensionsSupported(DriveNumber))
633 {
634 return 64;
635 }
636 /* Get the disk geometry. If this fails then we will
637 * just return 1 sector to be safe. */
638 else if (!PcDiskGetDriveGeometry(DriveNumber, &Geometry))
639 {
640 return 1;
641 }
642 else
643 {
644 return Geometry.Sectors;
645 }
646 }
647
648 /* EOF */