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