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