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"
14 Check if there is a valid FAT in the corresponding Block device
15 of the volume and if yes, fill in the relevant fields for the
16 volume structure. Note there should be a valid Block device number
19 @param PrivateData Global memory map for accessing global
21 @param Volume On input, the BlockDeviceNumber field of the
22 Volume should be a valid value. On successful
23 output, all fields except the VolumeNumber
26 @retval EFI_SUCCESS A FAT is found and the volume structure is
28 @retval EFI_NOT_FOUND There is no FAT on the corresponding device.
29 @retval EFI_DEVICE_ERROR There is something error while accessing device.
34 IN PEI_FAT_PRIVATE_DATA
*PrivateData
,
35 IN OUT PEI_FAT_VOLUME
*Volume
39 PEI_FAT_BOOT_SECTOR Bpb
;
40 PEI_FAT_BOOT_SECTOR_EX BpbEx
;
43 UINT32 RootDirSectors
;
46 UINT64 FirstClusterLba
;
51 Status
= FatReadDisk (
53 Volume
->BlockDeviceNo
,
55 sizeof (PEI_FAT_BOOT_SECTOR_EX
),
58 if (EFI_ERROR (Status
)) {
65 sizeof (PEI_FAT_BOOT_SECTOR
)
68 Volume
->FatType
= FatUnknown
;
70 Sectors
= Bpb
.Sectors
;
72 Sectors
= Bpb
.LargeSectors
;
75 SectorsPerFat
= Bpb
.SectorsPerFat
;
76 if (SectorsPerFat
== 0) {
77 SectorsPerFat
= BpbEx
.LargeSectorsPerFat
;
78 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
100 return EFI_NOT_FOUND
;
103 if (Volume
->FatType
== Fat32
&& (SectorsPerFat
== 0 || BpbEx
.FsVersion
!= 0)) {
104 return EFI_NOT_FOUND
;
107 if (Bpb
.Media
!= 0xf0 &&
122 return EFI_NOT_FOUND
;
125 if (Volume
->FatType
!= Fat32
&& Bpb
.RootEntries
== 0) {
126 return EFI_NOT_FOUND
;
129 // If this is fat32, refuse to mount mirror-disabled volumes
131 if (Volume
->FatType
== Fat32
&& ((BpbEx
.ExtendedFlags
& 0x80) != 0)) {
132 return EFI_NOT_FOUND
;
135 // Fill in the volume structure fields
136 // (Sectors & SectorsPerFat is computed earlier already)
138 Volume
->ClusterSize
= Bpb
.SectorSize
* Bpb
.SectorsPerCluster
;
139 Volume
->RootEntries
= Bpb
.RootEntries
;
140 Volume
->SectorSize
= Bpb
.SectorSize
;
142 RootDirSectors
= ((Volume
->RootEntries
* sizeof (FAT_DIRECTORY_ENTRY
)) + (Volume
->SectorSize
- 1)) / Volume
->SectorSize
;
144 FatLba
= Bpb
.ReservedSectors
;
145 RootLba
= Bpb
.NoFats
* SectorsPerFat
+ FatLba
;
146 FirstClusterLba
= RootLba
+ RootDirSectors
;
148 Volume
->VolumeSize
= MultU64x32 (Sectors
, Volume
->SectorSize
);
149 Volume
->FatPos
= MultU64x32 (FatLba
, Volume
->SectorSize
);
150 Volume
->RootDirPos
= MultU64x32 (RootLba
, Volume
->SectorSize
);
151 Volume
->FirstClusterPos
= MultU64x32 (FirstClusterLba
, Volume
->SectorSize
);
152 Volume
->MaxCluster
= (UINT32
) (Sectors
- FirstClusterLba
) / Bpb
.SectorsPerCluster
;
153 Volume
->RootDirCluster
= BpbEx
.RootDirFirstCluster
;
156 // If this is not a fat32, determine if it's a fat16 or fat12
158 if (Volume
->FatType
!= Fat32
) {
160 if (Volume
->MaxCluster
>= 65525) {
161 return EFI_NOT_FOUND
;
164 Volume
->FatType
= Volume
->MaxCluster
< 4085 ? Fat12
: Fat16
;
172 Gets the next cluster in the cluster chain
174 @param PrivateData Global memory map for accessing global variables
175 @param Volume The volume
176 @param Cluster The cluster
177 @param NextCluster The cluster number of the next cluster
179 @retval EFI_SUCCESS The address is got
180 @retval EFI_INVALID_PARAMETER ClusterNo exceeds the MaxCluster of the volume.
181 @retval EFI_DEVICE_ERROR Read disk error
186 IN PEI_FAT_PRIVATE_DATA
*PrivateData
,
187 IN PEI_FAT_VOLUME
*Volume
,
189 OUT UINT32
*NextCluster
198 if (Volume
->FatType
== Fat32
) {
199 FatEntryPos
= Volume
->FatPos
+ MultU64x32 (4, Cluster
);
201 Status
= FatReadDisk (PrivateData
, Volume
->BlockDeviceNo
, FatEntryPos
, 4, NextCluster
);
202 *NextCluster
&= 0x0fffffff;
205 // Pad high bits for our FAT_CLUSTER_... macro definitions to work
207 if ((*NextCluster
) >= 0x0ffffff7) {
208 *NextCluster
|= (-1 &~0xf);
211 } else if (Volume
->FatType
== Fat16
) {
212 FatEntryPos
= Volume
->FatPos
+ MultU64x32 (2, Cluster
);
214 Status
= FatReadDisk (PrivateData
, Volume
->BlockDeviceNo
, FatEntryPos
, 2, NextCluster
);
217 // Pad high bits for our FAT_CLUSTER_... macro definitions to work
219 if ((*NextCluster
) >= 0xfff7) {
220 *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;
234 // Pad high bits for our FAT_CLUSTER_... macro definitions to work
236 if ((*NextCluster
) >= 0x0ff7) {
237 *NextCluster
|= (-1 &~0xf);
241 if (EFI_ERROR (Status
)) {
242 return EFI_DEVICE_ERROR
;
251 Set a file's CurrentPos and CurrentCluster, then compute StraightReadAmount.
253 @param PrivateData the global memory map
255 @param Pos the Position which is offset from the file's
258 @retval EFI_SUCCESS Success.
259 @retval EFI_INVALID_PARAMETER Pos is beyond file's size.
260 @retval EFI_DEVICE_ERROR Something error while accessing media.
265 IN PEI_FAT_PRIVATE_DATA
*PrivateData
,
266 IN PEI_FAT_FILE
*File
,
276 if (File
->IsFixedRootDir
) {
278 if (Pos
>= MultU64x32 (File
->Volume
->RootEntries
, 32) - File
->CurrentPos
) {
279 return EFI_INVALID_PARAMETER
;
282 File
->CurrentPos
+= Pos
;
283 File
->StraightReadAmount
= (UINT32
) (MultU64x32 (File
->Volume
->RootEntries
, 32) - File
->CurrentPos
);
287 DivU64x32Remainder (File
->CurrentPos
, File
->Volume
->ClusterSize
, &Offset
);
288 AlignedPos
= (UINT32
) File
->CurrentPos
- (UINT32
) Offset
;
292 !FAT_CLUSTER_FUNCTIONAL (File
->CurrentCluster
) &&
293 AlignedPos
+ File
->Volume
->ClusterSize
<= File
->CurrentPos
+ Pos
295 AlignedPos
+= File
->Volume
->ClusterSize
;
296 Status
= FatGetNextCluster (
299 File
->CurrentCluster
,
300 &File
->CurrentCluster
302 if (EFI_ERROR (Status
)) {
303 return EFI_DEVICE_ERROR
;
307 if (FAT_CLUSTER_FUNCTIONAL (File
->CurrentCluster
)) {
308 return EFI_INVALID_PARAMETER
;
311 File
->CurrentPos
+= Pos
;
313 // Calculate the amount of consecutive cluster occupied by the file.
314 // FatReadFile() will use it to read these blocks once.
316 File
->StraightReadAmount
= 0;
317 Cluster
= File
->CurrentCluster
;
318 while (!FAT_CLUSTER_FUNCTIONAL (Cluster
)) {
319 File
->StraightReadAmount
+= File
->Volume
->ClusterSize
;
320 PrevCluster
= Cluster
;
321 Status
= FatGetNextCluster (PrivateData
, File
->Volume
, Cluster
, &Cluster
);
322 if (EFI_ERROR (Status
)) {
323 return EFI_DEVICE_ERROR
;
326 if (Cluster
!= PrevCluster
+ 1) {
331 DivU64x32Remainder (File
->CurrentPos
, File
->Volume
->ClusterSize
, &Offset
);
332 File
->StraightReadAmount
-= (UINT32
) Offset
;
341 Reads file data. Updates the file's CurrentPos.
343 @param PrivateData Global memory map for accessing global variables
344 @param File The file.
345 @param Size The amount of data to read.
346 @param Buffer The buffer storing the data.
348 @retval EFI_SUCCESS The data is read.
349 @retval EFI_INVALID_PARAMETER File is invalid.
350 @retval EFI_DEVICE_ERROR Something error while accessing media.
355 IN PEI_FAT_PRIVATE_DATA
*PrivateData
,
356 IN PEI_FAT_FILE
*File
,
369 if (File
->IsFixedRootDir
) {
371 // This is the fixed root dir in FAT12 and FAT16
373 if (File
->CurrentPos
+ Size
> File
->Volume
->RootEntries
* sizeof (FAT_DIRECTORY_ENTRY
)) {
374 return EFI_INVALID_PARAMETER
;
377 Status
= FatReadDisk (
379 File
->Volume
->BlockDeviceNo
,
380 File
->Volume
->RootDirPos
+ File
->CurrentPos
,
384 File
->CurrentPos
+= (UINT32
) Size
;
389 if ((File
->Attributes
& FAT_ATTR_DIRECTORY
) == 0) {
390 Size
= Size
< (File
->FileSize
- File
->CurrentPos
) ? Size
: (File
->FileSize
- File
->CurrentPos
);
393 // This is a normal cluster based file
396 DivU64x32Remainder (File
->CurrentPos
, File
->Volume
->ClusterSize
, &Offset
);
397 PhysicalAddr
= File
->Volume
->FirstClusterPos
+ MultU64x32 (File
->Volume
->ClusterSize
, File
->CurrentCluster
- 2);
399 Amount
= File
->StraightReadAmount
;
400 Amount
= Size
> Amount
? Amount
: Size
;
401 Status
= FatReadDisk (
403 File
->Volume
->BlockDeviceNo
,
404 PhysicalAddr
+ Offset
,
408 if (EFI_ERROR (Status
)) {
409 return EFI_DEVICE_ERROR
;
412 // Advance the file's current pos and current cluster
414 FatSetFilePos (PrivateData
, File
, (UINT32
) Amount
);
426 This function reads the next item in the parent directory and
427 initializes the output parameter SubFile (CurrentPos is initialized to 0).
428 The function updates the CurrentPos of the parent dir to after the item read.
429 If no more items were found, the function returns EFI_NOT_FOUND.
431 @param PrivateData Global memory map for accessing global variables
432 @param ParentDir The parent directory.
433 @param SubFile The File structure containing the sub file that
436 @retval EFI_SUCCESS The next sub file is obtained.
437 @retval EFI_INVALID_PARAMETER The ParentDir is not a directory.
438 @retval EFI_NOT_FOUND No more sub file exists.
439 @retval EFI_DEVICE_ERROR Something error while accessing media.
443 FatReadNextDirectoryEntry (
444 IN PEI_FAT_PRIVATE_DATA
*PrivateData
,
445 IN PEI_FAT_FILE
*ParentDir
,
446 OUT PEI_FAT_FILE
*SubFile
450 FAT_DIRECTORY_ENTRY DirEntry
;
455 ZeroMem ((UINT8
*) SubFile
, sizeof (PEI_FAT_FILE
));
458 // Pick a valid directory entry
464 Status
= FatReadFile (PrivateData
, ParentDir
, 32, &DirEntry
);
465 if (EFI_ERROR (Status
)) {
466 return EFI_DEVICE_ERROR
;
469 // We only search for *FILE* in root directory
470 // Long file name entry is *NOT* supported
472 if (((DirEntry
.Attributes
& FAT_ATTR_DIRECTORY
) == FAT_ATTR_DIRECTORY
) || (DirEntry
.Attributes
== FAT_ATTR_LFN
)) {
476 // if this is a terminator dir entry, just return EFI_NOT_FOUND
478 if (DirEntry
.FileName
[0] == EMPTY_ENTRY_MARK
) {
479 return EFI_NOT_FOUND
;
482 // If this not an invalid entry neither an empty entry, this is what we want.
483 // otherwise we will start a new loop to continue to find something meaningful
485 if ((UINT8
) DirEntry
.FileName
[0] != DELETE_ENTRY_MARK
) {
490 // fill in the output parameter
492 EngFatToStr (8, DirEntry
.FileName
, BaseName
);
493 EngFatToStr (3, DirEntry
.FileName
+ 8, Ext
);
495 Pos
= (UINT16
*) SubFile
->FileName
;
496 SetMem ((UINT8
*) Pos
, FAT_MAX_FILE_NAME_LENGTH
, 0);
497 CopyMem ((UINT8
*) Pos
, (UINT8
*) BaseName
, 2 * (StrLen (BaseName
) + 1));
500 Pos
+= StrLen (BaseName
);
503 CopyMem ((UINT8
*) Pos
, (UINT8
*) Ext
, 2 * (StrLen (Ext
) + 1));
506 SubFile
->Attributes
= DirEntry
.Attributes
;
507 SubFile
->CurrentCluster
= DirEntry
.FileCluster
;
508 if (ParentDir
->Volume
->FatType
== Fat32
) {
509 SubFile
->CurrentCluster
|= DirEntry
.FileClusterHigh
<< 16;
512 SubFile
->CurrentPos
= 0;
513 SubFile
->FileSize
= DirEntry
.FileSize
;
514 SubFile
->StartingCluster
= SubFile
->CurrentCluster
;
515 SubFile
->Volume
= ParentDir
->Volume
;
518 // in Pei phase, time parameters do not need to be filled for minimum use.