1 /** @file
2   FAT file system access routines for FAT recovery PEIM
3 
4 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
5 
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7 
8 **/
9 
10 #include "FatLitePeim.h"
11 
12 
13 /**
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
17   already set.
18 
19   @param  PrivateData            Global memory map for accessing global
20                                  variables.
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
24                                  field is initialized.
25 
26   @retval EFI_SUCCESS            A FAT is found and the volume structure is
27                                  initialized.
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.
30 
31 **/
32 EFI_STATUS
FatGetBpbInfo(IN PEI_FAT_PRIVATE_DATA * PrivateData,IN OUT PEI_FAT_VOLUME * Volume)33 FatGetBpbInfo (
34   IN      PEI_FAT_PRIVATE_DATA  *PrivateData,
35   IN OUT  PEI_FAT_VOLUME        *Volume
36   )
37 {
38   EFI_STATUS              Status;
39   PEI_FAT_BOOT_SECTOR     Bpb;
40   PEI_FAT_BOOT_SECTOR_EX  BpbEx;
41   UINT32                  Sectors;
42   UINT32                  SectorsPerFat;
43   UINT32                  RootDirSectors;
44   UINT64                  FatLba;
45   UINT64                  RootLba;
46   UINT64                  FirstClusterLba;
47 
48   //
49   // Read in the BPB
50   //
51   Status = FatReadDisk (
52             PrivateData,
53             Volume->BlockDeviceNo,
54             0,
55             sizeof (PEI_FAT_BOOT_SECTOR_EX),
56             &BpbEx
57             );
58   if (EFI_ERROR (Status)) {
59     return Status;
60   }
61 
62   CopyMem (
63     (UINT8 *) (&Bpb),
64     (UINT8 *) (&BpbEx),
65     sizeof (PEI_FAT_BOOT_SECTOR)
66     );
67 
68   Volume->FatType = FatUnknown;
69 
70   Sectors         = Bpb.Sectors;
71   if (Sectors == 0) {
72     Sectors = Bpb.LargeSectors;
73   }
74 
75   SectorsPerFat = Bpb.SectorsPerFat;
76   if (SectorsPerFat == 0) {
77     SectorsPerFat   = BpbEx.LargeSectorsPerFat;
78     Volume->FatType = Fat32;
79   }
80   //
81   // Filter out those not a FAT
82   //
83   if (Bpb.Ia32Jump[0] != 0xe9 && Bpb.Ia32Jump[0] != 0xeb && Bpb.Ia32Jump[0] != 0x49) {
84     return EFI_NOT_FOUND;
85   }
86 
87   if (Bpb.ReservedSectors == 0 || Bpb.NoFats == 0 || Sectors == 0) {
88     return EFI_NOT_FOUND;
89   }
90 
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
99       ) {
100     return EFI_NOT_FOUND;
101   }
102 
103   if (Volume->FatType == Fat32 && (SectorsPerFat == 0 || BpbEx.FsVersion != 0)) {
104     return EFI_NOT_FOUND;
105   }
106 
107   if (Bpb.Media != 0xf0 &&
108       Bpb.Media != 0xf8 &&
109       Bpb.Media != 0xf9 &&
110       Bpb.Media != 0xfb &&
111       Bpb.Media != 0xfc &&
112       Bpb.Media != 0xfd &&
113       Bpb.Media != 0xfe &&
114       Bpb.Media != 0xff &&
115       //
116       // FujitsuFMR
117       //
118       Bpb.Media != 0x00 &&
119       Bpb.Media != 0x01 &&
120       Bpb.Media != 0xfa
121       ) {
122     return EFI_NOT_FOUND;
123   }
124 
125   if (Volume->FatType != Fat32 && Bpb.RootEntries == 0) {
126     return EFI_NOT_FOUND;
127   }
128   //
129   // If this is fat32, refuse to mount mirror-disabled volumes
130   //
131   if (Volume->FatType == Fat32 && ((BpbEx.ExtendedFlags & 0x80) != 0)) {
132     return EFI_NOT_FOUND;
133   }
134   //
135   // Fill in the volume structure fields
136   // (Sectors & SectorsPerFat is computed earlier already)
137   //
138   Volume->ClusterSize = Bpb.SectorSize * Bpb.SectorsPerCluster;
139   Volume->RootEntries = Bpb.RootEntries;
140   Volume->SectorSize  = Bpb.SectorSize;
141 
142   RootDirSectors = ((Volume->RootEntries * sizeof (FAT_DIRECTORY_ENTRY)) + (Volume->SectorSize - 1)) / Volume->SectorSize;
143 
144   FatLba                  = Bpb.ReservedSectors;
145   RootLba                 = Bpb.NoFats * SectorsPerFat + FatLba;
146   FirstClusterLba         = RootLba + RootDirSectors;
147 
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;
154 
155   //
156   // If this is not a fat32, determine if it's a fat16 or fat12
157   //
158   if (Volume->FatType != Fat32) {
159 
160     if (Volume->MaxCluster >= 65525) {
161       return EFI_NOT_FOUND;
162     }
163 
164     Volume->FatType = Volume->MaxCluster < 4085 ? Fat12 : Fat16;
165   }
166 
167   return EFI_SUCCESS;
168 }
169 
170 
171 /**
172   Gets the next cluster in the cluster chain
173 
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
178 
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
182 
183 **/
184 EFI_STATUS
FatGetNextCluster(IN PEI_FAT_PRIVATE_DATA * PrivateData,IN PEI_FAT_VOLUME * Volume,IN UINT32 Cluster,OUT UINT32 * NextCluster)185 FatGetNextCluster (
186   IN  PEI_FAT_PRIVATE_DATA  *PrivateData,
187   IN  PEI_FAT_VOLUME        *Volume,
188   IN  UINT32                Cluster,
189   OUT UINT32                *NextCluster
190   )
191 {
192   EFI_STATUS  Status;
193   UINT64      FatEntryPos;
194   UINT32      Dummy;
195 
196   *NextCluster = 0;
197 
198   if (Volume->FatType == Fat32) {
199     FatEntryPos = Volume->FatPos + MultU64x32 (4, Cluster);
200 
201     Status      = FatReadDisk (PrivateData, Volume->BlockDeviceNo, FatEntryPos, 4, NextCluster);
202     *NextCluster &= 0x0fffffff;
203 
204     //
205     // Pad high bits for our FAT_CLUSTER_... macro definitions to work
206     //
207     if ((*NextCluster) >= 0x0ffffff7) {
208       *NextCluster |= (-1 &~0xf);
209     }
210 
211   } else if (Volume->FatType == Fat16) {
212     FatEntryPos = Volume->FatPos + MultU64x32 (2, Cluster);
213 
214     Status      = FatReadDisk (PrivateData, Volume->BlockDeviceNo, FatEntryPos, 2, NextCluster);
215 
216     //
217     // Pad high bits for our FAT_CLUSTER_... macro definitions to work
218     //
219     if ((*NextCluster) >= 0xfff7) {
220       *NextCluster |= (-1 &~0xf);
221     }
222 
223   } else {
224     FatEntryPos = Volume->FatPos + DivU64x32Remainder (MultU64x32 (3, Cluster), 2, &Dummy);
225 
226     Status      = FatReadDisk (PrivateData, Volume->BlockDeviceNo, FatEntryPos, 2, NextCluster);
227 
228     if ((Cluster & 0x01) != 0) {
229       *NextCluster = (*NextCluster) >> 4;
230     } else {
231       *NextCluster = (*NextCluster) & 0x0fff;
232     }
233     //
234     // Pad high bits for our FAT_CLUSTER_... macro definitions to work
235     //
236     if ((*NextCluster) >= 0x0ff7) {
237       *NextCluster |= (-1 &~0xf);
238     }
239   }
240 
241   if (EFI_ERROR (Status)) {
242     return EFI_DEVICE_ERROR;
243   }
244 
245   return EFI_SUCCESS;
246 
247 }
248 
249 
250 /**
251   Set a file's CurrentPos and CurrentCluster, then compute StraightReadAmount.
252 
253   @param  PrivateData            the global memory map
254   @param  File                   the file
255   @param  Pos                    the Position which is offset from the file's
256                                  CurrentPos
257 
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.
261 
262 **/
263 EFI_STATUS
FatSetFilePos(IN PEI_FAT_PRIVATE_DATA * PrivateData,IN PEI_FAT_FILE * File,IN UINT32 Pos)264 FatSetFilePos (
265   IN  PEI_FAT_PRIVATE_DATA  *PrivateData,
266   IN  PEI_FAT_FILE          *File,
267   IN  UINT32                Pos
268   )
269 {
270   EFI_STATUS  Status;
271   UINT32      AlignedPos;
272   UINT32      Offset;
273   UINT32      Cluster;
274   UINT32      PrevCluster;
275 
276   if (File->IsFixedRootDir) {
277 
278     if (Pos >= MultU64x32 (File->Volume->RootEntries, 32) - File->CurrentPos) {
279       return EFI_INVALID_PARAMETER;
280     }
281 
282     File->CurrentPos += Pos;
283     File->StraightReadAmount = (UINT32) (MultU64x32 (File->Volume->RootEntries, 32) - File->CurrentPos);
284 
285   } else {
286 
287     DivU64x32Remainder (File->CurrentPos, File->Volume->ClusterSize, &Offset);
288     AlignedPos = (UINT32) File->CurrentPos - (UINT32) Offset;
289 
290     while
291     (
292       !FAT_CLUSTER_FUNCTIONAL (File->CurrentCluster) &&
293       AlignedPos + File->Volume->ClusterSize <= File->CurrentPos + Pos
294     ) {
295       AlignedPos += File->Volume->ClusterSize;
296       Status = FatGetNextCluster (
297                 PrivateData,
298                 File->Volume,
299                 File->CurrentCluster,
300                 &File->CurrentCluster
301                 );
302       if (EFI_ERROR (Status)) {
303         return EFI_DEVICE_ERROR;
304       }
305     }
306 
307     if (FAT_CLUSTER_FUNCTIONAL (File->CurrentCluster)) {
308       return EFI_INVALID_PARAMETER;
309     }
310 
311     File->CurrentPos += Pos;
312     //
313     // Calculate the amount of consecutive cluster occupied by the file.
314     // FatReadFile() will use it to read these blocks once.
315     //
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;
324       }
325 
326       if (Cluster != PrevCluster + 1) {
327         break;
328       }
329     }
330 
331     DivU64x32Remainder (File->CurrentPos, File->Volume->ClusterSize, &Offset);
332     File->StraightReadAmount -= (UINT32) Offset;
333 
334   }
335 
336   return EFI_SUCCESS;
337 }
338 
339 
340 /**
341   Reads file data. Updates the file's CurrentPos.
342 
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.
347 
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.
351 
352 **/
353 EFI_STATUS
FatReadFile(IN PEI_FAT_PRIVATE_DATA * PrivateData,IN PEI_FAT_FILE * File,IN UINTN Size,OUT VOID * Buffer)354 FatReadFile (
355   IN  PEI_FAT_PRIVATE_DATA  *PrivateData,
356   IN  PEI_FAT_FILE          *File,
357   IN  UINTN                 Size,
358   OUT VOID                  *Buffer
359   )
360 {
361   EFI_STATUS  Status;
362   CHAR8       *BufferPtr;
363   UINT32      Offset;
364   UINT64      PhysicalAddr;
365   UINTN       Amount;
366 
367   BufferPtr = Buffer;
368 
369   if (File->IsFixedRootDir) {
370     //
371     // This is the fixed root dir in FAT12 and FAT16
372     //
373     if (File->CurrentPos + Size > File->Volume->RootEntries * sizeof (FAT_DIRECTORY_ENTRY)) {
374       return EFI_INVALID_PARAMETER;
375     }
376 
377     Status = FatReadDisk (
378               PrivateData,
379               File->Volume->BlockDeviceNo,
380               File->Volume->RootDirPos + File->CurrentPos,
381               Size,
382               Buffer
383               );
384     File->CurrentPos += (UINT32) Size;
385     return Status;
386 
387   } else {
388 
389     if ((File->Attributes & FAT_ATTR_DIRECTORY) == 0) {
390       Size = Size < (File->FileSize - File->CurrentPos) ? Size : (File->FileSize - File->CurrentPos);
391     }
392     //
393     // This is a normal cluster based file
394     //
395     while (Size != 0) {
396       DivU64x32Remainder (File->CurrentPos, File->Volume->ClusterSize, &Offset);
397       PhysicalAddr  = File->Volume->FirstClusterPos + MultU64x32 (File->Volume->ClusterSize, File->CurrentCluster - 2);
398 
399       Amount        = File->StraightReadAmount;
400       Amount        = Size > Amount ? Amount : Size;
401       Status = FatReadDisk (
402                 PrivateData,
403                 File->Volume->BlockDeviceNo,
404                 PhysicalAddr + Offset,
405                 Amount,
406                 BufferPtr
407                 );
408       if (EFI_ERROR (Status)) {
409         return EFI_DEVICE_ERROR;
410       }
411       //
412       // Advance the file's current pos and current cluster
413       //
414       FatSetFilePos (PrivateData, File, (UINT32) Amount);
415 
416       BufferPtr += Amount;
417       Size -= Amount;
418     }
419 
420     return EFI_SUCCESS;
421   }
422 }
423 
424 
425 /**
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.
430 
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
434                                  is caught.
435 
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.
440 
441 **/
442 EFI_STATUS
FatReadNextDirectoryEntry(IN PEI_FAT_PRIVATE_DATA * PrivateData,IN PEI_FAT_FILE * ParentDir,OUT PEI_FAT_FILE * SubFile)443 FatReadNextDirectoryEntry (
444   IN  PEI_FAT_PRIVATE_DATA  *PrivateData,
445   IN  PEI_FAT_FILE          *ParentDir,
446   OUT PEI_FAT_FILE          *SubFile
447   )
448 {
449   EFI_STATUS          Status;
450   FAT_DIRECTORY_ENTRY DirEntry;
451   CHAR16              *Pos;
452   CHAR16              BaseName[9];
453   CHAR16              Ext[4];
454 
455   ZeroMem ((UINT8 *) SubFile, sizeof (PEI_FAT_FILE));
456 
457   //
458   // Pick a valid directory entry
459   //
460   while (1) {
461     //
462     // Read one entry
463     //
464     Status = FatReadFile (PrivateData, ParentDir, 32, &DirEntry);
465     if (EFI_ERROR (Status)) {
466       return EFI_DEVICE_ERROR;
467     }
468     //
469     // We only search for *FILE* in root directory
470     // Long file name entry is *NOT* supported
471     //
472     if (((DirEntry.Attributes & FAT_ATTR_DIRECTORY) == FAT_ATTR_DIRECTORY) || (DirEntry.Attributes == FAT_ATTR_LFN)) {
473       continue;
474     }
475     //
476     // if this is a terminator dir entry, just return EFI_NOT_FOUND
477     //
478     if (DirEntry.FileName[0] == EMPTY_ENTRY_MARK) {
479       return EFI_NOT_FOUND;
480     }
481     //
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
484     //
485     if ((UINT8) DirEntry.FileName[0] != DELETE_ENTRY_MARK) {
486       break;
487     }
488   }
489   //
490   // fill in the output parameter
491   //
492   EngFatToStr (8, DirEntry.FileName, BaseName);
493   EngFatToStr (3, DirEntry.FileName + 8, Ext);
494 
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));
498 
499   if (Ext[0] != 0) {
500     Pos += StrLen (BaseName);
501     *Pos = '.';
502     Pos++;
503     CopyMem ((UINT8 *) Pos, (UINT8 *) Ext, 2 * (StrLen (Ext) + 1));
504   }
505 
506   SubFile->Attributes     = DirEntry.Attributes;
507   SubFile->CurrentCluster = DirEntry.FileCluster;
508   if (ParentDir->Volume->FatType == Fat32) {
509     SubFile->CurrentCluster |= DirEntry.FileClusterHigh << 16;
510   }
511 
512   SubFile->CurrentPos       = 0;
513   SubFile->FileSize         = DirEntry.FileSize;
514   SubFile->StartingCluster  = SubFile->CurrentCluster;
515   SubFile->Volume           = ParentDir->Volume;
516 
517   //
518   // in Pei phase, time parameters do not need to be filled for minimum use.
519   //
520   return Status;
521 }
522