Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Feng Tian <feng.tian@intel.com> Reviewed-by: Ruiyu Ni <ruiyu.ni@intel.com> (based on FatPkg commit add52adf722d2b0f1db4c8780a30289dacd59e02) [jordan.l.justen@intel.com: Use script to relicense to 2-clause BSD] Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Jordan Justen <jordan.l.justen@intel.com> Acked-by: Mark Doran <mark.doran@intel.com> Acked-by: Laszlo Ersek <lersek@redhat.com>
		
			
				
	
	
		
			377 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			377 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
  General purpose supporting routines for FAT recovery PEIM
 | 
						|
 | 
						|
Copyright (c) 2006 - 2015, 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);
 | 
						|
    //
 | 
						|
    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);
 | 
						|
}
 |