2 FAT file system access routines for FAT recovery PEIM
4 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
10 #include "FatLitePeim.h"
13 Check if there is a valid FAT in the corresponding Block device
14 of the volume and if yes, fill in the relevant fields for the
15 volume structure. Note there should be a valid Block device number
18 @param PrivateData Global memory map for accessing global
20 @param Volume On input, the BlockDeviceNumber field of the
21 Volume should be a valid value. On successful
22 output, all fields except the VolumeNumber
25 @retval EFI_SUCCESS A FAT is found and the volume structure is
27 @retval EFI_NOT_FOUND There is no FAT on the corresponding device.
28 @retval EFI_DEVICE_ERROR There is something error while accessing device.
33 IN PEI_FAT_PRIVATE_DATA
*PrivateData
,
34 IN OUT PEI_FAT_VOLUME
*Volume
38 PEI_FAT_BOOT_SECTOR Bpb
;
39 PEI_FAT_BOOT_SECTOR_EX BpbEx
;
42 UINT32 RootDirSectors
;
45 UINT64 FirstClusterLba
;
50 Status
= FatReadDisk (
52 Volume
->BlockDeviceNo
,
54 sizeof (PEI_FAT_BOOT_SECTOR_EX
),
57 if (EFI_ERROR (Status
)) {
64 sizeof (PEI_FAT_BOOT_SECTOR
)
67 Volume
->FatType
= FatUnknown
;
69 Sectors
= Bpb
.Sectors
;
71 Sectors
= Bpb
.LargeSectors
;
74 SectorsPerFat
= Bpb
.SectorsPerFat
;
75 if (SectorsPerFat
== 0) {
76 SectorsPerFat
= BpbEx
.LargeSectorsPerFat
;
77 Volume
->FatType
= Fat32
;
81 // Filter out those not a FAT
83 if ((Bpb
.Ia32Jump
[0] != 0xe9) && (Bpb
.Ia32Jump
[0] != 0xeb) && (Bpb
.Ia32Jump
[0] != 0x49)) {
87 if ((Bpb
.ReservedSectors
== 0) || (Bpb
.NoFats
== 0) || (Sectors
== 0)) {
91 if ((Bpb
.SectorsPerCluster
!= 1) &&
92 (Bpb
.SectorsPerCluster
!= 2) &&
93 (Bpb
.SectorsPerCluster
!= 4) &&
94 (Bpb
.SectorsPerCluster
!= 8) &&
95 (Bpb
.SectorsPerCluster
!= 16) &&
96 (Bpb
.SectorsPerCluster
!= 32) &&
97 (Bpb
.SectorsPerCluster
!= 64) &&
98 (Bpb
.SectorsPerCluster
!= 128)
101 return EFI_NOT_FOUND
;
104 if ((Volume
->FatType
== Fat32
) && ((SectorsPerFat
== 0) || (BpbEx
.FsVersion
!= 0))) {
105 return EFI_NOT_FOUND
;
108 if ((Bpb
.Media
!= 0xf0) &&
109 (Bpb
.Media
!= 0xf8) &&
110 (Bpb
.Media
!= 0xf9) &&
111 (Bpb
.Media
!= 0xfb) &&
112 (Bpb
.Media
!= 0xfc) &&
113 (Bpb
.Media
!= 0xfd) &&
114 (Bpb
.Media
!= 0xfe) &&
115 (Bpb
.Media
!= 0xff) &&
119 (Bpb
.Media
!= 0x00) &&
120 (Bpb
.Media
!= 0x01) &&
124 return EFI_NOT_FOUND
;
127 if ((Volume
->FatType
!= Fat32
) && (Bpb
.RootEntries
== 0)) {
128 return EFI_NOT_FOUND
;
132 // If this is fat32, refuse to mount mirror-disabled volumes
134 if ((Volume
->FatType
== Fat32
) && ((BpbEx
.ExtendedFlags
& 0x80) != 0)) {
135 return EFI_NOT_FOUND
;
139 // Fill in the volume structure fields
140 // (Sectors & SectorsPerFat is computed earlier already)
142 Volume
->ClusterSize
= Bpb
.SectorSize
* Bpb
.SectorsPerCluster
;
143 Volume
->RootEntries
= Bpb
.RootEntries
;
144 Volume
->SectorSize
= Bpb
.SectorSize
;
146 RootDirSectors
= ((Volume
->RootEntries
* sizeof (FAT_DIRECTORY_ENTRY
)) + (Volume
->SectorSize
- 1)) / Volume
->SectorSize
;
148 FatLba
= Bpb
.ReservedSectors
;
149 RootLba
= Bpb
.NoFats
* SectorsPerFat
+ FatLba
;
150 FirstClusterLba
= RootLba
+ RootDirSectors
;
152 Volume
->VolumeSize
= MultU64x32 (Sectors
, Volume
->SectorSize
);
153 Volume
->FatPos
= MultU64x32 (FatLba
, Volume
->SectorSize
);
154 Volume
->RootDirPos
= MultU64x32 (RootLba
, Volume
->SectorSize
);
155 Volume
->FirstClusterPos
= MultU64x32 (FirstClusterLba
, Volume
->SectorSize
);
156 Volume
->MaxCluster
= (UINT32
)(Sectors
- FirstClusterLba
) / Bpb
.SectorsPerCluster
;
157 Volume
->RootDirCluster
= BpbEx
.RootDirFirstCluster
;
160 // If this is not a fat32, determine if it's a fat16 or fat12
162 if (Volume
->FatType
!= Fat32
) {
163 if (Volume
->MaxCluster
>= 65525) {
164 return EFI_NOT_FOUND
;
167 Volume
->FatType
= Volume
->MaxCluster
< 4085 ? Fat12
: Fat16
;
174 Gets the next cluster in the cluster chain
176 @param PrivateData Global memory map for accessing global variables
177 @param Volume The volume
178 @param Cluster The cluster
179 @param NextCluster The cluster number of the next cluster
181 @retval EFI_SUCCESS The address is got
182 @retval EFI_INVALID_PARAMETER ClusterNo exceeds the MaxCluster of the volume.
183 @retval EFI_DEVICE_ERROR Read disk error
188 IN PEI_FAT_PRIVATE_DATA
*PrivateData
,
189 IN PEI_FAT_VOLUME
*Volume
,
191 OUT UINT32
*NextCluster
200 if (Volume
->FatType
== Fat32
) {
201 FatEntryPos
= Volume
->FatPos
+ MultU64x32 (4, Cluster
);
203 Status
= FatReadDisk (PrivateData
, Volume
->BlockDeviceNo
, FatEntryPos
, 4, NextCluster
);
204 *NextCluster
&= 0x0fffffff;
207 // Pad high bits for our FAT_CLUSTER_... macro definitions to work
209 if ((*NextCluster
) >= 0x0ffffff7) {
210 *NextCluster
|= (-1 &~0xf);
212 } else if (Volume
->FatType
== Fat16
) {
213 FatEntryPos
= Volume
->FatPos
+ MultU64x32 (2, Cluster
);
215 Status
= FatReadDisk (PrivateData
, Volume
->BlockDeviceNo
, FatEntryPos
, 2, NextCluster
);
218 // Pad high bits for our FAT_CLUSTER_... macro definitions to work
220 if ((*NextCluster
) >= 0xfff7) {
221 *NextCluster
|= (-1 &~0xf);
224 FatEntryPos
= Volume
->FatPos
+ DivU64x32Remainder (MultU64x32 (3, Cluster
), 2, &Dummy
);
226 Status
= FatReadDisk (PrivateData
, Volume
->BlockDeviceNo
, FatEntryPos
, 2, NextCluster
);
228 if ((Cluster
& 0x01) != 0) {
229 *NextCluster
= (*NextCluster
) >> 4;
231 *NextCluster
= (*NextCluster
) & 0x0fff;
235 // Pad high bits for our FAT_CLUSTER_... macro definitions to work
237 if ((*NextCluster
) >= 0x0ff7) {
238 *NextCluster
|= (-1 &~0xf);
242 if (EFI_ERROR (Status
)) {
243 return EFI_DEVICE_ERROR
;
250 Set a file's CurrentPos and CurrentCluster, then compute StraightReadAmount.
252 @param PrivateData the global memory map
254 @param Pos the Position which is offset from the file's
257 @retval EFI_SUCCESS Success.
258 @retval EFI_INVALID_PARAMETER Pos is beyond file's size.
259 @retval EFI_DEVICE_ERROR Something error while accessing media.
264 IN PEI_FAT_PRIVATE_DATA
*PrivateData
,
265 IN PEI_FAT_FILE
*File
,
275 if (File
->IsFixedRootDir
) {
276 if (Pos
>= MultU64x32 (File
->Volume
->RootEntries
, 32) - File
->CurrentPos
) {
277 return EFI_INVALID_PARAMETER
;
280 File
->CurrentPos
+= Pos
;
281 File
->StraightReadAmount
= (UINT32
)(MultU64x32 (File
->Volume
->RootEntries
, 32) - File
->CurrentPos
);
283 DivU64x32Remainder (File
->CurrentPos
, File
->Volume
->ClusterSize
, &Offset
);
284 AlignedPos
= (UINT32
)File
->CurrentPos
- (UINT32
)Offset
;
288 !FAT_CLUSTER_FUNCTIONAL (File
->CurrentCluster
) &&
289 AlignedPos
+ File
->Volume
->ClusterSize
<= File
->CurrentPos
+ Pos
292 AlignedPos
+= File
->Volume
->ClusterSize
;
293 Status
= FatGetNextCluster (
296 File
->CurrentCluster
,
297 &File
->CurrentCluster
299 if (EFI_ERROR (Status
)) {
300 return EFI_DEVICE_ERROR
;
304 if (FAT_CLUSTER_FUNCTIONAL (File
->CurrentCluster
)) {
305 return EFI_INVALID_PARAMETER
;
308 File
->CurrentPos
+= Pos
;
310 // Calculate the amount of consecutive cluster occupied by the file.
311 // FatReadFile() will use it to read these blocks once.
313 File
->StraightReadAmount
= 0;
314 Cluster
= File
->CurrentCluster
;
315 while (!FAT_CLUSTER_FUNCTIONAL (Cluster
)) {
316 File
->StraightReadAmount
+= File
->Volume
->ClusterSize
;
317 PrevCluster
= Cluster
;
318 Status
= FatGetNextCluster (PrivateData
, File
->Volume
, Cluster
, &Cluster
);
319 if (EFI_ERROR (Status
)) {
320 return EFI_DEVICE_ERROR
;
323 if (Cluster
!= PrevCluster
+ 1) {
328 DivU64x32Remainder (File
->CurrentPos
, File
->Volume
->ClusterSize
, &Offset
);
329 File
->StraightReadAmount
-= (UINT32
)Offset
;
336 Reads file data. Updates the file's CurrentPos.
338 @param PrivateData Global memory map for accessing global variables
339 @param File The file.
340 @param Size The amount of data to read.
341 @param Buffer The buffer storing the data.
343 @retval EFI_SUCCESS The data is read.
344 @retval EFI_INVALID_PARAMETER File is invalid.
345 @retval EFI_DEVICE_ERROR Something error while accessing media.
350 IN PEI_FAT_PRIVATE_DATA
*PrivateData
,
351 IN PEI_FAT_FILE
*File
,
364 if (File
->IsFixedRootDir
) {
366 // This is the fixed root dir in FAT12 and FAT16
368 if (File
->CurrentPos
+ Size
> File
->Volume
->RootEntries
* sizeof (FAT_DIRECTORY_ENTRY
)) {
369 return EFI_INVALID_PARAMETER
;
372 Status
= FatReadDisk (
374 File
->Volume
->BlockDeviceNo
,
375 File
->Volume
->RootDirPos
+ File
->CurrentPos
,
379 File
->CurrentPos
+= (UINT32
)Size
;
382 if ((File
->Attributes
& FAT_ATTR_DIRECTORY
) == 0) {
383 Size
= Size
< (File
->FileSize
- File
->CurrentPos
) ? Size
: (File
->FileSize
- File
->CurrentPos
);
387 // This is a normal cluster based file
390 DivU64x32Remainder (File
->CurrentPos
, File
->Volume
->ClusterSize
, &Offset
);
391 PhysicalAddr
= File
->Volume
->FirstClusterPos
+ MultU64x32 (File
->Volume
->ClusterSize
, File
->CurrentCluster
- 2);
393 Amount
= File
->StraightReadAmount
;
394 Amount
= Size
> Amount
? Amount
: Size
;
395 Status
= FatReadDisk (
397 File
->Volume
->BlockDeviceNo
,
398 PhysicalAddr
+ Offset
,
402 if (EFI_ERROR (Status
)) {
403 return EFI_DEVICE_ERROR
;
407 // Advance the file's current pos and current cluster
409 FatSetFilePos (PrivateData
, File
, (UINT32
)Amount
);
420 This function reads the next item in the parent directory and
421 initializes the output parameter SubFile (CurrentPos is initialized to 0).
422 The function updates the CurrentPos of the parent dir to after the item read.
423 If no more items were found, the function returns EFI_NOT_FOUND.
425 @param PrivateData Global memory map for accessing global variables
426 @param ParentDir The parent directory.
427 @param SubFile The File structure containing the sub file that
430 @retval EFI_SUCCESS The next sub file is obtained.
431 @retval EFI_INVALID_PARAMETER The ParentDir is not a directory.
432 @retval EFI_NOT_FOUND No more sub file exists.
433 @retval EFI_DEVICE_ERROR Something error while accessing media.
437 FatReadNextDirectoryEntry (
438 IN PEI_FAT_PRIVATE_DATA
*PrivateData
,
439 IN PEI_FAT_FILE
*ParentDir
,
440 OUT PEI_FAT_FILE
*SubFile
444 FAT_DIRECTORY_ENTRY DirEntry
;
449 ZeroMem ((UINT8
*)SubFile
, sizeof (PEI_FAT_FILE
));
452 // Pick a valid directory entry
458 Status
= FatReadFile (PrivateData
, ParentDir
, 32, &DirEntry
);
459 if (EFI_ERROR (Status
)) {
460 return EFI_DEVICE_ERROR
;
464 // We only search for *FILE* in root directory
465 // Long file name entry is *NOT* supported
467 if (((DirEntry
.Attributes
& FAT_ATTR_DIRECTORY
) == FAT_ATTR_DIRECTORY
) || (DirEntry
.Attributes
== FAT_ATTR_LFN
)) {
472 // if this is a terminator dir entry, just return EFI_NOT_FOUND
474 if (DirEntry
.FileName
[0] == EMPTY_ENTRY_MARK
) {
475 return EFI_NOT_FOUND
;
479 // If this not an invalid entry neither an empty entry, this is what we want.
480 // otherwise we will start a new loop to continue to find something meaningful
482 if ((UINT8
)DirEntry
.FileName
[0] != DELETE_ENTRY_MARK
) {
488 // fill in the output parameter
490 EngFatToStr (8, DirEntry
.FileName
, BaseName
);
491 EngFatToStr (3, DirEntry
.FileName
+ 8, Ext
);
493 Pos
= (UINT16
*)SubFile
->FileName
;
494 SetMem ((UINT8
*)Pos
, FAT_MAX_FILE_NAME_LENGTH
, 0);
495 CopyMem ((UINT8
*)Pos
, (UINT8
*)BaseName
, 2 * (StrLen (BaseName
) + 1));
498 Pos
+= StrLen (BaseName
);
501 CopyMem ((UINT8
*)Pos
, (UINT8
*)Ext
, 2 * (StrLen (Ext
) + 1));
504 SubFile
->Attributes
= DirEntry
.Attributes
;
505 SubFile
->CurrentCluster
= DirEntry
.FileCluster
;
506 if (ParentDir
->Volume
->FatType
== Fat32
) {
507 SubFile
->CurrentCluster
|= DirEntry
.FileClusterHigh
<< 16;
510 SubFile
->CurrentPos
= 0;
511 SubFile
->FileSize
= DirEntry
.FileSize
;
512 SubFile
->StartingCluster
= SubFile
->CurrentCluster
;
513 SubFile
->Volume
= ParentDir
->Volume
;
516 // in Pei phase, time parameters do not need to be filled for minimum use.