Home | History | Annotate | Download | only in FatPei
      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
     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
    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
    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
    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
    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