]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Universal/FirmwareVolume/FaultTolerantWriteDxe/FtwWorkSpace.c
Adjust directory structures.
[mirror_edk2.git] / MdeModulePkg / Universal / FirmwareVolume / FaultTolerantWriteDxe / FtwWorkSpace.c
diff --git a/MdeModulePkg/Universal/FirmwareVolume/FaultTolerantWriteDxe/FtwWorkSpace.c b/MdeModulePkg/Universal/FirmwareVolume/FaultTolerantWriteDxe/FtwWorkSpace.c
new file mode 100644 (file)
index 0000000..00d7d8e
--- /dev/null
@@ -0,0 +1,561 @@
+/*++\r
+\r
+Copyright (c) 2006 - 2007, Intel Corporation                                                         \r
+All rights reserved. This program and the accompanying materials                          \r
+are licensed and made available under the terms and conditions of the BSD License         \r
+which accompanies this 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
+Module Name:\r
+\r
+  FtwWorkSpace.c\r
+  \r
+Abstract:\r
+\r
+Revision History\r
+\r
+--*/\r
+\r
+\r
+#include <FtwLite.h>\r
+\r
+BOOLEAN\r
+IsValidWorkSpace (\r
+  IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader\r
+  )\r
+/*++\r
+\r
+Routine Description:\r
+    Check to see if it is a valid work space.\r
+\r
+Arguments:\r
+    WorkingHeader - Pointer of working block header \r
+\r
+Returns:\r
+    EFI_SUCCESS   - The function completed successfully\r
+    EFI_ABORTED   - The function could not complete successfully.\r
+\r
+--*/\r
+{\r
+  EFI_STATUS                              Status;\r
+  EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER WorkingBlockHeader;\r
+\r
+  ASSERT (WorkingHeader != NULL);\r
+  if (WorkingHeader->WorkingBlockValid != FTW_VALID_STATE) {\r
+    return FALSE;\r
+  }\r
+  //\r
+  // Check signature with gEfiSystemNvDataFvGuid\r
+  //\r
+  if (!CompareGuid (&gEfiSystemNvDataFvGuid, &WorkingHeader->Signature)) {\r
+    return FALSE;\r
+  }\r
+  //\r
+  // Check the CRC of header\r
+  //\r
+  CopyMem (\r
+    &WorkingBlockHeader,\r
+    WorkingHeader,\r
+    sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)\r
+    );\r
+\r
+  //\r
+  // Filter out the Crc and State fields\r
+  //\r
+  SetMem (\r
+    &WorkingBlockHeader.Crc,\r
+    sizeof (UINT32),\r
+    FTW_ERASED_BYTE\r
+    );\r
+  WorkingBlockHeader.WorkingBlockValid    = FTW_ERASE_POLARITY;\r
+  WorkingBlockHeader.WorkingBlockInvalid  = FTW_ERASE_POLARITY;\r
+\r
+  //\r
+  // Calculate the Crc of woking block header\r
+  //\r
+  Status = gBS->CalculateCrc32 (\r
+                  (UINT8 *) &WorkingBlockHeader,\r
+                  sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),\r
+                  &WorkingBlockHeader.Crc\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  if (WorkingBlockHeader.Crc != WorkingHeader->Crc) {\r
+    DEBUG ((EFI_D_FTW_LITE, "FtwLite: Work block header CRC check error\n"));\r
+    return FALSE;\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+EFI_STATUS\r
+InitWorkSpaceHeader (\r
+  IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader\r
+  )\r
+/*++\r
+\r
+Routine Description:\r
+    Initialize a work space when there is no work space.\r
+\r
+Arguments:\r
+    WorkingHeader - Pointer of working block header \r
+\r
+Returns:\r
+    EFI_SUCCESS   - The function completed successfully\r
+    EFI_ABORTED   - The function could not complete successfully.\r
+\r
+--*/\r
+{\r
+  EFI_STATUS  Status;\r
+\r
+  ASSERT (WorkingHeader != NULL);\r
+\r
+  //\r
+  // Here using gEfiSystemNvDataFvGuid as the signature.\r
+  //\r
+  CopyMem (\r
+    &WorkingHeader->Signature,\r
+    &gEfiSystemNvDataFvGuid,\r
+    sizeof (EFI_GUID)\r
+    );\r
+  WorkingHeader->WriteQueueSize = FTW_WORKING_QUEUE_SIZE;\r
+\r
+  //\r
+  // Crc is calculated with all the fields except Crc and STATE\r
+  //\r
+  WorkingHeader->WorkingBlockValid    = FTW_ERASE_POLARITY;\r
+  WorkingHeader->WorkingBlockInvalid  = FTW_ERASE_POLARITY;\r
+  SetMem (&WorkingHeader->Crc, sizeof (UINT32), FTW_ERASED_BYTE);\r
+\r
+  //\r
+  // Calculate the CRC value\r
+  //\r
+  Status = gBS->CalculateCrc32 (\r
+                  (UINT8 *) WorkingHeader,\r
+                  sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),\r
+                  &WorkingHeader->Crc\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  //\r
+  // Restore the WorkingBlockValid flag to VALID state\r
+  //\r
+  WorkingHeader->WorkingBlockValid    = FTW_VALID_STATE;\r
+  WorkingHeader->WorkingBlockInvalid  = FTW_INVALID_STATE;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+EFI_STATUS\r
+FtwUpdateFvState (\r
+  IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *FvBlock,\r
+  IN EFI_LBA                             Lba,\r
+  IN UINTN                               Offset,\r
+  IN UINT8                               NewBit\r
+  )\r
+/*++\r
+\r
+Routine Description:\r
+    Update a bit of state on a block device. The location of the bit is \r
+    calculated by the (Lba, Offset, bit). Here bit is determined by the \r
+    the name of a certain bit.\r
+\r
+Arguments:\r
+    FvBlock    - FVB Protocol interface to access SrcBlock and DestBlock\r
+    Lba        - Lba of a block\r
+    Offset     - Offset on the Lba\r
+    NewBit     - New value that will override the old value if it can be change\r
+\r
+Returns:\r
+    EFI_SUCCESS   - A state bit has been updated successfully\r
+    Others        - Access block device error.\r
+\r
+Notes:\r
+    Assume all bits of State are inside the same BYTE. \r
+\r
+    EFI_ABORTED   - Read block fail\r
+--*/\r
+{\r
+  EFI_STATUS  Status;\r
+  UINT8       State;\r
+  UINTN       Length;\r
+\r
+  //\r
+  // Read state from device, assume State is only one byte.\r
+  //\r
+  Length  = sizeof (UINT8);\r
+  Status  = FvBlock->Read (FvBlock, Lba, Offset, &Length, &State);\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_ABORTED;\r
+  }\r
+\r
+  State ^= FTW_POLARITY_REVERT;\r
+  State = (UINT8) (State | NewBit);\r
+  State ^= FTW_POLARITY_REVERT;\r
+\r
+  //\r
+  // Write state back to device\r
+  //\r
+  Length  = sizeof (UINT8);\r
+  Status  = FvBlock->Write (FvBlock, Lba, Offset, &Length, &State);\r
+\r
+  return Status;\r
+}\r
+\r
+EFI_STATUS\r
+FtwGetLastRecord (\r
+  IN  EFI_FTW_LITE_DEVICE  *FtwLiteDevice,\r
+  OUT EFI_FTW_LITE_RECORD  **FtwLastRecord\r
+  )\r
+/*++\r
+\r
+Routine Description:\r
+    Get the last Write record pointer. \r
+    The last record is the record whose 'complete' state hasn't been set.\r
+    After all, this header may be a EMPTY header entry for next Allocate. \r
+\r
+Arguments:\r
+    FtwLiteDevice   - Private data of this driver\r
+    FtwLastRecord   - Pointer to retrieve the last write record\r
+\r
+Returns:\r
+    EFI_SUCCESS     - Get the last write record successfully\r
+    EFI_ABORTED     - The FTW work space is damaged\r
+\r
+--*/\r
+{\r
+  EFI_FTW_LITE_RECORD *Record;\r
+\r
+  Record = (EFI_FTW_LITE_RECORD *) (FtwLiteDevice->FtwWorkSpaceHeader + 1);\r
+  while (Record->WriteCompleted == FTW_VALID_STATE) {\r
+    //\r
+    // If Offset exceed the FTW work space boudary, return error.\r
+    //\r
+    if ((UINTN) ((UINT8 *) Record - FtwLiteDevice->FtwWorkSpace) > FtwLiteDevice->FtwWorkSpaceSize) {\r
+      return EFI_ABORTED;\r
+    }\r
+\r
+    Record++;\r
+  }\r
+  //\r
+  // Last write record is found\r
+  //\r
+  *FtwLastRecord = Record;\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+EFI_STATUS\r
+WorkSpaceRefresh (\r
+  IN EFI_FTW_LITE_DEVICE  *FtwLiteDevice\r
+  )\r
+/*++\r
+\r
+Routine Description:\r
+    Read from working block to refresh the work space in memory.\r
+\r
+Arguments:\r
+    FtwLiteDevice     - Point to private data of FTW driver\r
+\r
+Returns:\r
+    EFI_SUCCESS   - The function completed successfully\r
+    EFI_ABORTED   - The function could not complete successfully.\r
+\r
+--*/\r
+{\r
+  EFI_STATUS          Status;\r
+  UINTN               Length;\r
+  UINTN               Offset;\r
+  EFI_FTW_LITE_RECORD *Record;\r
+\r
+  //\r
+  // Initialize WorkSpace as FTW_ERASED_BYTE\r
+  //\r
+  SetMem (\r
+    FtwLiteDevice->FtwWorkSpace,\r
+    FtwLiteDevice->FtwWorkSpaceSize,\r
+    FTW_ERASED_BYTE\r
+    );\r
+\r
+  //\r
+  // Read from working block\r
+  //\r
+  Length = FtwLiteDevice->FtwWorkSpaceSize;\r
+  Status = FtwLiteDevice->FtwFvBlock->Read (\r
+                                        FtwLiteDevice->FtwFvBlock,\r
+                                        FtwLiteDevice->FtwWorkSpaceLba,\r
+                                        FtwLiteDevice->FtwWorkSpaceBase,\r
+                                        &Length,\r
+                                        FtwLiteDevice->FtwWorkSpace\r
+                                        );\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_ABORTED;\r
+  }\r
+  //\r
+  // Refresh the FtwLastRecord\r
+  //\r
+  Status  = FtwGetLastRecord (FtwLiteDevice, &FtwLiteDevice->FtwLastRecord);\r
+\r
+  Record  = FtwLiteDevice->FtwLastRecord;\r
+  Offset  = (UINTN) (UINT8 *) Record - (UINTN) FtwLiteDevice->FtwWorkSpace;\r
+\r
+  //\r
+  // IF work space has error or Record is out of the workspace limit, THEN\r
+  //   call reclaim.\r
+  //\r
+  if (EFI_ERROR (Status) || (Offset + WRITE_TOTAL_SIZE >= FtwLiteDevice->FtwWorkSpaceSize)) {\r
+    //\r
+    // reclaim work space in working block.\r
+    //\r
+    Status = FtwReclaimWorkSpace (FtwLiteDevice);\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((EFI_D_FTW_LITE, "FtwLite: Reclaim workspace - %r\n", Status));\r
+      return EFI_ABORTED;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+EFI_STATUS\r
+CleanupWorkSpace (\r
+  IN EFI_FTW_LITE_DEVICE  *FtwLiteDevice,\r
+  IN OUT UINT8            *FtwSpaceBuffer,\r
+  IN UINTN                BufferSize\r
+  )\r
+/*++\r
+\r
+Routine Description:\r
+    Reclaim the work space. Get rid of all the completed write records\r
+    and write records in the Fault Tolerant work space.\r
+\r
+Arguments:\r
+    FtwLiteDevice   - Point to private data of FTW driver\r
+    FtwSpaceBuffer  - Buffer to contain the reclaimed clean data\r
+    BufferSize      - Size of the FtwSpaceBuffer\r
+\r
+Returns:\r
+    EFI_SUCCESS           - The function completed successfully\r
+    EFI_BUFFER_TOO_SMALL  - The FtwSpaceBuffer is too small\r
+    EFI_ABORTED           - The function could not complete successfully.\r
+\r
+--*/\r
+{\r
+  UINTN               Length;\r
+  EFI_FTW_LITE_RECORD *Record;\r
+\r
+  //\r
+  // To check if the buffer is large enough\r
+  //\r
+  Length = FtwLiteDevice->FtwWorkSpaceSize;\r
+  if (BufferSize < Length) {\r
+    return EFI_BUFFER_TOO_SMALL;\r
+  }\r
+  //\r
+  // Clear the content of buffer that will save the new work space data\r
+  //\r
+  SetMem (FtwSpaceBuffer, Length, FTW_ERASED_BYTE);\r
+\r
+  //\r
+  // Copy EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER to buffer\r
+  //\r
+  CopyMem (\r
+    FtwSpaceBuffer,\r
+    FtwLiteDevice->FtwWorkSpaceHeader,\r
+    sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)\r
+    );\r
+\r
+  //\r
+  // Get the last record\r
+  //\r
+  Record = FtwLiteDevice->FtwLastRecord;\r
+  if ((Record != NULL) && (Record->WriteAllocated == FTW_VALID_STATE) && (Record->WriteCompleted != FTW_VALID_STATE)) {\r
+    CopyMem (\r
+      (UINT8 *) FtwSpaceBuffer + sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER),\r
+      Record,\r
+      WRITE_TOTAL_SIZE\r
+      );\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+EFI_STATUS\r
+FtwReclaimWorkSpace (\r
+  IN EFI_FTW_LITE_DEVICE  *FtwLiteDevice\r
+  )\r
+/*++\r
+\r
+Routine Description:\r
+    Reclaim the work space on the working block.\r
+\r
+Arguments:\r
+    FtwLiteDevice     - Point to private data of FTW driver\r
+\r
+Returns:\r
+    EFI_SUCCESS           - The function completed successfully\r
+    EFI_OUT_OF_RESOURCES  - Allocate memory error\r
+    EFI_ABORTED           - The function could not complete successfully\r
+\r
+--*/\r
+{\r
+  EFI_STATUS                              Status;\r
+  UINT8                                   *TempBuffer;\r
+  UINTN                                   TempBufferSize;\r
+  UINT8                                   *Ptr;\r
+  UINTN                                   Length;\r
+  UINTN                                   Index;\r
+  UINTN                                   SpareBufferSize;\r
+  UINT8                                   *SpareBuffer;\r
+  EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingBlockHeader;\r
+\r
+  DEBUG ((EFI_D_FTW_LITE, "FtwLite: start to reclaim work space\n"));\r
+\r
+  //\r
+  // Read all original data from working block to a memory buffer\r
+  //\r
+  TempBufferSize = FtwLiteDevice->SpareAreaLength;\r
+  TempBuffer     = AllocateZeroPool (TempBufferSize);\r
+  if (TempBuffer != NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Ptr = TempBuffer;\r
+  for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {\r
+    Length = FtwLiteDevice->SizeOfSpareBlock;\r
+    Status = FtwLiteDevice->FtwFvBlock->Read (\r
+                                          FtwLiteDevice->FtwFvBlock,\r
+                                          FtwLiteDevice->FtwWorkBlockLba + Index,\r
+                                          0,\r
+                                          &Length,\r
+                                          Ptr\r
+                                          );\r
+    if (EFI_ERROR (Status)) {\r
+      FreePool (TempBuffer);\r
+      return EFI_ABORTED;\r
+    }\r
+\r
+    Ptr += Length;\r
+  }\r
+  //\r
+  // Clean up the workspace, remove all the completed records.\r
+  //\r
+  Ptr = TempBuffer +\r
+    ((UINTN) (FtwLiteDevice->FtwWorkSpaceLba - FtwLiteDevice->FtwWorkBlockLba)) *\r
+    FtwLiteDevice->SizeOfSpareBlock + FtwLiteDevice->FtwWorkSpaceBase;\r
+\r
+  Status = CleanupWorkSpace (\r
+            FtwLiteDevice,\r
+            Ptr,\r
+            FtwLiteDevice->FtwWorkSpaceSize\r
+            );\r
+\r
+  CopyMem (\r
+    FtwLiteDevice->FtwWorkSpace,\r
+    Ptr,\r
+    FtwLiteDevice->FtwWorkSpaceSize\r
+    );\r
+\r
+  Status = FtwGetLastRecord (FtwLiteDevice, &FtwLiteDevice->FtwLastRecord);\r
+\r
+  //\r
+  // Set the WorkingBlockValid and WorkingBlockInvalid as INVALID\r
+  //\r
+  WorkingBlockHeader                      = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) Ptr;\r
+  WorkingBlockHeader->WorkingBlockValid   = FTW_INVALID_STATE;\r
+  WorkingBlockHeader->WorkingBlockInvalid = FTW_INVALID_STATE;\r
+\r
+  //\r
+  // Try to keep the content of spare block\r
+  // Save spare block into a spare backup memory buffer (Sparebuffer)\r
+  //\r
+  SpareBufferSize = FtwLiteDevice->SpareAreaLength;\r
+  SpareBuffer     = AllocatePool (SpareBufferSize);\r
+  if (SpareBuffer == NULL) {\r
+    FreePool (TempBuffer);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Ptr = SpareBuffer;\r
+  for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {\r
+    Length = FtwLiteDevice->SizeOfSpareBlock;\r
+    Status = FtwLiteDevice->FtwBackupFvb->Read (\r
+                                            FtwLiteDevice->FtwBackupFvb,\r
+                                            FtwLiteDevice->FtwSpareLba + Index,\r
+                                            0,\r
+                                            &Length,\r
+                                            Ptr\r
+                                            );\r
+    if (EFI_ERROR (Status)) {\r
+      FreePool (TempBuffer);\r
+      FreePool (SpareBuffer);\r
+      return EFI_ABORTED;\r
+    }\r
+\r
+    Ptr += Length;\r
+  }\r
+  //\r
+  // Write the memory buffer to spare block\r
+  //\r
+  Status  = FtwEraseSpareBlock (FtwLiteDevice);\r
+  Ptr     = TempBuffer;\r
+  for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {\r
+    Length = FtwLiteDevice->SizeOfSpareBlock;\r
+    Status = FtwLiteDevice->FtwBackupFvb->Write (\r
+                                            FtwLiteDevice->FtwBackupFvb,\r
+                                            FtwLiteDevice->FtwSpareLba + Index,\r
+                                            0,\r
+                                            &Length,\r
+                                            Ptr\r
+                                            );\r
+    if (EFI_ERROR (Status)) {\r
+      FreePool (TempBuffer);\r
+      FreePool (SpareBuffer);\r
+      return EFI_ABORTED;\r
+    }\r
+\r
+    Ptr += Length;\r
+  }\r
+  //\r
+  // Free TempBuffer\r
+  //\r
+  FreePool (TempBuffer);\r
+\r
+  //\r
+  // Write the spare block to working block\r
+  //\r
+  Status = FlushSpareBlockToWorkingBlock (FtwLiteDevice);\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (SpareBuffer);\r
+    return Status;\r
+  }\r
+  //\r
+  // Restore spare backup buffer into spare block , if no failure happened during FtwWrite.\r
+  //\r
+  Status  = FtwEraseSpareBlock (FtwLiteDevice);\r
+  Ptr     = SpareBuffer;\r
+  for (Index = 0; Index < FtwLiteDevice->NumberOfSpareBlock; Index += 1) {\r
+    Length = FtwLiteDevice->SizeOfSpareBlock;\r
+    Status = FtwLiteDevice->FtwBackupFvb->Write (\r
+                                            FtwLiteDevice->FtwBackupFvb,\r
+                                            FtwLiteDevice->FtwSpareLba + Index,\r
+                                            0,\r
+                                            &Length,\r
+                                            Ptr\r
+                                            );\r
+    if (EFI_ERROR (Status)) {\r
+      FreePool (SpareBuffer);\r
+      return EFI_ABORTED;\r
+    }\r
+\r
+    Ptr += Length;\r
+  }\r
+\r
+  FreePool (SpareBuffer);\r
+\r
+  DEBUG ((EFI_D_FTW_LITE, "FtwLite: reclaim work space success\n"));\r
+\r
+  return EFI_SUCCESS;\r
+}\r