Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenpcdisk.c
Go to the documentation of this file.
00001 /* 00002 * FreeLoader 00003 * Copyright (C) 1998-2003 Brian Palmer <brianp@sginet.com> 00004 * 00005 * This program is free software; you can redistribute it and/or modify 00006 * it under the terms of the GNU General Public License as published by 00007 * the Free Software Foundation; either version 2 of the License, or 00008 * (at your option) any later version. 00009 * 00010 * This program is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 * GNU General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU General Public License along 00016 * with this program; if not, write to the Free Software Foundation, Inc., 00017 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 00018 */ 00019 00020 #include <freeldr.h> 00021 #include <debug.h> 00022 00023 DBG_DEFAULT_CHANNEL(DISK); 00024 00025 #include <pshpack2.h> 00026 typedef struct 00027 { 00028 UCHAR PacketSize; // 00h - Size of packet (10h or 18h) 00029 UCHAR Reserved; // 01h - Reserved (0) 00030 USHORT LBABlockCount; // 02h - Number of blocks to transfer (max 007Fh for Phoenix EDD) 00031 USHORT TransferBufferOffset; // 04h - Transfer buffer offset (seg:off) 00032 USHORT TransferBufferSegment; // Transfer buffer segment (seg:off) 00033 ULONGLONG LBAStartBlock; // 08h - Starting absolute block number 00034 //ULONGLONG TransferBuffer64; // 10h - (EDD-3.0, optional) 64-bit flat address of transfer buffer 00035 // used if DWORD at 04h is FFFFh:FFFFh 00036 // Commented since some earlier BIOSes refuse to work with 00037 // such extended structure 00038 } I386_DISK_ADDRESS_PACKET, *PI386_DISK_ADDRESS_PACKET; 00039 #include <poppack.h> 00040 00042 // FUNCTIONS 00044 00045 static BOOLEAN PcDiskResetController(UCHAR DriveNumber) 00046 { 00047 REGS RegsIn; 00048 REGS RegsOut; 00049 00050 WARN("PcDiskResetController(0x%x) DISK OPERATION FAILED -- RESETTING CONTROLLER\n", DriveNumber); 00051 00052 // BIOS Int 13h, function 0 - Reset disk system 00053 // AH = 00h 00054 // DL = drive (if bit 7 is set both hard disks and floppy disks reset) 00055 // Return: 00056 // AH = status 00057 // CF clear if successful 00058 // CF set on error 00059 RegsIn.b.ah = 0x00; 00060 RegsIn.b.dl = DriveNumber; 00061 00062 // Reset the disk controller 00063 Int386(0x13, &RegsIn, &RegsOut); 00064 00065 return INT386_SUCCESS(RegsOut); 00066 } 00067 00068 static BOOLEAN PcDiskReadLogicalSectorsLBA(UCHAR DriveNumber, ULONGLONG SectorNumber, ULONG SectorCount, PVOID Buffer) 00069 { 00070 REGS RegsIn; 00071 REGS RegsOut; 00072 ULONG RetryCount; 00073 PI386_DISK_ADDRESS_PACKET Packet = (PI386_DISK_ADDRESS_PACKET)(BIOSCALLBUFFER); 00074 00075 TRACE("PcDiskReadLogicalSectorsLBA() DriveNumber: 0x%x SectorNumber: %I64d SectorCount: %d Buffer: 0x%x\n", DriveNumber, SectorNumber, SectorCount, Buffer); 00076 ASSERT(((ULONG_PTR)Buffer) <= 0xFFFFF); 00077 00078 // BIOS int 0x13, function 42h - IBM/MS INT 13 Extensions - EXTENDED READ 00079 RegsIn.b.ah = 0x42; // Subfunction 42h 00080 RegsIn.b.dl = DriveNumber; // Drive number in DL (0 - floppy, 0x80 - harddisk) 00081 RegsIn.x.ds = BIOSCALLBUFSEGMENT; // DS:SI -> disk address packet 00082 RegsIn.w.si = BIOSCALLBUFOFFSET; 00083 00084 // Setup disk address packet 00085 RtlZeroMemory(Packet, sizeof(I386_DISK_ADDRESS_PACKET)); 00086 Packet->PacketSize = sizeof(I386_DISK_ADDRESS_PACKET); 00087 Packet->Reserved = 0; 00088 Packet->LBABlockCount = (USHORT)SectorCount; 00089 ASSERT(Packet->LBABlockCount == SectorCount); 00090 Packet->TransferBufferOffset = ((ULONG_PTR)Buffer) & 0x0F; 00091 Packet->TransferBufferSegment = (USHORT)(((ULONG_PTR)Buffer) >> 4); 00092 Packet->LBAStartBlock = SectorNumber; 00093 00094 // BIOS int 0x13, function 42h - IBM/MS INT 13 Extensions - EXTENDED READ 00095 // Return: 00096 // CF clear if successful 00097 // AH = 00h 00098 // CF set on error 00099 // AH = error code 00100 // disk address packet's block count field set to the 00101 // number of blocks successfully transferred 00102 00103 // Retry 3 times 00104 for (RetryCount=0; RetryCount<3; RetryCount++) 00105 { 00106 Int386(0x13, &RegsIn, &RegsOut); 00107 00108 // If it worked return TRUE 00109 if (INT386_SUCCESS(RegsOut)) 00110 { 00111 return TRUE; 00112 } 00113 // If it was a corrected ECC error then the data is still good 00114 else if (RegsOut.b.ah == 0x11) 00115 { 00116 return TRUE; 00117 } 00118 // If it failed the do the next retry 00119 else 00120 { 00121 PcDiskResetController(DriveNumber); 00122 00123 continue; 00124 } 00125 } 00126 00127 // If we get here then the read failed 00128 ERR("Disk Read Failed in LBA mode: %x (DriveNumber: 0x%x SectorNumber: %I64d SectorCount: %d)\n", RegsOut.b.ah, DriveNumber, SectorNumber, SectorCount); 00129 00130 return FALSE; 00131 } 00132 00133 static BOOLEAN PcDiskReadLogicalSectorsCHS(UCHAR DriveNumber, ULONGLONG SectorNumber, ULONG SectorCount, PVOID Buffer) 00134 { 00135 UCHAR PhysicalSector; 00136 UCHAR PhysicalHead; 00137 ULONG PhysicalTrack; 00138 GEOMETRY DriveGeometry; 00139 ULONG NumberOfSectorsToRead; 00140 REGS RegsIn; 00141 REGS RegsOut; 00142 ULONG RetryCount; 00143 00144 TRACE("PcDiskReadLogicalSectorsCHS()\n"); 00145 00146 // 00147 // Get the drive geometry 00148 // 00149 if (!MachDiskGetDriveGeometry(DriveNumber, &DriveGeometry) || 00150 DriveGeometry.Sectors == 0 || 00151 DriveGeometry.Heads == 0) 00152 { 00153 return FALSE; 00154 } 00155 00156 while (SectorCount) 00157 { 00158 00159 // 00160 // Calculate the physical disk offsets 00161 // Note: DriveGeometry.Sectors < 64 00162 // 00163 PhysicalSector = 1 + (UCHAR)(SectorNumber % DriveGeometry.Sectors); 00164 PhysicalHead = (UCHAR)((SectorNumber / DriveGeometry.Sectors) % DriveGeometry.Heads); 00165 PhysicalTrack = (ULONG)((SectorNumber / DriveGeometry.Sectors) / DriveGeometry.Heads); 00166 00167 // 00168 // Calculate how many sectors we need to read this round 00169 // 00170 if (PhysicalSector > 1) 00171 { 00172 if (SectorCount >= (DriveGeometry.Sectors - (PhysicalSector - 1))) 00173 NumberOfSectorsToRead = (DriveGeometry.Sectors - (PhysicalSector - 1)); 00174 else 00175 NumberOfSectorsToRead = SectorCount; 00176 } 00177 else 00178 { 00179 if (SectorCount >= DriveGeometry.Sectors) 00180 NumberOfSectorsToRead = DriveGeometry.Sectors; 00181 else 00182 NumberOfSectorsToRead = SectorCount; 00183 } 00184 00185 // 00186 // Make sure the read is within the geometry boundaries 00187 // 00188 if ((PhysicalHead >= DriveGeometry.Heads) || 00189 (PhysicalTrack >= DriveGeometry.Cylinders) || 00190 ((NumberOfSectorsToRead + PhysicalSector) > (DriveGeometry.Sectors + 1)) || 00191 (PhysicalSector > DriveGeometry.Sectors)) 00192 { 00193 DiskError("Disk read exceeds drive geometry limits.", 0); 00194 return FALSE; 00195 } 00196 00197 // BIOS Int 13h, function 2 - Read Disk Sectors 00198 // AH = 02h 00199 // AL = number of sectors to read (must be nonzero) 00200 // CH = low eight bits of cylinder number 00201 // CL = sector number 1-63 (bits 0-5) 00202 // high two bits of cylinder (bits 6-7, hard disk only) 00203 // DH = head number 00204 // DL = drive number (bit 7 set for hard disk) 00205 // ES:BX -> data buffer 00206 // Return: 00207 // CF set on error 00208 // if AH = 11h (corrected ECC error), AL = burst length 00209 // CF clear if successful 00210 // AH = status 00211 // AL = number of sectors transferred 00212 // (only valid if CF set for some BIOSes) 00213 RegsIn.b.ah = 0x02; 00214 RegsIn.b.al = (UCHAR)NumberOfSectorsToRead; 00215 RegsIn.b.ch = (PhysicalTrack & 0xFF); 00216 RegsIn.b.cl = (UCHAR)(PhysicalSector + ((PhysicalTrack & 0x300) >> 2)); 00217 RegsIn.b.dh = PhysicalHead; 00218 RegsIn.b.dl = DriveNumber; 00219 RegsIn.w.es = (USHORT)(((ULONG_PTR)Buffer) >> 4); 00220 RegsIn.w.bx = ((ULONG_PTR)Buffer) & 0x0F; 00221 00222 // 00223 // Perform the read 00224 // Retry 3 times 00225 // 00226 for (RetryCount=0; RetryCount<3; RetryCount++) 00227 { 00228 Int386(0x13, &RegsIn, &RegsOut); 00229 00230 // If it worked break out 00231 if (INT386_SUCCESS(RegsOut)) 00232 { 00233 break; 00234 } 00235 // If it was a corrected ECC error then the data is still good 00236 else if (RegsOut.b.ah == 0x11) 00237 { 00238 break; 00239 } 00240 // If it failed the do the next retry 00241 else 00242 { 00243 PcDiskResetController(DriveNumber); 00244 00245 continue; 00246 } 00247 } 00248 00249 // If we retried 3 times then fail 00250 if (RetryCount >= 3) 00251 { 00252 ERR("Disk Read Failed in CHS mode, after retrying 3 times: %x\n", RegsOut.b.ah); 00253 return FALSE; 00254 } 00255 00256 // I have learned that not all bioses return 00257 // the sector read count in the AL register (at least mine doesn't) 00258 // even if the sectors were read correctly. So instead 00259 // of checking the sector read count we will rely solely 00260 // on the carry flag being set on error 00261 00262 Buffer = (PVOID)((ULONG_PTR)Buffer + (NumberOfSectorsToRead * DriveGeometry.BytesPerSector)); 00263 SectorCount -= NumberOfSectorsToRead; 00264 SectorNumber += NumberOfSectorsToRead; 00265 } 00266 00267 return TRUE; 00268 } 00269 00270 BOOLEAN PcDiskReadLogicalSectors(UCHAR DriveNumber, ULONGLONG SectorNumber, ULONG SectorCount, PVOID Buffer) 00271 { 00272 BOOLEAN ExtensionsSupported; 00273 00274 TRACE("PcDiskReadLogicalSectors() DriveNumber: 0x%x SectorNumber: %I64d SectorCount: %d Buffer: 0x%x\n", DriveNumber, SectorNumber, SectorCount, Buffer); 00275 00276 // 00277 // Check to see if it is a fixed disk drive 00278 // If so then check to see if Int13 extensions work 00279 // If they do then use them, otherwise default back to BIOS calls 00280 // 00281 ExtensionsSupported = DiskInt13ExtensionsSupported(DriveNumber); 00282 00283 if ((DriveNumber >= 0x80) && ExtensionsSupported) 00284 { 00285 TRACE("Using Int 13 Extensions for read. DiskInt13ExtensionsSupported(%d) = %s\n", DriveNumber, ExtensionsSupported ? "TRUE" : "FALSE"); 00286 00287 // 00288 // LBA is easy, nothing to calculate 00289 // Just do the read 00290 // 00291 return PcDiskReadLogicalSectorsLBA(DriveNumber, SectorNumber, SectorCount, Buffer); 00292 } 00293 else 00294 { 00295 // LBA is not supported default to the CHS calls 00296 return PcDiskReadLogicalSectorsCHS(DriveNumber, SectorNumber, SectorCount, Buffer); 00297 } 00298 00299 return TRUE; 00300 } 00301 00302 BOOLEAN 00303 PcDiskGetDriveGeometry(UCHAR DriveNumber, PGEOMETRY Geometry) 00304 { 00305 EXTENDED_GEOMETRY ExtGeometry; 00306 REGS RegsIn; 00307 REGS RegsOut; 00308 ULONG Cylinders; 00309 00310 TRACE("DiskGetDriveGeometry()\n"); 00311 00312 /* Try to get the extended geometry first */ 00313 ExtGeometry.Size = sizeof(EXTENDED_GEOMETRY); 00314 if (DiskGetExtendedDriveParameters(DriveNumber, &ExtGeometry, ExtGeometry.Size)) 00315 { 00316 Geometry->Cylinders = ExtGeometry.Cylinders; 00317 Geometry->Heads = ExtGeometry.Heads; 00318 Geometry->Sectors = ExtGeometry.SectorsPerTrack; 00319 Geometry->BytesPerSector = ExtGeometry.BytesPerSector; 00320 00321 return TRUE; 00322 } 00323 00324 /* BIOS Int 13h, function 08h - Get drive parameters 00325 * AH = 08h 00326 * DL = drive (bit 7 set for hard disk) 00327 * ES:DI = 0000h:0000h to guard against BIOS bugs 00328 * Return: 00329 * CF set on error 00330 * AH = status (07h) 00331 * CF clear if successful 00332 * AH = 00h 00333 * AL = 00h on at least some BIOSes 00334 * BL = drive type (AT/PS2 floppies only) 00335 * CH = low eight bits of maximum cylinder number 00336 * CL = maximum sector number (bits 5-0) 00337 * high two bits of maximum cylinder number (bits 7-6) 00338 * DH = maximum head number 00339 * DL = number of drives 00340 * ES:DI -> drive parameter table (floppies only) 00341 */ 00342 RegsIn.b.ah = 0x08; 00343 RegsIn.b.dl = DriveNumber; 00344 RegsIn.w.es = 0x0000; 00345 RegsIn.w.di = 0x0000; 00346 00347 /* Get drive parameters */ 00348 Int386(0x13, &RegsIn, &RegsOut); 00349 00350 if (! INT386_SUCCESS(RegsOut)) 00351 { 00352 return FALSE; 00353 } 00354 00355 Cylinders = (RegsOut.b.cl & 0xC0) << 2; 00356 Cylinders += RegsOut.b.ch; 00357 Cylinders++; 00358 Geometry->Cylinders = Cylinders; 00359 Geometry->Heads = RegsOut.b.dh + 1; 00360 Geometry->Sectors = RegsOut.b.cl & 0x3F; 00361 Geometry->BytesPerSector = 512; /* Just assume 512 bytes per sector */ 00362 00363 return TRUE; 00364 } 00365 00366 ULONG 00367 PcDiskGetCacheableBlockCount(UCHAR DriveNumber) 00368 { 00369 GEOMETRY Geometry; 00370 00371 /* If LBA is supported then the block size will be 64 sectors (32k) 00372 * If not then the block size is the size of one track */ 00373 if (DiskInt13ExtensionsSupported(DriveNumber)) 00374 { 00375 return 64; 00376 } 00377 /* Get the disk geometry 00378 * If this fails then we will just return 1 sector to be safe */ 00379 else if (! PcDiskGetDriveGeometry(DriveNumber, &Geometry)) 00380 { 00381 return 1; 00382 } 00383 else 00384 { 00385 return Geometry.Sectors; 00386 } 00387 } 00388 00389 BOOLEAN 00390 PcDiskGetBootPath(char *BootPath, unsigned Size) 00391 { 00392 if (PxeInit()) 00393 { 00394 strcpy(BootPath, "net(0)"); 00395 return 0; 00396 } 00397 return DiskGetBootPath(BootPath, Size); 00398 } 00399 00400 /* EOF */ Generated on Sun May 27 2012 04:19:08 for ReactOS by
1.7.6.1
|