ftwlite.c

来自「EFI BIOS是Intel提出的下一代的BIOS标准。这里上传的Edk源代码是」· C语言 代码 · 共 971 行 · 第 1/3 页

C
971
字号
/*++

Copyright (c) 2004 - 2005, Intel Corporation                                                         
All rights reserved. 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.  


Module Name:

  FtwLite.c

Abstract:

  This is a simple fault tolerant write driver, based on PlatformFd library.
  And it only supports write BufferSize <= SpareAreaLength.

  This boot service only protocol provides fault tolerant write capability for 
  block devices.  The protocol has internal non-volatile intermediate storage 
  of the data and private information. It should be able to recover 
  automatically from a critical fault, such as power failure. 

Notes:

  The implementation uses an FTW Lite (Fault Tolerant Write) Work Space. 
  This work space is a memory copy of the work space on the Woring Block,
  the size of the work space is the FTW_WORK_SPACE_SIZE bytes.

--*/

#include "FtwLite.h"
#include "PeiHob.h"
#include "EfiFlashMap.h"
#include "EfiHobLib.h"
#include EFI_GUID_DEFINITION (Hob)
#include EFI_GUID_DEFINITION (FlashMapHob)

//
// In write function, we should check the target range to prevent the user
// from writing Spare block and Working space directly.
//
//
// Fault Tolerant Write Protocol API
//
EFI_STATUS
EFIAPI
FtwLiteWrite (
  IN EFI_FTW_LITE_PROTOCOL                 *This,
  IN EFI_HANDLE                            FvbHandle,
  IN EFI_LBA                               Lba,
  IN UINTN                                 Offset,
  IN OUT UINTN                             *NumBytes,
  IN VOID                                  *Buffer
  )
/*++

Routine Description:
    Starts a target block update. This function will record data about write 
    in fault tolerant storage and will complete the write in a recoverable 
    manner, ensuring at all times that either the original contents or 
    the modified contents are available.

Arguments:
    This             - Calling context
    FvbHandle        - The handle of FVB protocol that provides services for 
                       reading, writing, and erasing the target block.
    Lba              - The logical block address of the target block.  
    Offset           - The offset within the target block to place the data.
    NumBytes         - The number of bytes to write to the target block.
    Buffer           - The data to write.

Returns:
    EFI_SUCCESS          - The function completed successfully
    EFI_BAD_BUFFER_SIZE  - The write would span a target block, which is not 
                           a valid action.
    EFI_ACCESS_DENIED    - No writes have been allocated.
    EFI_NOT_FOUND        - Cannot find FVB by handle.
    EFI_OUT_OF_RESOURCES - Cannot allocate memory.
    EFI_ABORTED          - The function could not complete successfully.

--*/
{
  EFI_STATUS                          Status;
  EFI_FTW_LITE_DEVICE                 *FtwLiteDevice;
  EFI_FTW_LITE_RECORD                 *Record;
  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *Fvb;
  EFI_PHYSICAL_ADDRESS                FvbPhysicalAddress;
  UINTN                               MyLength;
  UINTN                               MyOffset;
  UINTN                               MyBufferSize;
  UINT8                               *MyBuffer;
  UINTN                               SpareBufferSize;
  UINT8                               *SpareBuffer;
  UINTN                               Index;
  UINT8                               *Ptr;
  EFI_DEV_PATH_PTR                    DevPtr;

  //
  // Refresh work space and get last record
  //
  FtwLiteDevice = FTW_LITE_CONTEXT_FROM_THIS (This);
  Status        = WorkSpaceRefresh (FtwLiteDevice);
  if (EFI_ERROR (Status)) {
    return EFI_ABORTED;
  }

  Record = FtwLiteDevice->FtwLastRecord;

  //
  // Check the flags of last write record
  //
  if ((Record->WriteAllocated == FTW_VALID_STATE) || (Record->SpareCompleted == FTW_VALID_STATE)) {
    return EFI_ACCESS_DENIED;
  }
  //
  // IF former record has completed, THEN use next record
  //
  if (Record->WriteCompleted == FTW_VALID_STATE) {
    Record++;
    FtwLiteDevice->FtwLastRecord = Record;
  }

  MyOffset = (UINT8 *) Record - FtwLiteDevice->FtwWorkSpace;

  //
  // Check if the input data can fit within the target block
  //
  if ((Offset +*NumBytes) > FtwLiteDevice->SpareAreaLength) {
    return EFI_BAD_BUFFER_SIZE;
  }
  //
  // Check if there is enough free space for allocate a record
  //
  if ((MyOffset + WRITE_TOTAL_SIZE) > FtwLiteDevice->FtwWorkSpaceSize) {
    Status = FtwReclaimWorkSpace (FtwLiteDevice);
    if (EFI_ERROR (Status)) {
      DEBUG ((EFI_D_ERROR, "FtwLite: Reclaim work space - %r", Status));
      return EFI_ABORTED;
    }
  }
  //
  // Get the FVB protocol by handle
  //
  Status = FtwGetFvbByHandle (FvbHandle, &Fvb);
  if (EFI_ERROR (Status)) {
    return EFI_NOT_FOUND;
  }
  //
  // Allocate a write record in workspace.
  // Update Header->WriteAllocated as VALID
  //
  Status = FtwUpdateFvState (
            FtwLiteDevice->FtwFvBlock,
            FtwLiteDevice->FtwWorkSpaceLba,
            FtwLiteDevice->FtwWorkSpaceBase + MyOffset,
            WRITE_ALLOCATED
            );

  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_FTW_LITE, "FtwLite: Allocate record - %r\n", Status));
    return EFI_ABORTED;
  }

  Record->WriteAllocated = FTW_VALID_STATE;

  //
  // Prepare data of write record, filling DevPath with memory mapped address.
  //
  DevPtr.MemMap                 = (MEMMAP_DEVICE_PATH *) &Record->DevPath;
  DevPtr.MemMap->Header.Type    = HARDWARE_DEVICE_PATH;
  DevPtr.MemMap->Header.SubType = HW_MEMMAP_DP;
  SetDevicePathNodeLength (&DevPtr.MemMap->Header, sizeof (MEMMAP_DEVICE_PATH));

  Status = Fvb->GetPhysicalAddress (Fvb, &FvbPhysicalAddress);
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_FTW_LITE, "FtwLite: Get FVB physical address - %r\n", Status));
    return EFI_ABORTED;
  }

  DevPtr.MemMap->MemoryType       = EfiMemoryMappedIO;
  DevPtr.MemMap->StartingAddress  = FvbPhysicalAddress;
  DevPtr.MemMap->EndingAddress    = FvbPhysicalAddress +*NumBytes;
  //
  // ignored!
  //
  Record->Lba       = Lba;
  Record->Offset    = Offset;
  Record->NumBytes  = *NumBytes;

  //
  // Write the record to the work space.
  //
  MyOffset  = (UINT8 *) Record - FtwLiteDevice->FtwWorkSpace;
  MyLength  = FTW_LITE_RECORD_SIZE;

  Status = FtwLiteDevice->FtwFvBlock->Write (
                                        FtwLiteDevice->FtwFvBlock,
                                        FtwLiteDevice->FtwWorkSpaceLba,
                                        FtwLiteDevice->FtwWorkSpaceBase + MyOffset,
                                        &MyLength,
                                        (UINT8 *) Record
                                        );
  if (EFI_ERROR (Status)) {
    return EFI_ABORTED;
  }
  //
  // Record has been written to working block, then write data.
  //
  //
  // Allocate a memory buffer
  //
  MyBufferSize  = FtwLiteDevice->SpareAreaLength;
  MyBuffer      = EfiLibAllocatePool (MyBufferSize);
  if (MyBuffer == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }
  //
  // Starting at Lba, if the number of the rest blocks on Fvb is less
  // than NumberOfSpareBlock.
  //
  //
  // Read all original data from target block to memory buffer
  //
  if (IsInWorkingBlock (FtwLiteDevice, Fvb, Lba)) {
    //
    // If target block falls into working block, we must follow the process of
    // updating working block.
    //
    Ptr = MyBuffer;
    for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {
      MyLength = FtwLiteDevice->SizeOfSpareBlock;
      Status = FtwLiteDevice->FtwFvBlock->Read (
                                            FtwLiteDevice->FtwFvBlock,
                                            FtwLiteDevice->FtwWorkBlockLba + Index,
                                            0,
                                            &MyLength,
                                            Ptr
                                            );
      if (EFI_ERROR (Status)) {
        gBS->FreePool (MyBuffer);
        return EFI_ABORTED;
      }

      Ptr += MyLength;
    }
    //
    // Update Offset by adding the offset from the start LBA of working block to
    // the target LBA. The target block can not span working block!
    //
    Offset = (((UINTN) (Lba - FtwLiteDevice->FtwWorkBlockLba)) * FtwLiteDevice->SizeOfSpareBlock + Offset);
    ASSERT ((Offset +*NumBytes) <= FtwLiteDevice->SpareAreaLength);

  } else {

    Ptr = MyBuffer;
    for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {
      MyLength  = FtwLiteDevice->SizeOfSpareBlock;
      Status    = Fvb->Read (Fvb, Lba + Index, 0, &MyLength, Ptr);
      if (EFI_ERROR (Status)) {
        gBS->FreePool (MyBuffer);
        return EFI_ABORTED;
      }

      Ptr += MyLength;
    }
  }
  //
  // Overwrite the updating range data with
  // the input buffer content
  //
  EfiCopyMem (MyBuffer + Offset, Buffer, *NumBytes);

  //
  // Try to keep the content of spare block
  // Save spare block into a spare backup memory buffer (Sparebuffer)
  //
  SpareBufferSize = FtwLiteDevice->SpareAreaLength;
  SpareBuffer     = EfiLibAllocatePool (SpareBufferSize);
  if (SpareBuffer == NULL) {
    gBS->FreePool (MyBuffer);
    return EFI_OUT_OF_RESOURCES;
  }

  Ptr = SpareBuffer;
  for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {
    MyLength = FtwLiteDevice->SizeOfSpareBlock;
    Status = FtwLiteDevice->FtwBackupFvb->Read (
                                            FtwLiteDevice->FtwBackupFvb,
                                            FtwLiteDevice->FtwSpareLba + Index,
                                            0,
                                            &MyLength,
                                            Ptr
                                            );
    if (EFI_ERROR (Status)) {
      gBS->FreePool (MyBuffer);
      gBS->FreePool (SpareBuffer);
      return EFI_ABORTED;
    }

    Ptr += MyLength;
  }
  //
  // Write the memory buffer to spare block
  // Don't forget to erase Flash first.
  //
  Status  = FtwEraseSpareBlock (FtwLiteDevice);
  Ptr     = MyBuffer;
  for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {
    MyLength = FtwLiteDevice->SizeOfSpareBlock;
    Status = FtwLiteDevice->FtwBackupFvb->Write (
                                            FtwLiteDevice->FtwBackupFvb,
                                            FtwLiteDevice->FtwSpareLba + Index,
                                            0,
                                            &MyLength,
                                            Ptr
                                            );
    if (EFI_ERROR (Status)) {
      gBS->FreePool (MyBuffer);
      gBS->FreePool (SpareBuffer);
      return EFI_ABORTED;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?