2 FAT file system access routines for FAT recovery PEIM
4 Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.<BR>
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
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.
16 #include "FatLitePeim.h"
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
25 @param PrivateData Global memory map for accessing global
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
32 @retval EFI_SUCCESS A FAT is found and the volume structure is
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.
40 IN PEI_FAT_PRIVATE_DATA
*PrivateData
,
41 IN OUT PEI_FAT_VOLUME
*Volume
45 PEI_FAT_BOOT_SECTOR Bpb
;
46 PEI_FAT_BOOT_SECTOR_EX BpbEx
;
49 UINT32 RootDirSectors
;
52 UINT64 FirstClusterLba
;
57 Status
= FatReadDisk (
59 Volume
->BlockDeviceNo
,
61 sizeof (PEI_FAT_BOOT_SECTOR_EX
),
64 if (EFI_ERROR (Status
)) {
71 sizeof (PEI_FAT_BOOT_SECTOR
)
74 Volume
->FatType
= FatUnknown
;
76 Sectors
= Bpb
.Sectors
;
78 Sectors
= Bpb
.LargeSectors
;
81 SectorsPerFat
= Bpb
.SectorsPerFat
;
82 if (SectorsPerFat
== 0) {
83 SectorsPerFat
= BpbEx
.LargeSectorsPerFat
;
84 Volume
->FatType
= Fat32
;
87 // Filter out those not a FAT
89 if (Bpb
.Ia32Jump
[0] != 0xe9 && Bpb
.Ia32Jump
[0] != 0xeb && Bpb
.Ia32Jump
[0] != 0x49) {
93 if (Bpb
.ReservedSectors
== 0 || Bpb
.NoFats
== 0 || Sectors
== 0) {
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
106 return EFI_NOT_FOUND
;
109 if (Volume
->FatType
== Fat32
&& (SectorsPerFat
== 0 || BpbEx
.FsVersion
!= 0)) {
110 return EFI_NOT_FOUND
;
113 if (Bpb
.Media
!= 0xf0 &&
128 return EFI_NOT_FOUND
;
131 if (Volume
->FatType
!= Fat32
&& Bpb
.RootEntries
== 0) {
132 return EFI_NOT_FOUND
;
135 // If this is fat32, refuse to mount mirror-disabled volumes
137 if (Volume
->FatType
== Fat32
&& ((BpbEx
.ExtendedFlags
& 0x80) != 0)) {
138 return EFI_NOT_FOUND
;
141 // Fill in the volume structure fields
142 // (Sectors & SectorsPerFat is computed earlier already)
144 Volume
->ClusterSize
= Bpb
.SectorSize
* Bpb
.SectorsPerCluster
;
145 Volume
->RootEntries
= Bpb
.RootEntries
;
146 Volume
->SectorSize
= Bpb
.SectorSize
;
148 RootDirSectors
= ((Volume
->RootEntries
* sizeof (FAT_DIRECTORY_ENTRY
)) + (Volume
->SectorSize
- 1)) / Volume
->SectorSize
;
150 FatLba
= Bpb
.ReservedSectors
;
151 RootLba
= Bpb
.NoFats
* SectorsPerFat
+ FatLba
;
152 FirstClusterLba
= RootLba
+ RootDirSectors
;
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
;
162 // If this is not a fat32, determine if it's a fat16 or fat12
164 if (Volume
->FatType
!= Fat32
) {
166 if (Volume
->MaxCluster
>= 65525) {
167 return EFI_NOT_FOUND
;
170 Volume
->FatType
= Volume
->MaxCluster
< 4085 ? Fat12
: Fat16
;
178 Gets the next cluster in the cluster chain
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
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
192 IN PEI_FAT_PRIVATE_DATA
*PrivateData
,
193 IN PEI_FAT_VOLUME
*Volume
,
195 OUT UINT32
*NextCluster
204 if (Volume
->FatType
== Fat32
) {
205 FatEntryPos
= Volume
->FatPos
+ MultU64x32 (4, Cluster
);
207 Status
= FatReadDisk (PrivateData
, Volume
->BlockDeviceNo
, FatEntryPos
, 4, NextCluster
);
208 *NextCluster
&= 0x0fffffff;
211 // Pad high bits for our FAT_CLUSTER_... macro definitions to work
213 if ((*NextCluster
) >= 0x0ffffff7) {
214 *NextCluster
|= (-1 &~0xf);
217 } else if (Volume
->FatType
== Fat16
) {
218 FatEntryPos
= Volume
->FatPos
+ MultU64x32 (2, Cluster
);
220 Status
= FatReadDisk (PrivateData
, Volume
->BlockDeviceNo
, FatEntryPos
, 2, NextCluster
);
223 // Pad high bits for our FAT_CLUSTER_... macro definitions to work
225 if ((*NextCluster
) >= 0xfff7) {
226 *NextCluster
|= (-1 &~0xf);
230 FatEntryPos
= Volume
->FatPos
+ DivU64x32Remainder (MultU64x32 (3, Cluster
), 2, &Dummy
);
232 Status
= FatReadDisk (PrivateData
, Volume
->BlockDeviceNo
, FatEntryPos
, 2, NextCluster
);
234 if ((Cluster
& 0x01) != 0) {
235 *NextCluster
= (*NextCluster
) >> 4;
237 *NextCluster
= (*NextCluster
) & 0x0fff;
240 // Pad high bits for our FAT_CLUSTER_... macro definitions to work
242 if ((*NextCluster
) >= 0x0ff7) {
243 *NextCluster
|= (-1 &~0xf);
247 if (EFI_ERROR (Status
)) {
248 return EFI_DEVICE_ERROR
;
257 Set a file's CurrentPos and CurrentCluster, then compute StraightReadAmount.
259 @param PrivateData the global memory map
261 @param Pos the Position which is offset from the file's
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.
271 IN PEI_FAT_PRIVATE_DATA
*PrivateData
,
272 IN PEI_FAT_FILE
*File
,
282 if (File
->IsFixedRootDir
) {
284 if (Pos
>= MultU64x32 (File
->Volume
->RootEntries
, 32) - File
->CurrentPos
) {
285 return EFI_INVALID_PARAMETER
;
288 File
->CurrentPos
+= Pos
;
289 File
->StraightReadAmount
= (UINT32
) (MultU64x32 (File
->Volume
->RootEntries
, 32) - File
->CurrentPos
);
293 DivU64x32Remainder (File
->CurrentPos
, File
->Volume
->ClusterSize
, &Offset
);
294 AlignedPos
= (UINT32
) File
->CurrentPos
- (UINT32
) Offset
;
298 !FAT_CLUSTER_FUNCTIONAL (File
->CurrentCluster
) &&
299 AlignedPos
+ File
->Volume
->ClusterSize
<= File
->CurrentPos
+ Pos
301 AlignedPos
+= File
->Volume
->ClusterSize
;
302 Status
= FatGetNextCluster (
305 File
->CurrentCluster
,
306 &File
->CurrentCluster
308 if (EFI_ERROR (Status
)) {
309 return EFI_DEVICE_ERROR
;
313 if (FAT_CLUSTER_FUNCTIONAL (File
->CurrentCluster
)) {
314 return EFI_INVALID_PARAMETER
;
317 File
->CurrentPos
+= Pos
;
319 // Calculate the amount of consecutive cluster occupied by the file.
320 // FatReadFile() will use it to read these blocks once.
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
;
332 if (Cluster
!= PrevCluster
+ 1) {
337 DivU64x32Remainder (File
->CurrentPos
, File
->Volume
->ClusterSize
, &Offset
);
338 File
->StraightReadAmount
-= (UINT32
) Offset
;
347 Reads file data. Updates the file's CurrentPos.
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.
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.
361 IN PEI_FAT_PRIVATE_DATA
*PrivateData
,
362 IN PEI_FAT_FILE
*File
,
375 if (File
->IsFixedRootDir
) {
377 // This is the fixed root dir in FAT12 and FAT16
379 if (File
->CurrentPos
+ Size
> File
->Volume
->RootEntries
* sizeof (FAT_DIRECTORY_ENTRY
)) {
380 return EFI_INVALID_PARAMETER
;
383 Status
= FatReadDisk (
385 File
->Volume
->BlockDeviceNo
,
386 File
->Volume
->RootDirPos
+ File
->CurrentPos
,
390 File
->CurrentPos
+= (UINT32
) Size
;
395 if ((File
->Attributes
& FAT_ATTR_DIRECTORY
) == 0) {
396 Size
= Size
< (File
->FileSize
- File
->CurrentPos
) ? Size
: (File
->FileSize
- File
->CurrentPos
);
399 // This is a normal cluster based file
402 DivU64x32Remainder (File
->CurrentPos
, File
->Volume
->ClusterSize
, &Offset
);
403 PhysicalAddr
= File
->Volume
->FirstClusterPos
+ MultU64x32 (File
->Volume
->ClusterSize
, File
->CurrentCluster
- 2);
405 Amount
= File
->StraightReadAmount
;
406 Amount
= Size
> Amount
? Amount
: Size
;
407 Status
= FatReadDisk (
409 File
->Volume
->BlockDeviceNo
,
410 PhysicalAddr
+ Offset
,
414 if (EFI_ERROR (Status
)) {
415 return EFI_DEVICE_ERROR
;
418 // Advance the file's current pos and current cluster
420 FatSetFilePos (PrivateData
, File
, (UINT32
) Amount
);
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.
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
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.
449 FatReadNextDirectoryEntry (
450 IN PEI_FAT_PRIVATE_DATA
*PrivateData
,
451 IN PEI_FAT_FILE
*ParentDir
,
452 OUT PEI_FAT_FILE
*SubFile
456 FAT_DIRECTORY_ENTRY DirEntry
;
461 ZeroMem ((UINT8
*) SubFile
, sizeof (PEI_FAT_FILE
));
464 // Pick a valid directory entry
470 Status
= FatReadFile (PrivateData
, ParentDir
, 32, &DirEntry
);
471 if (EFI_ERROR (Status
)) {
472 return EFI_DEVICE_ERROR
;
475 // We only search for *FILE* in root directory
476 // Long file name entry is *NOT* supported
478 if (((DirEntry
.Attributes
& FAT_ATTR_DIRECTORY
) == FAT_ATTR_DIRECTORY
) || (DirEntry
.Attributes
== FAT_ATTR_LFN
)) {
482 // if this is a terminator dir entry, just return EFI_NOT_FOUND
484 if (DirEntry
.FileName
[0] == EMPTY_ENTRY_MARK
) {
485 return EFI_NOT_FOUND
;
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
491 if ((UINT8
) DirEntry
.FileName
[0] != DELETE_ENTRY_MARK
) {
496 // fill in the output parameter
498 EngFatToStr (8, DirEntry
.FileName
, BaseName
);
499 EngFatToStr (3, DirEntry
.FileName
+ 8, Ext
);
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));
506 Pos
+= StrLen (BaseName
);
509 CopyMem ((UINT8
*) Pos
, (UINT8
*) Ext
, 2 * (StrLen (Ext
) + 1));
512 SubFile
->Attributes
= DirEntry
.Attributes
;
513 SubFile
->CurrentCluster
= DirEntry
.FileCluster
;
514 if (ParentDir
->Volume
->FatType
== Fat32
) {
515 SubFile
->CurrentCluster
|= DirEntry
.FileClusterHigh
<< 16;
518 SubFile
->CurrentPos
= 0;
519 SubFile
->FileSize
= DirEntry
.FileSize
;
520 SubFile
->StartingCluster
= SubFile
->CurrentCluster
;
521 SubFile
->Volume
= ParentDir
->Volume
;
524 // in Pei phase, time parameters do not need to be filled for minimum use.