alder_lake_bios/Insyde/InsydeModulePkg/Universal/FileAccess/FileAccessPei/FatLib.c

646 lines
16 KiB
C

/** @file
General purpose supporting routines for file access PEIM
;******************************************************************************
;* Copyright (c) 2014 - 2019, Insyde Software Corp. All Rights Reserved.
;*
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
;* transmit, broadcast, present, recite, release, license or otherwise exploit
;* any part of this publication in any form, by any means, without the prior
;* written permission of Insyde Software Corporation.
;*
;******************************************************************************
*/
/*++
This file contains 'Framework Code' and is licensed as such
under the terms of your license agreement with Intel or your
vendor. This file may not be modified, except as allowed by
additional terms of your license agreement.
--*/
/*++
Copyright (c) 1999 - 2002 Intel Corporation. All rights reserved
This software and associated documentation (if any) is furnished
under a license and may only be used or copied in accordance
with the terms of the license. Except as permitted by such
license, no part of this software or documentation may be
reproduced, stored in a retrieval system, or transmitted in any
form or by any means without the express written consent of
Intel Corporation.
Module Name:
FatLib.c
Abstract:
General purpose supporting routines for file access PEIM
--*/
#include "FatPeim.h"
/**
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.
**/
STATIC
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->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;
}
/**
Write a block of data to 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.
@retval EFI_UNSUPPORTED The block device not support block write function.
**/
EFI_STATUS
FatWriteBlock (
IN PEI_FAT_PRIVATE_DATA *PrivateData,
IN UINTN BlockDeviceNo,
IN EFI_PEI_LBA Lba,
IN UINTN BufferSize,
IN 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) {
if (BlockDev->H2OBlockIo == NULL) {
//
// This device not support write function.
//
return EFI_UNSUPPORTED;
}
Status = BlockDev->H2OBlockIo->WriteBlocks (
(EFI_PEI_SERVICES **) GetPeiServicesTablePointer (),
BlockDev->H2OBlockIo,
BlockDev->PhysicalDevNo,
Lba,
BufferSize,
Buffer
);
} else {
Status = FatWriteDisk (
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;
}
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;
if (!Size) {
return EFI_SUCCESS;
}
//
// 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;
}
/**
Disk Writing.
@param PrivateData the global memory map;
@param BlockDeviceNo the block device to write;
@param StartingAddress the starting address. BYTE address
@param Size the amount of data to write.
@param Buffer the buffer holding the data
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_DEVICE_ERROR Something error.
**/
EFI_STATUS
FatWriteDisk (
IN PEI_FAT_PRIVATE_DATA *PrivateData,
IN UINTN BlockDeviceNo,
IN UINT64 StartingAddress,
IN UINTN Size,
IN 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;
//
// Write underrun
// Offset starts in the middle of an Lba, so do read modify write.
//
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 (CachePtr + Offset, BufferPtr, Amount);
Status = FatWriteBlock (PrivateData, BlockDeviceNo, Lba, BlockSize, CachePtr);
if (EFI_ERROR (Status)) {
return Status;
}
if (Size == Amount) {
return EFI_SUCCESS;
}
Size -= Amount;
BufferPtr += Amount;
StartingAddress += Amount;
Lba += 1;
if (!Size) {
return EFI_SUCCESS;
}
//
// Write aligned parts
//
OverRunLba = Lba + DivU64x32Remainder (Size, BlockSize, &Offset);
Size -= Offset;
Status = FatWriteBlock (PrivateData, BlockDeviceNo, Lba, Size, BufferPtr);
if (EFI_ERROR (Status)) {
return Status;
}
BufferPtr += Size;
//
// Write overrun
//
if (Offset != 0) {
Status = FatGetCacheBlock (PrivateData, BlockDeviceNo, OverRunLba, &CachePtr);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
CopyMem (CachePtr, BufferPtr, Offset);
Status = FatWriteBlock (PrivateData, BlockDeviceNo, OverRunLba, BlockSize, CachePtr);
if (EFI_ERROR (Status)) {
return Status;
}
}
return Status;
}
/**
Calculating FAT directory entry checksum
@param Dir Pointer to the FAT_DIRECTORY_ENTRY structure
@return The calculated directory entry checksum
**/
UINT8
FatDirEntryChecksum (
FAT_DIRECTORY_ENTRY *Dir
)
{
UINT8 Check;
UINTN Index;
//
// Computes checksum for LFN
//
Check = 0;
for (Index = 0; Index < 11; Index++) {
Check = (UINT8) (((Check & 1) ? 0x80 : 0) + (Check >> 1) + Dir->FileName[Index]);
}
return Check;
}
/**
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) {
*String = *Fat;
String += 1;
Fat += 1;
FatSize -= 1;
}
//
// Strip off trailing blanks
//
while (String > Str) {
if (*(String - 1) == ' '){
String -= 1;
} else {
break;
}
}
*String = 0;
}
/**
Converts a Null-terminated string to legal characters in a FAT
filename using an OEM character set.
@param This Protocol instance pointer.
@param String A pointer to a Null-terminated string. The string must
be preallocated to hold FatSize characters.
@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.
@retval TRUE Fat is a Long File Name
@retval FALSE Fat is an 8.3 file name
**/
BOOLEAN
EFIAPI
EngStrToFat (
IN CHAR16 *String,
IN UINTN FatSize,
OUT CHAR8 *Fat
)
{
BOOLEAN SpecialCharExist;
CHAR16 Tmp;
SpecialCharExist = FALSE;
while ((*String != 0) && (FatSize != 0)) {
if (*String > 0x7F ||
*String < 0x20 ||
*String == '\"' ||
*String == '*' ||
*String == '/' ||
*String == ':' ||
*String == '<' ||
*String == '>' ||
*String == '?' ||
*String == '\\' ||
*String == '|') {
SpecialCharExist = TRUE;
*(Fat++) = '_';
String++;
} else {
if (*String >= 'a' && *String <= 'z') {
Tmp = *String - 0x20;
String++;
*(Fat++) = (CHAR8)Tmp;
} else {
*(Fat++) = (CHAR8)*(String++);
}
}
FatSize--;
}
//
// Do not terminate that fat string
//
return SpecialCharExist;
}
/**
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.
@retval TRUE Str1 and Str2 are identical with case-insensitive comparison
@retval FALSE Str1 and Str2 are different
**/
BOOLEAN
EngStriColl (
IN PEI_FAT_PRIVATE_DATA *PrivateData,
IN CHAR16 *s1,
IN CHAR16 *s2
)
{
while (*s1) {
if (ToUpper (*s1) != ToUpper (*s2)) {
return FALSE;
}
s1++;
s2++;
}
return (BOOLEAN) ((*s2) ? FALSE : TRUE);
}