]> git.proxmox.com Git - mirror_edk2.git/blobdiff - FatPkg/FatPei/FatLiteAccess.c
FatPkg: Add FAT PEIM
[mirror_edk2.git] / FatPkg / FatPei / FatLiteAccess.c
diff --git a/FatPkg/FatPei/FatLiteAccess.c b/FatPkg/FatPei/FatLiteAccess.c
new file mode 100644 (file)
index 0000000..0a688a3
--- /dev/null
@@ -0,0 +1,527 @@
+/** @file\r
+  FAT file system access routines for FAT recovery PEIM\r
+\r
+Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+This program and the accompanying materials are licensed and made available\r
+under the terms and conditions of the BSD License which accompanies this\r
+distribution. The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "FatLitePeim.h"\r
+\r
+\r
+/**\r
+  Check if there is a valid FAT in the corresponding Block device\r
+  of the volume and if yes, fill in the relevant fields for the\r
+  volume structure. Note there should be a valid Block device number\r
+  already set.\r
+\r
+  @param  PrivateData            Global memory map for accessing global \r
+                                 variables. \r
+  @param  Volume                 On input, the BlockDeviceNumber field of the \r
+                                 Volume  should be a valid value. On successful \r
+                                 output, all  fields except the VolumeNumber \r
+                                 field is initialized. \r
+\r
+  @retval EFI_SUCCESS            A FAT is found and the volume structure is \r
+                                 initialized. \r
+  @retval EFI_NOT_FOUND          There is no FAT on the corresponding device. \r
+  @retval EFI_DEVICE_ERROR       There is something error while accessing device.\r
+\r
+**/\r
+EFI_STATUS\r
+FatGetBpbInfo (\r
+  IN      PEI_FAT_PRIVATE_DATA  *PrivateData,\r
+  IN OUT  PEI_FAT_VOLUME        *Volume\r
+  )\r
+{\r
+  EFI_STATUS              Status;\r
+  PEI_FAT_BOOT_SECTOR     Bpb;\r
+  PEI_FAT_BOOT_SECTOR_EX  BpbEx;\r
+  UINT32                  Sectors;\r
+  UINT32                  SectorsPerFat;\r
+  UINT32                  RootDirSectors;\r
+  UINT64                  FatLba;\r
+  UINT64                  RootLba;\r
+  UINT64                  FirstClusterLba;\r
+\r
+  //\r
+  // Read in the BPB\r
+  //\r
+  Status = FatReadDisk (\r
+            PrivateData,\r
+            Volume->BlockDeviceNo,\r
+            0,\r
+            sizeof (PEI_FAT_BOOT_SECTOR_EX),\r
+            &BpbEx\r
+            );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  CopyMem (\r
+    (UINT8 *) (&Bpb),\r
+    (UINT8 *) (&BpbEx),\r
+    sizeof (PEI_FAT_BOOT_SECTOR)\r
+    );\r
+\r
+  Volume->FatType = FatUnknown;\r
+\r
+  Sectors         = Bpb.Sectors;\r
+  if (Sectors == 0) {\r
+    Sectors = Bpb.LargeSectors;\r
+  }\r
+\r
+  SectorsPerFat = Bpb.SectorsPerFat;\r
+  if (SectorsPerFat == 0) {\r
+    SectorsPerFat   = BpbEx.LargeSectorsPerFat;\r
+    Volume->FatType = Fat32;\r
+  }\r
+  //\r
+  // Filter out those not a FAT\r
+  //\r
+  if (Bpb.Ia32Jump[0] != 0xe9 && Bpb.Ia32Jump[0] != 0xeb && Bpb.Ia32Jump[0] != 0x49) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  if (Bpb.ReservedSectors == 0 || Bpb.NoFats == 0 || Sectors == 0) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  if (Bpb.SectorsPerCluster != 1 &&\r
+      Bpb.SectorsPerCluster != 2 &&\r
+      Bpb.SectorsPerCluster != 4 &&\r
+      Bpb.SectorsPerCluster != 8 &&\r
+      Bpb.SectorsPerCluster != 16 &&\r
+      Bpb.SectorsPerCluster != 32 &&\r
+      Bpb.SectorsPerCluster != 64 &&\r
+      Bpb.SectorsPerCluster != 128\r
+      ) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  if (Volume->FatType == Fat32 && (SectorsPerFat == 0 || BpbEx.FsVersion != 0)) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  if (Bpb.Media != 0xf0 &&\r
+      Bpb.Media != 0xf8 &&\r
+      Bpb.Media != 0xf9 &&\r
+      Bpb.Media != 0xfb &&\r
+      Bpb.Media != 0xfc &&\r
+      Bpb.Media != 0xfd &&\r
+      Bpb.Media != 0xfe &&\r
+      Bpb.Media != 0xff &&\r
+      //\r
+      // FujitsuFMR\r
+      //\r
+      Bpb.Media != 0x00 &&\r
+      Bpb.Media != 0x01 &&\r
+      Bpb.Media != 0xfa\r
+      ) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  if (Volume->FatType != Fat32 && Bpb.RootEntries == 0) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+  //\r
+  // If this is fat32, refuse to mount mirror-disabled volumes\r
+  //\r
+  if (Volume->FatType == Fat32 && ((BpbEx.ExtendedFlags & 0x80) != 0)) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+  //\r
+  // Fill in the volume structure fields\r
+  // (Sectors & SectorsPerFat is computed earlier already)\r
+  //\r
+  Volume->ClusterSize = Bpb.SectorSize * Bpb.SectorsPerCluster;\r
+  Volume->RootEntries = Bpb.RootEntries;\r
+  Volume->SectorSize  = Bpb.SectorSize;\r
+\r
+  RootDirSectors = ((Volume->RootEntries * sizeof (FAT_DIRECTORY_ENTRY)) + (Volume->SectorSize - 1)) / Volume->SectorSize;\r
+\r
+  FatLba                  = Bpb.ReservedSectors;\r
+  RootLba                 = Bpb.NoFats * SectorsPerFat + FatLba;\r
+  FirstClusterLba         = RootLba + RootDirSectors;\r
+\r
+  Volume->VolumeSize      = MultU64x32 (Sectors, Volume->SectorSize);\r
+  Volume->FatPos          = MultU64x32 (FatLba, Volume->SectorSize);\r
+  Volume->RootDirPos      = MultU64x32 (RootLba, Volume->SectorSize);\r
+  Volume->FirstClusterPos = MultU64x32 (FirstClusterLba, Volume->SectorSize);\r
+  Volume->MaxCluster      = (UINT32) (Sectors - FirstClusterLba) / Bpb.SectorsPerCluster;\r
+  Volume->RootDirCluster  = BpbEx.RootDirFirstCluster;\r
+\r
+  //\r
+  // If this is not a fat32, determine if it's a fat16 or fat12\r
+  //\r
+  if (Volume->FatType != Fat32) {\r
+\r
+    if (Volume->MaxCluster >= 65525) {\r
+      return EFI_NOT_FOUND;\r
+    }\r
+\r
+    Volume->FatType = Volume->MaxCluster < 4085 ? Fat12 : Fat16;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Gets the next cluster in the cluster chain\r
+\r
+  @param  PrivateData            Global memory map for accessing global variables \r
+  @param  Volume                 The volume \r
+  @param  Cluster                The cluster \r
+  @param  NextCluster            The cluster number of the next cluster \r
+\r
+  @retval EFI_SUCCESS            The address is got \r
+  @retval EFI_INVALID_PARAMETER  ClusterNo exceeds the MaxCluster of the volume. \r
+  @retval EFI_DEVICE_ERROR       Read disk error\r
+\r
+**/\r
+EFI_STATUS\r
+FatGetNextCluster (\r
+  IN  PEI_FAT_PRIVATE_DATA  *PrivateData,\r
+  IN  PEI_FAT_VOLUME        *Volume,\r
+  IN  UINT32                Cluster,\r
+  OUT UINT32                *NextCluster\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  UINT64      FatEntryPos;\r
+  UINT32      Dummy;\r
+\r
+  *NextCluster = 0;\r
+\r
+  if (Volume->FatType == Fat32) {\r
+    FatEntryPos = Volume->FatPos + MultU64x32 (4, Cluster);\r
+\r
+    Status      = FatReadDisk (PrivateData, Volume->BlockDeviceNo, FatEntryPos, 4, NextCluster);\r
+    *NextCluster &= 0x0fffffff;\r
+\r
+    //\r
+    // Pad high bits for our FAT_CLUSTER_... macro definitions to work\r
+    //\r
+    if ((*NextCluster) >= 0x0ffffff7) {\r
+      *NextCluster |= (-1 &~0xf);\r
+    }\r
+\r
+  } else if (Volume->FatType == Fat16) {\r
+    FatEntryPos = Volume->FatPos + MultU64x32 (2, Cluster);\r
+\r
+    Status      = FatReadDisk (PrivateData, Volume->BlockDeviceNo, FatEntryPos, 2, NextCluster);\r
+\r
+    //\r
+    // Pad high bits for our FAT_CLUSTER_... macro definitions to work\r
+    //\r
+    if ((*NextCluster) >= 0xfff7) {\r
+      *NextCluster |= (-1 &~0xf);\r
+    }\r
+\r
+  } else {\r
+    FatEntryPos = Volume->FatPos + DivU64x32Remainder (MultU64x32 (3, Cluster), 2, &Dummy);\r
+\r
+    Status      = FatReadDisk (PrivateData, Volume->BlockDeviceNo, FatEntryPos, 2, NextCluster);\r
+\r
+    if ((Cluster & 0x01) != 0) {\r
+      *NextCluster = (*NextCluster) >> 4;\r
+    } else {\r
+      *NextCluster = (*NextCluster) & 0x0fff;\r
+    }\r
+    //\r
+    // Pad high bits for our FAT_CLUSTER_... macro definitions to work\r
+    //\r
+    if ((*NextCluster) >= 0x0ff7) {\r
+      *NextCluster |= (-1 &~0xf);\r
+    }\r
+  }\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+}\r
+\r
+\r
+/**\r
+  Set a file's CurrentPos and CurrentCluster, then compute StraightReadAmount.\r
+\r
+  @param  PrivateData            the global memory map \r
+  @param  File                   the file \r
+  @param  Pos                    the Position which is offset from the file's \r
+                                 CurrentPos \r
+\r
+  @retval EFI_SUCCESS            Success. \r
+  @retval EFI_INVALID_PARAMETER  Pos is beyond file's size. \r
+  @retval EFI_DEVICE_ERROR       Something error while accessing media.\r
+\r
+**/\r
+EFI_STATUS\r
+FatSetFilePos (\r
+  IN  PEI_FAT_PRIVATE_DATA  *PrivateData,\r
+  IN  PEI_FAT_FILE          *File,\r
+  IN  UINT32                Pos\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  UINT32      AlignedPos;\r
+  UINT32      Offset;\r
+  UINT32      Cluster;\r
+  UINT32      PrevCluster;\r
+\r
+  if (File->IsFixedRootDir) {\r
+\r
+    if (Pos >= MultU64x32 (File->Volume->RootEntries, 32) - File->CurrentPos) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    File->CurrentPos += Pos;\r
+    File->StraightReadAmount = (UINT32) (MultU64x32 (File->Volume->RootEntries, 32) - File->CurrentPos);\r
+\r
+  } else {\r
+\r
+    DivU64x32Remainder (File->CurrentPos, File->Volume->ClusterSize, &Offset);\r
+    AlignedPos = (UINT32) File->CurrentPos - (UINT32) Offset;\r
+\r
+    while\r
+    (\r
+      !FAT_CLUSTER_FUNCTIONAL (File->CurrentCluster) &&\r
+      AlignedPos + File->Volume->ClusterSize <= File->CurrentPos + Pos\r
+    ) {\r
+      AlignedPos += File->Volume->ClusterSize;\r
+      Status = FatGetNextCluster (\r
+                PrivateData,\r
+                File->Volume,\r
+                File->CurrentCluster,\r
+                &File->CurrentCluster\r
+                );\r
+      if (EFI_ERROR (Status)) {\r
+        return EFI_DEVICE_ERROR;\r
+      }\r
+    }\r
+\r
+    if (FAT_CLUSTER_FUNCTIONAL (File->CurrentCluster)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    File->CurrentPos += Pos;\r
+\r
+    File->StraightReadAmount  = 0;\r
+    Cluster                   = File->CurrentCluster;\r
+    while (!FAT_CLUSTER_FUNCTIONAL (Cluster)) {\r
+      File->StraightReadAmount += File->Volume->ClusterSize;\r
+      PrevCluster = Cluster;\r
+      Status      = FatGetNextCluster (PrivateData, File->Volume, Cluster, &Cluster);\r
+      if (EFI_ERROR (Status)) {\r
+        return EFI_DEVICE_ERROR;\r
+      }\r
+\r
+      if (Cluster != PrevCluster + 1) {\r
+        break;\r
+      }\r
+    }\r
+\r
+    DivU64x32Remainder (File->CurrentPos, File->Volume->ClusterSize, &Offset);\r
+    File->StraightReadAmount -= (UINT32) Offset;\r
+\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Reads file data. Updates the file's CurrentPos.\r
+\r
+  @param  PrivateData            Global memory map for accessing global variables \r
+  @param  File                   The file. \r
+  @param  Size                   The amount of data to read. \r
+  @param  Buffer                 The buffer storing the data. \r
+\r
+  @retval EFI_SUCCESS            The data is read. \r
+  @retval EFI_INVALID_PARAMETER  File is invalid. \r
+  @retval EFI_DEVICE_ERROR       Something error while accessing media.\r
+\r
+**/\r
+EFI_STATUS\r
+FatReadFile (\r
+  IN  PEI_FAT_PRIVATE_DATA  *PrivateData,\r
+  IN  PEI_FAT_FILE          *File,\r
+  IN  UINTN                 Size,\r
+  OUT VOID                  *Buffer\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  CHAR8       *BufferPtr;\r
+  UINT32      Offset;\r
+  UINT64      PhysicalAddr;\r
+  UINTN       Amount;\r
+\r
+  BufferPtr = Buffer;\r
+\r
+  if (File->IsFixedRootDir) {\r
+    //\r
+    // This is the fixed root dir in FAT12 and FAT16\r
+    //\r
+    if (File->CurrentPos + Size > File->Volume->RootEntries * sizeof (FAT_DIRECTORY_ENTRY)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    Status = FatReadDisk (\r
+              PrivateData,\r
+              File->Volume->BlockDeviceNo,\r
+              File->Volume->RootDirPos + File->CurrentPos,\r
+              Size,\r
+              Buffer\r
+              );\r
+    File->CurrentPos += (UINT32) Size;\r
+    return Status;\r
+\r
+  } else {\r
+\r
+    if ((File->Attributes & FAT_ATTR_DIRECTORY) == 0) {\r
+      Size = Size < (File->FileSize - File->CurrentPos) ? Size : (UINTN) (File->FileSize - File->CurrentPos);\r
+    }\r
+    //\r
+    // This is a normal cluster based file\r
+    //\r
+    while (Size != 0) {\r
+      DivU64x32Remainder (File->CurrentPos, File->Volume->ClusterSize, &Offset);\r
+      PhysicalAddr  = File->Volume->FirstClusterPos + MultU64x32 (File->Volume->ClusterSize, File->CurrentCluster - 2);\r
+\r
+      Amount        = File->StraightReadAmount;\r
+      Amount        = Size > Amount ? Amount : Size;\r
+      Status = FatReadDisk (\r
+                PrivateData,\r
+                File->Volume->BlockDeviceNo,\r
+                PhysicalAddr + Offset,\r
+                Amount,\r
+                BufferPtr\r
+                );\r
+      if (EFI_ERROR (Status)) {\r
+        return EFI_DEVICE_ERROR;\r
+      }\r
+      //\r
+      // Advance the file's current pos and current cluster\r
+      //\r
+      FatSetFilePos (PrivateData, File, (UINT32) Amount);\r
+\r
+      BufferPtr += Amount;\r
+      Size -= Amount;\r
+    }\r
+\r
+    return EFI_SUCCESS;\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  This function reads the next item in the parent directory and\r
+  initializes the output parameter SubFile (CurrentPos is initialized to 0).\r
+  The function updates the CurrentPos of the parent dir to after the item read.\r
+  If no more items were found, the function returns EFI_NOT_FOUND.\r
+\r
+  @param  PrivateData            Global memory map for accessing global variables \r
+  @param  ParentDir              The parent directory. \r
+  @param  SubFile                The File structure containing the sub file that \r
+                                 is caught. \r
+\r
+  @retval EFI_SUCCESS            The next sub file is obtained. \r
+  @retval EFI_INVALID_PARAMETER  The ParentDir is not a directory. \r
+  @retval EFI_NOT_FOUND          No more sub file exists. \r
+  @retval EFI_DEVICE_ERROR       Something error while accessing media.\r
+\r
+**/\r
+EFI_STATUS\r
+FatReadNextDirectoryEntry (\r
+  IN  PEI_FAT_PRIVATE_DATA  *PrivateData,\r
+  IN  PEI_FAT_FILE          *ParentDir,\r
+  OUT PEI_FAT_FILE          *SubFile\r
+  )\r
+{\r
+  EFI_STATUS          Status;\r
+  FAT_DIRECTORY_ENTRY DirEntry;\r
+  CHAR16              *Pos;\r
+  CHAR16              BaseName[9];\r
+  CHAR16              Ext[4];\r
+\r
+  ZeroMem ((UINT8 *) SubFile, sizeof (PEI_FAT_FILE));\r
+\r
+  //\r
+  // Pick a valid directory entry\r
+  //\r
+  while (1) {\r
+    //\r
+    // Read one entry\r
+    //\r
+    Status = FatReadFile (PrivateData, ParentDir, 32, &DirEntry);\r
+    if (EFI_ERROR (Status)) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+    //\r
+    // We only search for *FILE* in root directory\r
+    // Long file name entry is *NOT* supported\r
+    //\r
+    if ((DirEntry.Attributes == FAT_ATTR_DIRECTORY) || (DirEntry.Attributes == FAT_ATTR_LFN)) {\r
+      continue;\r
+    }\r
+    //\r
+    // if this is a terminator dir entry, just return EFI_NOT_FOUND\r
+    //\r
+    if (DirEntry.FileName[0] == EMPTY_ENTRY_MARK) {\r
+      return EFI_NOT_FOUND;\r
+    }\r
+    //\r
+    // If this not an invalid entry neither an empty entry, this is what we want.\r
+    // otherwise we will start a new loop to continue to find something meaningful\r
+    //\r
+    if ((UINT8) DirEntry.FileName[0] != DELETE_ENTRY_MARK) {\r
+      break;\r
+    }\r
+  }\r
+  //\r
+  // fill in the output parameter\r
+  //\r
+  EngFatToStr (8, DirEntry.FileName, BaseName);\r
+  EngFatToStr (3, DirEntry.FileName + 8, Ext);\r
+\r
+  Pos = (UINT16 *) SubFile->FileName;\r
+  SetMem ((UINT8 *) Pos, FAT_MAX_FILE_NAME_LENGTH, 0);\r
+  CopyMem ((UINT8 *) Pos, (UINT8 *) BaseName, 2 * (StrLen (BaseName) + 1));\r
+\r
+  if (Ext[0] != 0) {\r
+    Pos += StrLen (BaseName);\r
+    *Pos = '.';\r
+    Pos++;\r
+    CopyMem ((UINT8 *) Pos, (UINT8 *) Ext, 2 * (StrLen (Ext) + 1));\r
+  }\r
+\r
+  SubFile->Attributes     = DirEntry.Attributes;\r
+  SubFile->CurrentCluster = DirEntry.FileCluster;\r
+  if (ParentDir->Volume->FatType == Fat32) {\r
+    SubFile->CurrentCluster |= DirEntry.FileClusterHigh << 16;\r
+  }\r
+\r
+  SubFile->CurrentPos       = 0;\r
+  SubFile->FileSize         = DirEntry.FileSize;\r
+  SubFile->StartingCluster  = SubFile->CurrentCluster;\r
+  SubFile->Volume           = ParentDir->Volume;\r
+\r
+  if (SubFile->StartingCluster != 0) {\r
+    Status = FatSetFilePos (PrivateData, SubFile, 0);\r
+  }\r
+  //\r
+  // in Pei phase, time parameters do not need to be filled for minimum use.\r
+  //\r
+  return Status;\r
+}\r