1 /** @file 2 FAT file system access routines for FAT recovery PEIM 3 4 Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR> 5 6 This program and the accompanying materials are licensed and made available 7 under the terms and conditions of the BSD License which accompanies this 8 distribution. The full text of the license may be found at 9 http://opensource.org/licenses/bsd-license.php 10 11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 13 14 **/ 15 16 #include "FatLitePeim.h" 17 18 19 /** 20 Check if there is a valid FAT in the corresponding Block device 21 of the volume and if yes, fill in the relevant fields for the 22 volume structure. Note there should be a valid Block device number 23 already set. 24 25 @param PrivateData Global memory map for accessing global 26 variables. 27 @param Volume On input, the BlockDeviceNumber field of the 28 Volume should be a valid value. On successful 29 output, all fields except the VolumeNumber 30 field is initialized. 31 32 @retval EFI_SUCCESS A FAT is found and the volume structure is 33 initialized. 34 @retval EFI_NOT_FOUND There is no FAT on the corresponding device. 35 @retval EFI_DEVICE_ERROR There is something error while accessing device. 36 37 **/ 38 EFI_STATUS 39 FatGetBpbInfo ( 40 IN PEI_FAT_PRIVATE_DATA *PrivateData, 41 IN OUT PEI_FAT_VOLUME *Volume 42 ) 43 { 44 EFI_STATUS Status; 45 PEI_FAT_BOOT_SECTOR Bpb; 46 PEI_FAT_BOOT_SECTOR_EX BpbEx; 47 UINT32 Sectors; 48 UINT32 SectorsPerFat; 49 UINT32 RootDirSectors; 50 UINT64 FatLba; 51 UINT64 RootLba; 52 UINT64 FirstClusterLba; 53 54 // 55 // Read in the BPB 56 // 57 Status = FatReadDisk ( 58 PrivateData, 59 Volume->BlockDeviceNo, 60 0, 61 sizeof (PEI_FAT_BOOT_SECTOR_EX), 62 &BpbEx 63 ); 64 if (EFI_ERROR (Status)) { 65 return Status; 66 } 67 68 CopyMem ( 69 (UINT8 *) (&Bpb), 70 (UINT8 *) (&BpbEx), 71 sizeof (PEI_FAT_BOOT_SECTOR) 72 ); 73 74 Volume->FatType = FatUnknown; 75 76 Sectors = Bpb.Sectors; 77 if (Sectors == 0) { 78 Sectors = Bpb.LargeSectors; 79 } 80 81 SectorsPerFat = Bpb.SectorsPerFat; 82 if (SectorsPerFat == 0) { 83 SectorsPerFat = BpbEx.LargeSectorsPerFat; 84 Volume->FatType = Fat32; 85 } 86 // 87 // Filter out those not a FAT 88 // 89 if (Bpb.Ia32Jump[0] != 0xe9 && Bpb.Ia32Jump[0] != 0xeb && Bpb.Ia32Jump[0] != 0x49) { 90 return EFI_NOT_FOUND; 91 } 92 93 if (Bpb.ReservedSectors == 0 || Bpb.NoFats == 0 || Sectors == 0) { 94 return EFI_NOT_FOUND; 95 } 96 97 if (Bpb.SectorsPerCluster != 1 && 98 Bpb.SectorsPerCluster != 2 && 99 Bpb.SectorsPerCluster != 4 && 100 Bpb.SectorsPerCluster != 8 && 101 Bpb.SectorsPerCluster != 16 && 102 Bpb.SectorsPerCluster != 32 && 103 Bpb.SectorsPerCluster != 64 && 104 Bpb.SectorsPerCluster != 128 105 ) { 106 return EFI_NOT_FOUND; 107 } 108 109 if (Volume->FatType == Fat32 && (SectorsPerFat == 0 || BpbEx.FsVersion != 0)) { 110 return EFI_NOT_FOUND; 111 } 112 113 if (Bpb.Media != 0xf0 && 114 Bpb.Media != 0xf8 && 115 Bpb.Media != 0xf9 && 116 Bpb.Media != 0xfb && 117 Bpb.Media != 0xfc && 118 Bpb.Media != 0xfd && 119 Bpb.Media != 0xfe && 120 Bpb.Media != 0xff && 121 // 122 // FujitsuFMR 123 // 124 Bpb.Media != 0x00 && 125 Bpb.Media != 0x01 && 126 Bpb.Media != 0xfa 127 ) { 128 return EFI_NOT_FOUND; 129 } 130 131 if (Volume->FatType != Fat32 && Bpb.RootEntries == 0) { 132 return EFI_NOT_FOUND; 133 } 134 // 135 // If this is fat32, refuse to mount mirror-disabled volumes 136 // 137 if (Volume->FatType == Fat32 && ((BpbEx.ExtendedFlags & 0x80) != 0)) { 138 return EFI_NOT_FOUND; 139 } 140 // 141 // Fill in the volume structure fields 142 // (Sectors & SectorsPerFat is computed earlier already) 143 // 144 Volume->ClusterSize = Bpb.SectorSize * Bpb.SectorsPerCluster; 145 Volume->RootEntries = Bpb.RootEntries; 146 Volume->SectorSize = Bpb.SectorSize; 147 148 RootDirSectors = ((Volume->RootEntries * sizeof (FAT_DIRECTORY_ENTRY)) + (Volume->SectorSize - 1)) / Volume->SectorSize; 149 150 FatLba = Bpb.ReservedSectors; 151 RootLba = Bpb.NoFats * SectorsPerFat + FatLba; 152 FirstClusterLba = RootLba + RootDirSectors; 153 154 Volume->VolumeSize = MultU64x32 (Sectors, Volume->SectorSize); 155 Volume->FatPos = MultU64x32 (FatLba, Volume->SectorSize); 156 Volume->RootDirPos = MultU64x32 (RootLba, Volume->SectorSize); 157 Volume->FirstClusterPos = MultU64x32 (FirstClusterLba, Volume->SectorSize); 158 Volume->MaxCluster = (UINT32) (Sectors - FirstClusterLba) / Bpb.SectorsPerCluster; 159 Volume->RootDirCluster = BpbEx.RootDirFirstCluster; 160 161 // 162 // If this is not a fat32, determine if it's a fat16 or fat12 163 // 164 if (Volume->FatType != Fat32) { 165 166 if (Volume->MaxCluster >= 65525) { 167 return EFI_NOT_FOUND; 168 } 169 170 Volume->FatType = Volume->MaxCluster < 4085 ? Fat12 : Fat16; 171 } 172 173 return EFI_SUCCESS; 174 } 175 176 177 /** 178 Gets the next cluster in the cluster chain 179 180 @param PrivateData Global memory map for accessing global variables 181 @param Volume The volume 182 @param Cluster The cluster 183 @param NextCluster The cluster number of the next cluster 184 185 @retval EFI_SUCCESS The address is got 186 @retval EFI_INVALID_PARAMETER ClusterNo exceeds the MaxCluster of the volume. 187 @retval EFI_DEVICE_ERROR Read disk error 188 189 **/ 190 EFI_STATUS 191 FatGetNextCluster ( 192 IN PEI_FAT_PRIVATE_DATA *PrivateData, 193 IN PEI_FAT_VOLUME *Volume, 194 IN UINT32 Cluster, 195 OUT UINT32 *NextCluster 196 ) 197 { 198 EFI_STATUS Status; 199 UINT64 FatEntryPos; 200 UINT32 Dummy; 201 202 *NextCluster = 0; 203 204 if (Volume->FatType == Fat32) { 205 FatEntryPos = Volume->FatPos + MultU64x32 (4, Cluster); 206 207 Status = FatReadDisk (PrivateData, Volume->BlockDeviceNo, FatEntryPos, 4, NextCluster); 208 *NextCluster &= 0x0fffffff; 209 210 // 211 // Pad high bits for our FAT_CLUSTER_... macro definitions to work 212 // 213 if ((*NextCluster) >= 0x0ffffff7) { 214 *NextCluster |= (-1 &~0xf); 215 } 216 217 } else if (Volume->FatType == Fat16) { 218 FatEntryPos = Volume->FatPos + MultU64x32 (2, Cluster); 219 220 Status = FatReadDisk (PrivateData, Volume->BlockDeviceNo, FatEntryPos, 2, NextCluster); 221 222 // 223 // Pad high bits for our FAT_CLUSTER_... macro definitions to work 224 // 225 if ((*NextCluster) >= 0xfff7) { 226 *NextCluster |= (-1 &~0xf); 227 } 228 229 } else { 230 FatEntryPos = Volume->FatPos + DivU64x32Remainder (MultU64x32 (3, Cluster), 2, &Dummy); 231 232 Status = FatReadDisk (PrivateData, Volume->BlockDeviceNo, FatEntryPos, 2, NextCluster); 233 234 if ((Cluster & 0x01) != 0) { 235 *NextCluster = (*NextCluster) >> 4; 236 } else { 237 *NextCluster = (*NextCluster) & 0x0fff; 238 } 239 // 240 // Pad high bits for our FAT_CLUSTER_... macro definitions to work 241 // 242 if ((*NextCluster) >= 0x0ff7) { 243 *NextCluster |= (-1 &~0xf); 244 } 245 } 246 247 if (EFI_ERROR (Status)) { 248 return EFI_DEVICE_ERROR; 249 } 250 251 return EFI_SUCCESS; 252 253 } 254 255 256 /** 257 Set a file's CurrentPos and CurrentCluster, then compute StraightReadAmount. 258 259 @param PrivateData the global memory map 260 @param File the file 261 @param Pos the Position which is offset from the file's 262 CurrentPos 263 264 @retval EFI_SUCCESS Success. 265 @retval EFI_INVALID_PARAMETER Pos is beyond file's size. 266 @retval EFI_DEVICE_ERROR Something error while accessing media. 267 268 **/ 269 EFI_STATUS 270 FatSetFilePos ( 271 IN PEI_FAT_PRIVATE_DATA *PrivateData, 272 IN PEI_FAT_FILE *File, 273 IN UINT32 Pos 274 ) 275 { 276 EFI_STATUS Status; 277 UINT32 AlignedPos; 278 UINT32 Offset; 279 UINT32 Cluster; 280 UINT32 PrevCluster; 281 282 if (File->IsFixedRootDir) { 283 284 if (Pos >= MultU64x32 (File->Volume->RootEntries, 32) - File->CurrentPos) { 285 return EFI_INVALID_PARAMETER; 286 } 287 288 File->CurrentPos += Pos; 289 File->StraightReadAmount = (UINT32) (MultU64x32 (File->Volume->RootEntries, 32) - File->CurrentPos); 290 291 } else { 292 293 DivU64x32Remainder (File->CurrentPos, File->Volume->ClusterSize, &Offset); 294 AlignedPos = (UINT32) File->CurrentPos - (UINT32) Offset; 295 296 while 297 ( 298 !FAT_CLUSTER_FUNCTIONAL (File->CurrentCluster) && 299 AlignedPos + File->Volume->ClusterSize <= File->CurrentPos + Pos 300 ) { 301 AlignedPos += File->Volume->ClusterSize; 302 Status = FatGetNextCluster ( 303 PrivateData, 304 File->Volume, 305 File->CurrentCluster, 306 &File->CurrentCluster 307 ); 308 if (EFI_ERROR (Status)) { 309 return EFI_DEVICE_ERROR; 310 } 311 } 312 313 if (FAT_CLUSTER_FUNCTIONAL (File->CurrentCluster)) { 314 return EFI_INVALID_PARAMETER; 315 } 316 317 File->CurrentPos += Pos; 318 // 319 // Calculate the amount of consecutive cluster occupied by the file. 320 // FatReadFile() will use it to read these blocks once. 321 // 322 File->StraightReadAmount = 0; 323 Cluster = File->CurrentCluster; 324 while (!FAT_CLUSTER_FUNCTIONAL (Cluster)) { 325 File->StraightReadAmount += File->Volume->ClusterSize; 326 PrevCluster = Cluster; 327 Status = FatGetNextCluster (PrivateData, File->Volume, Cluster, &Cluster); 328 if (EFI_ERROR (Status)) { 329 return EFI_DEVICE_ERROR; 330 } 331 332 if (Cluster != PrevCluster + 1) { 333 break; 334 } 335 } 336 337 DivU64x32Remainder (File->CurrentPos, File->Volume->ClusterSize, &Offset); 338 File->StraightReadAmount -= (UINT32) Offset; 339 340 } 341 342 return EFI_SUCCESS; 343 } 344 345 346 /** 347 Reads file data. Updates the file's CurrentPos. 348 349 @param PrivateData Global memory map for accessing global variables 350 @param File The file. 351 @param Size The amount of data to read. 352 @param Buffer The buffer storing the data. 353 354 @retval EFI_SUCCESS The data is read. 355 @retval EFI_INVALID_PARAMETER File is invalid. 356 @retval EFI_DEVICE_ERROR Something error while accessing media. 357 358 **/ 359 EFI_STATUS 360 FatReadFile ( 361 IN PEI_FAT_PRIVATE_DATA *PrivateData, 362 IN PEI_FAT_FILE *File, 363 IN UINTN Size, 364 OUT VOID *Buffer 365 ) 366 { 367 EFI_STATUS Status; 368 CHAR8 *BufferPtr; 369 UINT32 Offset; 370 UINT64 PhysicalAddr; 371 UINTN Amount; 372 373 BufferPtr = Buffer; 374 375 if (File->IsFixedRootDir) { 376 // 377 // This is the fixed root dir in FAT12 and FAT16 378 // 379 if (File->CurrentPos + Size > File->Volume->RootEntries * sizeof (FAT_DIRECTORY_ENTRY)) { 380 return EFI_INVALID_PARAMETER; 381 } 382 383 Status = FatReadDisk ( 384 PrivateData, 385 File->Volume->BlockDeviceNo, 386 File->Volume->RootDirPos + File->CurrentPos, 387 Size, 388 Buffer 389 ); 390 File->CurrentPos += (UINT32) Size; 391 return Status; 392 393 } else { 394 395 if ((File->Attributes & FAT_ATTR_DIRECTORY) == 0) { 396 Size = Size < (File->FileSize - File->CurrentPos) ? Size : (UINTN) (File->FileSize - File->CurrentPos); 397 } 398 // 399 // This is a normal cluster based file 400 // 401 while (Size != 0) { 402 DivU64x32Remainder (File->CurrentPos, File->Volume->ClusterSize, &Offset); 403 PhysicalAddr = File->Volume->FirstClusterPos + MultU64x32 (File->Volume->ClusterSize, File->CurrentCluster - 2); 404 405 Amount = File->StraightReadAmount; 406 Amount = Size > Amount ? Amount : Size; 407 Status = FatReadDisk ( 408 PrivateData, 409 File->Volume->BlockDeviceNo, 410 PhysicalAddr + Offset, 411 Amount, 412 BufferPtr 413 ); 414 if (EFI_ERROR (Status)) { 415 return EFI_DEVICE_ERROR; 416 } 417 // 418 // Advance the file's current pos and current cluster 419 // 420 FatSetFilePos (PrivateData, File, (UINT32) Amount); 421 422 BufferPtr += Amount; 423 Size -= Amount; 424 } 425 426 return EFI_SUCCESS; 427 } 428 } 429 430 431 /** 432 This function reads the next item in the parent directory and 433 initializes the output parameter SubFile (CurrentPos is initialized to 0). 434 The function updates the CurrentPos of the parent dir to after the item read. 435 If no more items were found, the function returns EFI_NOT_FOUND. 436 437 @param PrivateData Global memory map for accessing global variables 438 @param ParentDir The parent directory. 439 @param SubFile The File structure containing the sub file that 440 is caught. 441 442 @retval EFI_SUCCESS The next sub file is obtained. 443 @retval EFI_INVALID_PARAMETER The ParentDir is not a directory. 444 @retval EFI_NOT_FOUND No more sub file exists. 445 @retval EFI_DEVICE_ERROR Something error while accessing media. 446 447 **/ 448 EFI_STATUS 449 FatReadNextDirectoryEntry ( 450 IN PEI_FAT_PRIVATE_DATA *PrivateData, 451 IN PEI_FAT_FILE *ParentDir, 452 OUT PEI_FAT_FILE *SubFile 453 ) 454 { 455 EFI_STATUS Status; 456 FAT_DIRECTORY_ENTRY DirEntry; 457 CHAR16 *Pos; 458 CHAR16 BaseName[9]; 459 CHAR16 Ext[4]; 460 461 ZeroMem ((UINT8 *) SubFile, sizeof (PEI_FAT_FILE)); 462 463 // 464 // Pick a valid directory entry 465 // 466 while (1) { 467 // 468 // Read one entry 469 // 470 Status = FatReadFile (PrivateData, ParentDir, 32, &DirEntry); 471 if (EFI_ERROR (Status)) { 472 return EFI_DEVICE_ERROR; 473 } 474 // 475 // We only search for *FILE* in root directory 476 // Long file name entry is *NOT* supported 477 // 478 if (((DirEntry.Attributes & FAT_ATTR_DIRECTORY) == FAT_ATTR_DIRECTORY) || (DirEntry.Attributes == FAT_ATTR_LFN)) { 479 continue; 480 } 481 // 482 // if this is a terminator dir entry, just return EFI_NOT_FOUND 483 // 484 if (DirEntry.FileName[0] == EMPTY_ENTRY_MARK) { 485 return EFI_NOT_FOUND; 486 } 487 // 488 // If this not an invalid entry neither an empty entry, this is what we want. 489 // otherwise we will start a new loop to continue to find something meaningful 490 // 491 if ((UINT8) DirEntry.FileName[0] != DELETE_ENTRY_MARK) { 492 break; 493 } 494 } 495 // 496 // fill in the output parameter 497 // 498 EngFatToStr (8, DirEntry.FileName, BaseName); 499 EngFatToStr (3, DirEntry.FileName + 8, Ext); 500 501 Pos = (UINT16 *) SubFile->FileName; 502 SetMem ((UINT8 *) Pos, FAT_MAX_FILE_NAME_LENGTH, 0); 503 CopyMem ((UINT8 *) Pos, (UINT8 *) BaseName, 2 * (StrLen (BaseName) + 1)); 504 505 if (Ext[0] != 0) { 506 Pos += StrLen (BaseName); 507 *Pos = '.'; 508 Pos++; 509 CopyMem ((UINT8 *) Pos, (UINT8 *) Ext, 2 * (StrLen (Ext) + 1)); 510 } 511 512 SubFile->Attributes = DirEntry.Attributes; 513 SubFile->CurrentCluster = DirEntry.FileCluster; 514 if (ParentDir->Volume->FatType == Fat32) { 515 SubFile->CurrentCluster |= DirEntry.FileClusterHigh << 16; 516 } 517 518 SubFile->CurrentPos = 0; 519 SubFile->FileSize = DirEntry.FileSize; 520 SubFile->StartingCluster = SubFile->CurrentCluster; 521 SubFile->Volume = ParentDir->Volume; 522 523 // 524 // in Pei phase, time parameters do not need to be filled for minimum use. 525 // 526 return Status; 527 } 528