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
FatGetBpbInfo(IN PEI_FAT_PRIVATE_DATA * PrivateData,IN OUT PEI_FAT_VOLUME * Volume)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
FatGetNextCluster(IN PEI_FAT_PRIVATE_DATA * PrivateData,IN PEI_FAT_VOLUME * Volume,IN UINT32 Cluster,OUT UINT32 * NextCluster)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
FatSetFilePos(IN PEI_FAT_PRIVATE_DATA * PrivateData,IN PEI_FAT_FILE * File,IN UINT32 Pos)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
FatReadFile(IN PEI_FAT_PRIVATE_DATA * PrivateData,IN PEI_FAT_FILE * File,IN UINTN Size,OUT VOID * Buffer)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
FatReadNextDirectoryEntry(IN PEI_FAT_PRIVATE_DATA * PrivateData,IN PEI_FAT_FILE * ParentDir,OUT PEI_FAT_FILE * SubFile)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