mirror of https://github.com/acidanthera/audk.git
366 lines
9.7 KiB
C
366 lines
9.7 KiB
C
/** @file
|
|
General purpose supporting routines for FAT recovery PEIM
|
|
|
|
Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR>
|
|
|
|
This program and the accompanying materials are licensed and made available
|
|
under the terms and conditions of the BSD License which accompanies this
|
|
distribution. The full text of the license may be found at
|
|
http://opensource.org/licenses/bsd-license.php
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
|
|
**/
|
|
|
|
#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);
|
|
//
|
|
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);
|
|
}
|