/** @file | |
General purpose supporting 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" | |
#define CHAR_FAT_VALID 0x01 | |
/** | |
Converts a union code character to upper case. | |
This functions converts a unicode character to upper case. | |
If the input Letter is not a lower-cased letter, | |
the original value is returned. | |
@param Letter The input unicode character. | |
@return The upper cased letter. | |
**/ | |
CHAR16 | |
ToUpper ( | |
IN CHAR16 Letter | |
) | |
{ | |
if (('a' <= Letter) && (Letter <= 'z')) { | |
Letter = (CHAR16)(Letter - 0x20); | |
} | |
return Letter; | |
} | |
/** | |
Reads a block of data from the block device by calling | |
underlying Block I/O service. | |
@param PrivateData Global memory map for accessing global variables | |
@param BlockDeviceNo The index for the block device number. | |
@param Lba The logic block address to read data from. | |
@param BufferSize The size of data in byte to read. | |
@param Buffer The buffer of the | |
@retval EFI_DEVICE_ERROR The specified block device number exceeds the maximum | |
device number. | |
@retval EFI_DEVICE_ERROR The maximum address has exceeded the maximum address | |
of the block device. | |
**/ | |
EFI_STATUS | |
FatReadBlock ( | |
IN PEI_FAT_PRIVATE_DATA *PrivateData, | |
IN UINTN BlockDeviceNo, | |
IN EFI_PEI_LBA Lba, | |
IN UINTN BufferSize, | |
OUT VOID *Buffer | |
) | |
{ | |
EFI_STATUS Status; | |
PEI_FAT_BLOCK_DEVICE *BlockDev; | |
if (BlockDeviceNo > PEI_FAT_MAX_BLOCK_DEVICE - 1) { | |
return EFI_DEVICE_ERROR; | |
} | |
Status = EFI_SUCCESS; | |
BlockDev = &(PrivateData->BlockDevice[BlockDeviceNo]); | |
if (BufferSize > MultU64x32 (BlockDev->LastBlock - Lba + 1, BlockDev->BlockSize)) { | |
return EFI_DEVICE_ERROR; | |
} | |
if (!BlockDev->Logical) { | |
// | |
// Status = BlockDev->ReadFunc | |
// (PrivateData->PeiServices, BlockDev->PhysicalDevNo, Lba, BufferSize, Buffer); | |
// | |
if (BlockDev->BlockIo2 != NULL) { | |
Status = BlockDev->BlockIo2->ReadBlocks ( | |
(EFI_PEI_SERVICES **)GetPeiServicesTablePointer (), | |
BlockDev->BlockIo2, | |
BlockDev->PhysicalDevNo, | |
Lba, | |
BufferSize, | |
Buffer | |
); | |
} else { | |
Status = BlockDev->BlockIo->ReadBlocks ( | |
(EFI_PEI_SERVICES **)GetPeiServicesTablePointer (), | |
BlockDev->BlockIo, | |
BlockDev->PhysicalDevNo, | |
Lba, | |
BufferSize, | |
Buffer | |
); | |
} | |
} else { | |
Status = FatReadDisk ( | |
PrivateData, | |
BlockDev->ParentDevNo, | |
BlockDev->StartingPos + MultU64x32 (Lba, BlockDev->BlockSize), | |
BufferSize, | |
Buffer | |
); | |
} | |
return Status; | |
} | |
/** | |
Find a cache block designated to specific Block device and Lba. | |
If not found, invalidate an oldest one and use it. (LRU cache) | |
@param PrivateData the global memory map. | |
@param BlockDeviceNo the Block device. | |
@param Lba the Logical Block Address | |
@param CachePtr Ptr to the starting address of the memory holding the | |
data; | |
@retval EFI_SUCCESS The function completed successfully. | |
@retval EFI_DEVICE_ERROR Something error while accessing media. | |
**/ | |
EFI_STATUS | |
FatGetCacheBlock ( | |
IN PEI_FAT_PRIVATE_DATA *PrivateData, | |
IN UINTN BlockDeviceNo, | |
IN UINT64 Lba, | |
OUT CHAR8 **CachePtr | |
) | |
{ | |
EFI_STATUS Status; | |
PEI_FAT_CACHE_BUFFER *CacheBuffer; | |
INTN Index; | |
STATIC UINT8 Seed; | |
Status = EFI_SUCCESS; | |
CacheBuffer = NULL; | |
// | |
// go through existing cache buffers | |
// | |
for (Index = 0; Index < PEI_FAT_CACHE_SIZE; Index++) { | |
CacheBuffer = &(PrivateData->CacheBuffer[Index]); | |
if (CacheBuffer->Valid && (CacheBuffer->BlockDeviceNo == BlockDeviceNo) && (CacheBuffer->Lba == Lba)) { | |
break; | |
} | |
} | |
if (Index < PEI_FAT_CACHE_SIZE) { | |
*CachePtr = (CHAR8 *)CacheBuffer->Buffer; | |
return EFI_SUCCESS; | |
} | |
// | |
// We have to find an invalid cache buffer | |
// | |
for (Index = 0; Index < PEI_FAT_CACHE_SIZE; Index++) { | |
if (!PrivateData->CacheBuffer[Index].Valid) { | |
break; | |
} | |
} | |
// | |
// Use the cache buffer | |
// | |
if (Index == PEI_FAT_CACHE_SIZE) { | |
Index = (Seed++) % PEI_FAT_CACHE_SIZE; | |
} | |
// | |
// Current device ID should be less than maximum device ID. | |
// | |
if (BlockDeviceNo >= PEI_FAT_MAX_BLOCK_DEVICE) { | |
return EFI_DEVICE_ERROR; | |
} | |
CacheBuffer = &(PrivateData->CacheBuffer[Index]); | |
CacheBuffer->BlockDeviceNo = BlockDeviceNo; | |
CacheBuffer->Lba = Lba; | |
CacheBuffer->Size = PrivateData->BlockDevice[BlockDeviceNo].BlockSize; | |
// | |
// Read in the data | |
// | |
Status = FatReadBlock ( | |
PrivateData, | |
BlockDeviceNo, | |
Lba, | |
CacheBuffer->Size, | |
CacheBuffer->Buffer | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_DEVICE_ERROR; | |
} | |
CacheBuffer->Valid = TRUE; | |
*CachePtr = (CHAR8 *)CacheBuffer->Buffer; | |
return Status; | |
} | |
/** | |
Disk reading. | |
@param PrivateData the global memory map; | |
@param BlockDeviceNo the block device to read; | |
@param StartingAddress the starting address. | |
@param Size the amount of data to read. | |
@param Buffer the buffer holding the data | |
@retval EFI_SUCCESS The function completed successfully. | |
@retval EFI_DEVICE_ERROR Something error. | |
**/ | |
EFI_STATUS | |
FatReadDisk ( | |
IN PEI_FAT_PRIVATE_DATA *PrivateData, | |
IN UINTN BlockDeviceNo, | |
IN UINT64 StartingAddress, | |
IN UINTN Size, | |
OUT VOID *Buffer | |
) | |
{ | |
EFI_STATUS Status; | |
UINT32 BlockSize; | |
CHAR8 *BufferPtr; | |
CHAR8 *CachePtr; | |
UINT32 Offset; | |
UINT64 Lba; | |
UINT64 OverRunLba; | |
UINTN Amount; | |
Status = EFI_SUCCESS; | |
BufferPtr = Buffer; | |
BlockSize = PrivateData->BlockDevice[BlockDeviceNo].BlockSize; | |
// | |
// Read underrun | |
// | |
Lba = DivU64x32Remainder (StartingAddress, BlockSize, &Offset); | |
Status = FatGetCacheBlock (PrivateData, BlockDeviceNo, Lba, &CachePtr); | |
if (EFI_ERROR (Status)) { | |
return EFI_DEVICE_ERROR; | |
} | |
Amount = Size < (BlockSize - Offset) ? Size : (BlockSize - Offset); | |
CopyMem (BufferPtr, CachePtr + Offset, Amount); | |
if (Size == Amount) { | |
return EFI_SUCCESS; | |
} | |
Size -= Amount; | |
BufferPtr += Amount; | |
StartingAddress += Amount; | |
Lba += 1; | |
// | |
// Read aligned parts | |
// | |
OverRunLba = Lba + DivU64x32Remainder (Size, BlockSize, &Offset); | |
Size -= Offset; | |
Status = FatReadBlock (PrivateData, BlockDeviceNo, Lba, Size, BufferPtr); | |
if (EFI_ERROR (Status)) { | |
return EFI_DEVICE_ERROR; | |
} | |
BufferPtr += Size; | |
// | |
// Read overrun | |
// | |
if (Offset != 0) { | |
Status = FatGetCacheBlock (PrivateData, BlockDeviceNo, OverRunLba, &CachePtr); | |
if (EFI_ERROR (Status)) { | |
return EFI_DEVICE_ERROR; | |
} | |
CopyMem (BufferPtr, CachePtr, Offset); | |
} | |
return Status; | |
} | |
/** | |
This version is different from the version in Unicode collation | |
protocol in that this version strips off trailing blanks. | |
Converts an 8.3 FAT file name using an OEM character set | |
to a Null-terminated Unicode string. | |
Here does not expand DBCS FAT chars. | |
@param FatSize The size of the string Fat in bytes. | |
@param Fat A pointer to a Null-terminated string that contains | |
an 8.3 file name using an OEM character set. | |
@param Str A pointer to a Null-terminated Unicode string. The | |
string must be allocated in advance to hold FatSize | |
Unicode characters | |
**/ | |
VOID | |
EngFatToStr ( | |
IN UINTN FatSize, | |
IN CHAR8 *Fat, | |
OUT CHAR16 *Str | |
) | |
{ | |
CHAR16 *String; | |
String = Str; | |
// | |
// No DBCS issues, just expand and add null terminate to end of string | |
// | |
while (*Fat != 0 && FatSize != 0) { | |
if (*Fat == ' ') { | |
break; | |
} | |
*String = *Fat; | |
String += 1; | |
Fat += 1; | |
FatSize -= 1; | |
} | |
*String = 0; | |
} | |
/** | |
Performs a case-insensitive comparison of two Null-terminated Unicode strings. | |
@param PrivateData Global memory map for accessing global variables | |
@param Str1 First string to perform case insensitive comparison. | |
@param Str2 Second string to perform case insensitive comparison. | |
**/ | |
BOOLEAN | |
EngStriColl ( | |
IN PEI_FAT_PRIVATE_DATA *PrivateData, | |
IN CHAR16 *Str1, | |
IN CHAR16 *Str2 | |
) | |
{ | |
CHAR16 UpperS1; | |
CHAR16 UpperS2; | |
UpperS1 = ToUpper (*Str1); | |
UpperS2 = ToUpper (*Str2); | |
while (*Str1 != 0) { | |
if (UpperS1 != UpperS2) { | |
return FALSE; | |
} | |
Str1++; | |
Str2++; | |
UpperS1 = ToUpper (*Str1); | |
UpperS2 = ToUpper (*Str2); | |
} | |
return (BOOLEAN)((*Str2 != 0) ? FALSE : TRUE); | |
} |