From 8a2d49964e371b1715beb3225fde47edfcaa51ca Mon Sep 17 00:00:00 2001 From: gdong1 Date: Fri, 10 Dec 2010 09:27:54 +0000 Subject: [PATCH] Add SMM Variable implementation. git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@11151 6f19259b-4bc3-4df7-8a09-765794883524 --- MdeModulePkg/Include/Guid/VariableFormat.h | 86 ++ .../Include/Protocol/SmmFaultTolerantWrite.h | 38 + .../Include/Protocol/SmmFirmwareVolumeBlock.h | 36 + .../Include/Protocol/SmmSwapAddressRange.h | 40 + MdeModulePkg/Include/Protocol/SmmVariable.h | 39 + MdeModulePkg/MdeModulePkg.dec | 20 + MdeModulePkg/MdeModulePkg.dsc | 3 + .../FaultTolerantWrite.c | 432 +------ .../FaultTolerantWrite.h | 69 +- .../FaultTolerantWriteDxe.c | 250 ++++ .../FaultTolerantWriteDxe.inf | 3 +- .../FaultTolerantWriteSmm.c | 281 +++++ .../FaultTolerantWriteSmm.inf | 73 ++ .../Universal/FaultTolerantWriteDxe/FtwMisc.c | 416 ++++++- .../Universal/Variable/RuntimeDxe/Reclaim.c | 197 +-- .../Universal/Variable/RuntimeDxe/Variable.c | 1080 ++++++----------- .../Universal/Variable/RuntimeDxe/Variable.h | 343 +++++- .../Variable/RuntimeDxe/VariableDxe.c | 402 ++++++ .../RuntimeDxe/VariableRuntimeDxe.inf | 6 +- .../Variable/RuntimeDxe/VariableSmm.c | 585 +++++++++ .../Variable/RuntimeDxe/VariableSmm.inf | 86 ++ .../Variable/RuntimeDxe/VariableSmmCommon.h | 40 + .../RuntimeDxe/VariableSmmRuntimeDxe.c | 650 ++++++++++ .../RuntimeDxe/VariableSmmRuntimeDxe.inf | 69 ++ 24 files changed, 3912 insertions(+), 1332 deletions(-) create mode 100644 MdeModulePkg/Include/Protocol/SmmFaultTolerantWrite.h create mode 100644 MdeModulePkg/Include/Protocol/SmmFirmwareVolumeBlock.h create mode 100644 MdeModulePkg/Include/Protocol/SmmSwapAddressRange.h create mode 100644 MdeModulePkg/Include/Protocol/SmmVariable.h create mode 100644 MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.c create mode 100644 MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmm.c create mode 100644 MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmm.inf create mode 100644 MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c create mode 100644 MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c create mode 100644 MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf create mode 100644 MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmCommon.h create mode 100644 MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c create mode 100644 MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf diff --git a/MdeModulePkg/Include/Guid/VariableFormat.h b/MdeModulePkg/Include/Guid/VariableFormat.h index 2276262a3b..0db3a22f34 100644 --- a/MdeModulePkg/Include/Guid/VariableFormat.h +++ b/MdeModulePkg/Include/Guid/VariableFormat.h @@ -157,4 +157,90 @@ struct _VARIABLE_INFO_ENTRY { BOOLEAN Volatile; ///< TRUE if volatile, FALSE if non-volatile. }; +// +// This structure is used for SMM variable. the collected statistics data is saved in SMRAM. It can be got from +// SMI handler. The communication buffer should be: +// EFI_SMM_COMMUNICATE_HEADER + SMM_VARIABLE_COMMUNICATE_HEADER + payload. +// +typedef struct { + UINTN Function; + EFI_STATUS ReturnStatus; + UINT8 Data[1]; +} SMM_VARIABLE_COMMUNICATE_HEADER; + +// +// The payload for this function is SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE. +// +#define SMM_VARIABLE_FUNCTION_GET_VARIABLE 1 +// +// The payload for this function is SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME. +// +#define SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME 2 +// +// The payload for this function is SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE. +// +#define SMM_VARIABLE_FUNCTION_SET_VARIABLE 3 +// +// The payload for this function is SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO. +// +#define SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO 4 +// +// It is a notify event, no extra payload for this function. +// +#define SMM_VARIABLE_FUNCTION_READY_TO_BOOT 5 +// +// It is a notify event, no extra payload for this function. +// +#define SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE 6 +// +// The payload for this function is VARIABLE_INFO_ENTRY. The GUID in EFI_SMM_COMMUNICATE_HEADER +// is gEfiSmmVariableProtocolGuid. +// +#define SMM_VARIABLE_FUNCTION_GET_STATISTICS 7 + +/// +/// Size of SMM communicate header, without including the payload. +/// +#define SMM_COMMUNICATE_HEADER_SIZE (OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data)) + +/// +/// Size of SMM variable communicate header, without including the payload. +/// +#define SMM_VARIABLE_COMMUNICATE_HEADER_SIZE (OFFSET_OF (SMM_VARIABLE_COMMUNICATE_HEADER, Data)) + +/// +/// This structure is used to communicate with SMI handler by SetVariable and GetVariable. +/// +typedef struct { + EFI_GUID Guid; + UINTN DataSize; + UINTN NameSize; + UINT32 Attributes; + CHAR16 Name[1]; +} SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE; + +/// +/// This structure is used to communicate with SMI handler by GetNextVariableName. +/// +typedef struct { + EFI_GUID Guid; + UINTN NameSize; + CHAR16 Name[1]; +} SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME; + +/// +/// This structure is used to communicate with SMI handler by QueryVariableInfo. +/// +typedef struct { + UINT64 MaximumVariableStorageSize; + UINT64 RemainingVariableStorageSize; + UINT64 MaximumVariableSize; + UINT32 Attributes; +} SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO; + +/// +/// This structure is used to communicate with SMI handler to get variable statistics information. +/// +typedef VARIABLE_INFO_ENTRY SMM_VARIABLE_COMMUNICATE_VARIABLE_INFO_ENTRY; + #endif // _EFI_VARIABLE_H_ diff --git a/MdeModulePkg/Include/Protocol/SmmFaultTolerantWrite.h b/MdeModulePkg/Include/Protocol/SmmFaultTolerantWrite.h new file mode 100644 index 0000000000..a12e53b740 --- /dev/null +++ b/MdeModulePkg/Include/Protocol/SmmFaultTolerantWrite.h @@ -0,0 +1,38 @@ +/** @file + SMM Fault Tolerant Write protocol is related to EDK II-specific implementation of FTW, + provides boot-time service for fault tolerant write capability for block devices in + EFI SMM environment. The protocol provides for non-volatile storage of the intermediate + data and private information a caller would need to recover from a critical fault, + such as a power failure. + +Copyright (c) 2010, 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 that 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. + +**/ + +#ifndef __SMM_FAULT_TOLERANT_WRITE_H__ +#define __SMM_FAULT_TOLERANT_WRITE_H__ + +#include + +#define EFI_SMM_FAULT_TOLERANT_WRITE_PROTOCOL_GUID \ + { \ + 0x3868fc3b, 0x7e45, 0x43a7, { 0x90, 0x6c, 0x4b, 0xa4, 0x7d, 0xe1, 0x75, 0x4d } \ + } + +// +// SMM Fault Tolerant Write protocol structure is the same as Fault Tolerant Write protocol. +// The SMM one is intend to run in SMM environment, which means it can be used by +// SMM drivers after ExitPmAuth. +// +typedef EFI_FAULT_TOLERANT_WRITE_PROTOCOL EFI_SMM_FAULT_TOLERANT_WRITE_PROTOCOL; + +extern EFI_GUID gEfiSmmFaultTolerantWriteProtocolGuid; + +#endif diff --git a/MdeModulePkg/Include/Protocol/SmmFirmwareVolumeBlock.h b/MdeModulePkg/Include/Protocol/SmmFirmwareVolumeBlock.h new file mode 100644 index 0000000000..b04762b955 --- /dev/null +++ b/MdeModulePkg/Include/Protocol/SmmFirmwareVolumeBlock.h @@ -0,0 +1,36 @@ +/** @file + SMM Firmware Volume Block protocol is related to EDK II-specific implementation of + FVB driver, provides control over block-oriented firmware devices and is intended + to use in the EFI SMM environment. + +Copyright (c) 2010, 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 that 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. + +**/ + +#ifndef __SMM_FIRMWARE_VOLUME_BLOCK_H__ +#define __SMM_FIRMWARE_VOLUME_BLOCK_H__ + +#include + +#define EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL_GUID \ + { \ + 0xf52fc9ff, 0x8025, 0x4432, { 0xa5, 0x3b, 0xb4, 0x7b, 0x5e, 0x9, 0xdb, 0xf9 } \ + } + +// +// SMM Firmware Volume Block protocol structure is the same as Firmware Volume Block +// protocol. The SMM one is intend to run in SMM environment, which means it can be +// used by SMM drivers after ExitPmAuth. +// +typedef EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL; + +extern EFI_GUID gEfiSmmFirmwareVolumeBlockProtocolGuid; + +#endif diff --git a/MdeModulePkg/Include/Protocol/SmmSwapAddressRange.h b/MdeModulePkg/Include/Protocol/SmmSwapAddressRange.h new file mode 100644 index 0000000000..60a0a2c4a2 --- /dev/null +++ b/MdeModulePkg/Include/Protocol/SmmSwapAddressRange.h @@ -0,0 +1,40 @@ +/** @file + The EFI_SMM_SWAP_ADDRESS_RANGE_PROTOCOL is related to EDK II-specific implementation + and used to abstract the swap operation of boot block and backup block of FV in EFI + SMM environment. This swap is especially needed when updating the boot block of FV. + If a power failure happens during the boot block update, the swapped backup block + (now the boot block) can boot the machine with the old boot block backed up in it. + The swap operation is platform dependent, so other protocols such as SMM FTW (Fault + Tolerant Write) should use this protocol instead of handling hardware directly. + +Copyright (c) 2010, 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 that 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. + +**/ + +#ifndef __SMM_SWAP_ADDRESS_RANGE_H__ +#define __SMM_SWAP_ADDRESS_RANGE_H__ + +#include + +#define EFI_SMM_SWAP_ADDRESS_RANGE_PROTOCOL_GUID \ + { \ + 0x67c4f112, 0x3385, 0x4e55, { 0x9c, 0x5b, 0xc0, 0x5b, 0x71, 0x7c, 0x42, 0x28 } \ + } + +// +// SMM Swap Address Range protocol structure is the same as Swap Address Range protocol. +// The SMM one is intend to run in SMM environment, which means it can be used by +// SMM drivers after ExitPmAuth. +// +typedef EFI_SWAP_ADDRESS_RANGE_PROTOCOL EFI_SMM_SWAP_ADDRESS_RANGE_PROTOCOL; + +extern EFI_GUID gEfiSmmSwapAddressRangeProtocolGuid; + +#endif diff --git a/MdeModulePkg/Include/Protocol/SmmVariable.h b/MdeModulePkg/Include/Protocol/SmmVariable.h new file mode 100644 index 0000000000..75ab6c3057 --- /dev/null +++ b/MdeModulePkg/Include/Protocol/SmmVariable.h @@ -0,0 +1,39 @@ +/** @file + EFI SMM Variable Protocol is related to EDK II-specific implementation of variables + and intended for use as a means to store data in the EFI SMM environment. + + Copyright (c) 2010, 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. + +**/ + +#ifndef __SMM_VARIABLE_H__ +#define __SMM_VARIABLE_H__ + +#define EFI_SMM_VARIABLE_PROTOCOL_GUID \ + { \ + 0xed32d533, 0x99e6, 0x4209, { 0x9c, 0xc0, 0x2d, 0x72, 0xcd, 0xd9, 0x98, 0xa7 } \ + } + +typedef struct _EFI_SMM_VARIABLE_PROTOCOL EFI_SMM_VARIABLE_PROTOCOL; + +/// +/// EFI SMM Variable Protocol is intended for use as a means +/// to store data in the EFI SMM environment. +/// +struct _EFI_SMM_VARIABLE_PROTOCOL { + EFI_GET_VARIABLE SmmGetVariable; + EFI_GET_NEXT_VARIABLE_NAME SmmGetNextVariableName; + EFI_SET_VARIABLE SmmSetVariable; + EFI_QUERY_VARIABLE_INFO SmmQueryVariableInfo; +}; + +extern EFI_GUID gEfiSmmVariableProtocolGuid; + +#endif diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec index 29aec9348b..0530f91180 100644 --- a/MdeModulePkg/MdeModulePkg.dec +++ b/MdeModulePkg/MdeModulePkg.dec @@ -171,9 +171,25 @@ # Include/Protocol/FaultTolerantWrite.h gEfiFaultTolerantWriteProtocolGuid = { 0x3EBD9E82, 0x2C78, 0x4DE6, { 0x97, 0x86, 0x8D, 0x4B, 0xFC, 0xB7, 0xC8, 0x81 }} + ## This protocol provides boot-time service to do fault tolerant write capability for block devices in SMM environment. + # Include/Protocol/SmmFaultTolerantWrite.h + gEfiSmmFaultTolerantWriteProtocolGuid = { 0x3868fc3b, 0x7e45, 0x43a7, { 0x90, 0x6c, 0x4b, 0xa4, 0x7d, 0xe1, 0x75, 0x4d }} + ## This protocol is used to abstract the swap operation of boot block and backup block of boot FV. # Include/Protocol/SwapAddressRange.h gEfiSwapAddressRangeProtocolGuid = { 0x1259F60D, 0xB754, 0x468E, { 0xA7, 0x89, 0x4D, 0xB8, 0x5D, 0x55, 0xE8, 0x7E }} + + ## This protocol is used to abstract the swap operation of boot block and backup block of boot FV in SMM environment. + # Include/Protocol/SmmSwapAddressRange.h + gEfiSmmSwapAddressRangeProtocolGuid = { 0x67c4f112, 0x3385, 0x4e55, { 0x9c, 0x5b, 0xc0, 0x5b, 0x71, 0x7c, 0x42, 0x28 }} + + ## This protocol is intended for use as a means to store data in the EFI SMM environment. + # Include/Protocol/SmmVariableProtocol.h + gEfiSmmVariableProtocolGuid = { 0xed32d533, 0x99e6, 0x4209, { 0x9c, 0xc0, 0x2d, 0x72, 0xcd, 0xd9, 0x98, 0xa7 }} + + ## This protocol is similar with DXE FVB protocol and used in the UEFI SMM evvironment. + # Include/Protocol/SmmFirmwareVolumeBlock.h + gEfiSmmFirmwareVolumeBlockProtocolGuid = { 0xd326d041, 0xbd31, 0x4c01, { 0xb5, 0xa8, 0x62, 0x8b, 0xe8, 0x7f, 0x6, 0x53 }} [PcdsFeatureFlag] ## Indicate whether platform can support update capsule across a system reset @@ -224,6 +240,10 @@ # interrupt to access usb device in the case of absence of usb stack. # DUET platform requires the token to be TRUE. gEfiMdeModulePkgTokenSpaceGuid.PcdTurnOffUsbLegacySupport|FALSE|BOOLEAN|0x00010047 + + ## If TRUE, the variable services are provided in DXE_SMM. The SMM driver can use SMM variable protocol + # to access variable. Otherwise the variable services are provided in DXE_RUNTIME. + gEfiMdeModulePkgTokenSpaceGuid.PcdSmmVariableEnable|TRUE|BOOLEAN|0x00010048 ## If TRUE, HiiImageProtocol will be installed. # FALSE is for size reduction. diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc index 0f2fbd7975..d8a3826ef7 100644 --- a/MdeModulePkg/MdeModulePkg.dsc +++ b/MdeModulePkg/MdeModulePkg.dsc @@ -257,6 +257,7 @@ MdeModulePkg/Universal/Variable/Pei/VariablePei.inf MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf + MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmm.inf MdeModulePkg/Universal/Acpi/AcpiPlatformDxe/AcpiPlatformDxe.inf MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableDxe.inf @@ -278,6 +279,8 @@ [Components.IA32, Components.X64] MdeModulePkg/Core/PiSmmCore/PiSmmIpl.inf MdeModulePkg/Core/PiSmmCore/PiSmmCore.inf + MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf + MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf MdeModulePkg/Library/SmmReportStatusCodeLib/SmmReportStatusCodeLib.inf MdeModulePkg/Universal/StatusCodeHandler/Smm/StatusCodeHandlerSmm.inf MdeModulePkg/Universal/ReportStatusCodeRouter/Smm/ReportStatusCodeRouterSmm.inf diff --git a/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.c b/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.c index cf72609777..625737eac0 100644 --- a/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.c +++ b/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.c @@ -1,44 +1,7 @@ /** @file - This is a simple fault tolerant write driver. - - This boot service protocol only 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. - - The implementation uses an FTW (Fault Tolerant Write) Work Space. - This work space is a memory copy of the work space on the Working Block, - the size of the work space is the FTW_WORK_SPACE_SIZE bytes. - - The work space stores each write record as EFI_FTW_RECORD structure. - The spare block stores the write buffer before write to the target block. - - The write record has three states to specify the different phase of write operation. - 1) WRITE_ALLOCATED is that the record is allocated in write space. - The information of write operation is stored in write record structure. - 2) SPARE_COMPLETED is that the data from write buffer is writed into the spare block as the backup. - 3) WRITE_COMPLETED is that the data is copied from the spare block to the target block. - - This driver operates the data as the whole size of spare block. - It first read the SpareAreaLength data from the target block into the spare memory buffer. - Then copy the write buffer data into the spare memory buffer. - Then write the spare memory buffer into the spare block. - Final copy the data from the spare block to the target block. - - To make this drive work well, the following conditions must be satisfied: - 1. The write NumBytes data must be fit within Spare area. - Offset + NumBytes <= SpareAreaLength - 2. The whole flash range has the same block size. - 3. Working block is an area which contains working space in its last block and has the same size as spare block. - 4. Working Block area must be in the single one Firmware Volume Block range which FVB protocol is produced on. - 5. Spare area must be in the single one Firmware Volume Block range which FVB protocol is produced on. - 6. Any write data area (SpareAreaLength Area) which the data will be written into must be - in the single one Firmware Volume Block range which FVB protocol is produced on. - 7. If write data area (such as Variable range) is enlarged, the spare area range must be enlarged. - The spare area must be enough large to store the write data before write them into the target range. - If one of them is not satisfied, FtwWrite may fail. - Usually, Spare area only takes one block. That's SpareAreaLength = BlockSize, NumberOfSpareBlock = 1. + These are the common Fault Tolerant Write (FTW) functions that are shared + by DXE FTW driver and SMM FTW driver. Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
This program and the accompanying materials @@ -53,8 +16,6 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "FaultTolerantWrite.h" -EFI_EVENT mFvbRegistration = NULL; - // // Fault Tolerant Write Protocol API // @@ -237,7 +198,7 @@ FtwWriteRecord ( // // Spare Complete but Destination not complete, - // Recover the targt block with the spare block. + // Recover the target block with the spare block. // Header = FtwDevice->FtwLastWriteHeader; Record = FtwDevice->FtwLastWriteRecord; @@ -864,390 +825,3 @@ FtwGetLastWrite ( return Status; } -/** - Firmware Volume Block Protocol notification event handler. - - Initialization for Fault Tolerant Write is done in this handler. - - @param[in] Event Event whose notification function is being invoked. - @param[in] Context Pointer to the notification function's context. -**/ -VOID -EFIAPI -FvbNotificationEvent ( - IN EFI_EVENT Event, - IN VOID *Context - ) -{ - EFI_STATUS Status; - EFI_HANDLE *HandleBuffer; - UINTN HandleCount; - UINTN Index; - EFI_PHYSICAL_ADDRESS FvbBaseAddress; - EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb; - EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader; - EFI_FVB_ATTRIBUTES_2 Attributes; - EFI_FTW_DEVICE *FtwDevice; - EFI_FV_BLOCK_MAP_ENTRY *FvbMapEntry; - UINT32 LbaIndex; - UINTN Length; - EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader; - UINTN Offset; - EFI_HANDLE FvbHandle; - - FtwDevice = (EFI_FTW_DEVICE *)Context; - FvbHandle = NULL; - Fvb = NULL; - - FtwDevice->WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageFtwWorkingBase64); - if (FtwDevice->WorkSpaceAddress == 0) { - FtwDevice->WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwWorkingBase); - } - - FtwDevice->SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageFtwSpareBase64); - if (FtwDevice->SpareAreaAddress == 0) { - FtwDevice->SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwSpareBase); - } - - - // - // Locate all handles of Fvb protocol - // - Status = gBS->LocateHandleBuffer ( - ByProtocol, - &gEfiFirmwareVolumeBlockProtocolGuid, - NULL, - &HandleCount, - &HandleBuffer - ); - if (EFI_ERROR (Status)) { - return; - } - - // - // Get the FVB to access variable store - // - for (Index = 0; Index < HandleCount; Index += 1) { - Status = gBS->HandleProtocol ( - HandleBuffer[Index], - &gEfiFirmwareVolumeBlockProtocolGuid, - (VOID **) &Fvb - ); - if (EFI_ERROR (Status)) { - Status = EFI_NOT_FOUND; - break; - } - - // - // Ensure this FVB protocol supported Write operation. - // - Status = Fvb->GetAttributes (Fvb, &Attributes); - if (EFI_ERROR (Status) || ((Attributes & EFI_FVB2_WRITE_STATUS) == 0)) { - continue; - } - // - // Compare the address and select the right one - // - Status = Fvb->GetPhysicalAddress (Fvb, &FvbBaseAddress); - if (EFI_ERROR (Status)) { - continue; - } - - FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) ((UINTN) FvbBaseAddress); - if ((FtwDevice->FtwFvBlock == NULL) && (FtwDevice->WorkSpaceAddress >= FvbBaseAddress) && - ((FtwDevice->WorkSpaceAddress + FtwDevice->WorkSpaceLength) <= (FvbBaseAddress + FwVolHeader->FvLength)) - ) { - FtwDevice->FtwFvBlock = Fvb; - // - // To get the LBA of work space - // - if ((FwVolHeader->FvLength) > (FwVolHeader->HeaderLength)) { - // - // Now, one FV has one type of BlockLength - // - FvbMapEntry = &FwVolHeader->BlockMap[0]; - for (LbaIndex = 1; LbaIndex <= FvbMapEntry->NumBlocks; LbaIndex += 1) { - if ((FtwDevice->WorkSpaceAddress >= (FvbBaseAddress + FvbMapEntry->Length * (LbaIndex - 1))) - && (FtwDevice->WorkSpaceAddress < (FvbBaseAddress + FvbMapEntry->Length * LbaIndex))) { - FtwDevice->FtwWorkSpaceLba = LbaIndex - 1; - // - // Get the Work space size and Base(Offset) - // - FtwDevice->FtwWorkSpaceSize = FtwDevice->WorkSpaceLength; - FtwDevice->FtwWorkSpaceBase = (UINTN) (FtwDevice->WorkSpaceAddress - (FvbBaseAddress + FvbMapEntry->Length * (LbaIndex - 1))); - break; - } - } - } - } - - if ((FtwDevice->FtwBackupFvb == NULL) && (FtwDevice->SpareAreaAddress >= FvbBaseAddress) && - ((FtwDevice->SpareAreaAddress + FtwDevice->SpareAreaLength) <= (FvbBaseAddress + FwVolHeader->FvLength)) - ) { - FtwDevice->FtwBackupFvb = Fvb; - // - // To get the LBA of spare - // - if ((FwVolHeader->FvLength) > (FwVolHeader->HeaderLength)) { - // - // Now, one FV has one type of BlockLength - // - FvbMapEntry = &FwVolHeader->BlockMap[0]; - for (LbaIndex = 1; LbaIndex <= FvbMapEntry->NumBlocks; LbaIndex += 1) { - if ((FtwDevice->SpareAreaAddress >= (FvbBaseAddress + FvbMapEntry->Length * (LbaIndex - 1))) - && (FtwDevice->SpareAreaAddress < (FvbBaseAddress + FvbMapEntry->Length * LbaIndex))) { - // - // Get the NumberOfSpareBlock and BlockSize - // - FtwDevice->FtwSpareLba = LbaIndex - 1; - FtwDevice->BlockSize = FvbMapEntry->Length; - FtwDevice->NumberOfSpareBlock = FtwDevice->SpareAreaLength / FtwDevice->BlockSize; - // - // Check the range of spare area to make sure that it's in FV range - // - if ((FtwDevice->FtwSpareLba + FtwDevice->NumberOfSpareBlock) > FvbMapEntry->NumBlocks) { - DEBUG ((EFI_D_ERROR, "Ftw: Spare area is out of FV range\n")); - ASSERT (FALSE); - return; - } - break; - } - } - } - } - } - - if ((FtwDevice->FtwBackupFvb == NULL) || (FtwDevice->FtwFvBlock == NULL) || - (FtwDevice->FtwWorkSpaceLba == (EFI_LBA) (-1)) || (FtwDevice->FtwSpareLba == (EFI_LBA) (-1))) { - return; - } - - DEBUG ((EFI_D_INFO, "Ftw: Working and spare FVB is ready\n")); - // - // Calculate the start LBA of working block. Working block is an area which - // contains working space in its last block and has the same size as spare - // block, unless there are not enough blocks before the block that contains - // working space. - // - FtwDevice->FtwWorkBlockLba = FtwDevice->FtwWorkSpaceLba - FtwDevice->NumberOfSpareBlock + 1; - ASSERT ((INT64) (FtwDevice->FtwWorkBlockLba) >= 0); - - // - // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE. - // - FtwDevice->FtwWorkSpace = (UINT8 *) (FtwDevice + 1); - FtwDevice->FtwWorkSpaceHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) FtwDevice->FtwWorkSpace; - - FtwDevice->FtwLastWriteHeader = NULL; - FtwDevice->FtwLastWriteRecord = NULL; - - // - // Refresh the working space data from working block - // - Status = WorkSpaceRefresh (FtwDevice); - ASSERT_EFI_ERROR (Status); - // - // If the working block workspace is not valid, try the spare block - // - if (!IsValidWorkSpace (FtwDevice->FtwWorkSpaceHeader)) { - // - // Read from spare block - // - Length = FtwDevice->FtwWorkSpaceSize; - Status = FtwDevice->FtwBackupFvb->Read ( - FtwDevice->FtwBackupFvb, - FtwDevice->FtwSpareLba, - FtwDevice->FtwWorkSpaceBase, - &Length, - FtwDevice->FtwWorkSpace - ); - ASSERT_EFI_ERROR (Status); - - // - // If spare block is valid, then replace working block content. - // - if (IsValidWorkSpace (FtwDevice->FtwWorkSpaceHeader)) { - Status = FlushSpareBlockToWorkingBlock (FtwDevice); - DEBUG ((EFI_D_ERROR, "Ftw: Restart working block update in Init() - %r\n", Status)); - FtwAbort (&FtwDevice->FtwInstance); - // - // Refresh work space. - // - Status = WorkSpaceRefresh (FtwDevice); - ASSERT_EFI_ERROR (Status); - } else { - DEBUG ((EFI_D_ERROR, "Ftw: Both are invalid, init workspace\n")); - // - // If both are invalid, then initialize work space. - // - SetMem ( - FtwDevice->FtwWorkSpace, - FtwDevice->FtwWorkSpaceSize, - FTW_ERASED_BYTE - ); - InitWorkSpaceHeader (FtwDevice->FtwWorkSpaceHeader); - // - // Initialize the work space - // - Status = FtwReclaimWorkSpace (FtwDevice, FALSE); - ASSERT_EFI_ERROR (Status); - } - } - // - // If the FtwDevice->FtwLastWriteRecord is 1st record of write header && - // (! SpareComplete) THEN call Abort(). - // - if ((FtwDevice->FtwLastWriteHeader->HeaderAllocated == FTW_VALID_STATE) && - (FtwDevice->FtwLastWriteRecord->SpareComplete != FTW_VALID_STATE) && - IsFirstRecordOfWrites (FtwDevice->FtwLastWriteHeader, FtwDevice->FtwLastWriteRecord) - ) { - DEBUG ((EFI_D_ERROR, "Ftw: Init.. find first record not SpareCompleted, abort()\n")); - FtwAbort (&FtwDevice->FtwInstance); - } - // - // If Header is incompleted and the last record has completed, then - // call Abort() to set the Header->Complete FLAG. - // - if ((FtwDevice->FtwLastWriteHeader->Complete != FTW_VALID_STATE) && - (FtwDevice->FtwLastWriteRecord->DestinationComplete == FTW_VALID_STATE) && - IsLastRecordOfWrites (FtwDevice->FtwLastWriteHeader, FtwDevice->FtwLastWriteRecord) - ) { - DEBUG ((EFI_D_ERROR, "Ftw: Init.. find last record completed but header not, abort()\n")); - FtwAbort (&FtwDevice->FtwInstance); - } - // - // To check the workspace buffer following last Write header/records is EMPTY or not. - // If it's not EMPTY, FTW also need to call reclaim(). - // - FtwHeader = FtwDevice->FtwLastWriteHeader; - Offset = (UINT8 *) FtwHeader - FtwDevice->FtwWorkSpace; - if (FtwDevice->FtwWorkSpace[Offset] != FTW_ERASED_BYTE) { - Offset += WRITE_TOTAL_SIZE (FtwHeader->NumberOfWrites, FtwHeader->PrivateDataSize); - } - - if (!IsErasedFlashBuffer (FtwDevice->FtwWorkSpace + Offset, FtwDevice->FtwWorkSpaceSize - Offset)) { - Status = FtwReclaimWorkSpace (FtwDevice, TRUE); - ASSERT_EFI_ERROR (Status); - } - - // - // Restart if it's boot block - // - if ((FtwDevice->FtwLastWriteHeader->Complete != FTW_VALID_STATE) && - (FtwDevice->FtwLastWriteRecord->SpareComplete == FTW_VALID_STATE) - ) { - if (FtwDevice->FtwLastWriteRecord->BootBlockUpdate == FTW_VALID_STATE) { - Status = FlushSpareBlockToBootBlock (FtwDevice); - DEBUG ((EFI_D_ERROR, "Ftw: Restart boot block update - %r\n", Status)); - ASSERT_EFI_ERROR (Status); - FtwAbort (&FtwDevice->FtwInstance); - } else { - // - // if (SpareCompleted) THEN Restart to fault tolerant write. - // - FvbHandle = GetFvbByAddress (FtwDevice->FtwLastWriteRecord->FvBaseAddress, &Fvb); - if (FvbHandle != NULL) { - Status = FtwRestart (&FtwDevice->FtwInstance, FvbHandle); - DEBUG ((EFI_D_ERROR, "FtwLite: Restart last write - %r\n", Status)); - ASSERT_EFI_ERROR (Status); - } - FtwAbort (&FtwDevice->FtwInstance); - } - } - // - // Hook the protocol API - // - FtwDevice->FtwInstance.GetMaxBlockSize = FtwGetMaxBlockSize; - FtwDevice->FtwInstance.Allocate = FtwAllocate; - FtwDevice->FtwInstance.Write = FtwWrite; - FtwDevice->FtwInstance.Restart = FtwRestart; - FtwDevice->FtwInstance.Abort = FtwAbort; - FtwDevice->FtwInstance.GetLastWrite = FtwGetLastWrite; - - // - // Install protocol interface - // - Status = gBS->InstallProtocolInterface ( - &FtwDevice->Handle, - &gEfiFaultTolerantWriteProtocolGuid, - EFI_NATIVE_INTERFACE, - &FtwDevice->FtwInstance - ); - - ASSERT_EFI_ERROR (Status); - - // - // Close the notify event to avoid install FaultTolerantWriteProtocol again. - // - Status = gBS->CloseEvent (Event); - ASSERT_EFI_ERROR (Status); - - return; -} - -/** - This function is the entry point of the Fault Tolerant Write driver. - - @param ImageHandle A handle for the image that is initializing this driver - @param SystemTable A pointer to the EFI system table - - @return EFI_SUCCESS FTW has finished the initialization - @retval EFI_NOT_FOUND Locate FVB protocol error - @retval EFI_OUT_OF_RESOURCES Allocate memory error - @retval EFI_VOLUME_CORRUPTED Firmware volume is error - @retval EFI_ABORTED FTW initialization error - -**/ -EFI_STATUS -EFIAPI -InitializeFaultTolerantWrite ( - IN EFI_HANDLE ImageHandle, - IN EFI_SYSTEM_TABLE *SystemTable - ) -{ - EFI_FTW_DEVICE *FtwDevice; - - // - // Allocate Private data of this driver, - // INCLUDING THE FtwWorkSpace[FTW_WORK_SPACE_SIZE]. - // - FtwDevice = NULL; - FtwDevice = AllocateZeroPool (sizeof (EFI_FTW_DEVICE) + PcdGet32 (PcdFlashNvStorageFtwWorkingSize)); - if (FtwDevice == NULL) { - return EFI_OUT_OF_RESOURCES; - } - - ZeroMem (FtwDevice, sizeof (EFI_FTW_DEVICE)); - FtwDevice->Signature = FTW_DEVICE_SIGNATURE; - - // - // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE. - // - - FtwDevice->WorkSpaceLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwWorkingSize); - - FtwDevice->SpareAreaLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwSpareSize); - - if ((FtwDevice->WorkSpaceLength == 0) || (FtwDevice->SpareAreaLength == 0)) { - DEBUG ((EFI_D_ERROR, "Ftw: Workspace or Spare block does not exist!\n")); - FreePool (FtwDevice); - return EFI_OUT_OF_RESOURCES; - } - FtwDevice->FtwFvBlock = NULL; - FtwDevice->FtwBackupFvb = NULL; - FtwDevice->FtwWorkSpaceLba = (EFI_LBA) (-1); - FtwDevice->FtwSpareLba = (EFI_LBA) (-1); - - // - // Register FvbNotificationEvent () notify function. - // - EfiCreateProtocolNotifyEvent ( - &gEfiFirmwareVolumeBlockProtocolGuid, - TPL_CALLBACK, - FvbNotificationEvent, - (VOID *)FtwDevice, - &mFvbRegistration - ); - - return EFI_SUCCESS; -} diff --git a/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.h b/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.h index a75db426fd..00ac5c9c98 100644 --- a/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.h +++ b/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWrite.h @@ -3,7 +3,7 @@ The internal header file includes the common header files, defines internal structure and functions used by FtwLite module. -Copyright (c) 2006 - 2008, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2010, 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 @@ -670,4 +670,71 @@ GetFvbByAddress ( OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock ); +/** + Retrive the proper Swap Address Range protocol interface. + + @param[out] SarProtocol The interface of SAR protocol + + @retval EFI_SUCCESS The SAR protocol instance was found and returned in SarProtocol. + @retval EFI_NOT_FOUND The SAR protocol instance was not found. + @retval EFI_INVALID_PARAMETER SarProtocol is NULL. + +**/ +EFI_STATUS +FtwGetSarProtocol ( + OUT VOID **SarProtocol + ); + +/** + Function returns an array of handles that support the FVB protocol + in a buffer allocated from pool. + + @param[out] NumberHandles The number of handles returned in Buffer. + @param[out] Buffer A pointer to the buffer to return the requested + array of handles that support FVB protocol. + + @retval EFI_SUCCESS The array of handles was returned in Buffer, and the number of + handles in Buffer was returned in NumberHandles. + @retval EFI_NOT_FOUND No FVB handle was found. + @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the matching results. + @retval EFI_INVALID_PARAMETER NumberHandles is NULL or Buffer is NULL. + +**/ +EFI_STATUS +GetFvbCountAndBuffer ( + OUT UINTN *NumberHandles, + OUT EFI_HANDLE **Buffer + ); + + +/** + Allocate private data for FTW driver and initialize it. + + @param[out] FtwData Pointer to the FTW device structure + + @retval EFI_SUCCESS Initialize the FTW device successfully. + @retval EFI_OUT_OF_RESOURCES Allocate memory error + @retval EFI_INVALID_PARAMETER Workspace or Spare block does not exist + +**/ +EFI_STATUS +InitFtwDevice ( + OUT EFI_FTW_DEVICE **FtwData + ); + + +/** + Initialization for Fault Tolerant Write is done in this handler. + + @param[in,out] FtwData Pointer to the FTW device structure + + @retval EFI_SUCCESS Initialize the FTW protocol successfully. + @retval EFI_NOT_FOUND No proper FVB protocol was found. + +**/ +EFI_STATUS +InitFtwProtocol ( + IN OUT EFI_FTW_DEVICE *FtwDevice + ); + #endif diff --git a/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.c b/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.c new file mode 100644 index 0000000000..5f998c0c66 --- /dev/null +++ b/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.c @@ -0,0 +1,250 @@ +/** @file + + This is a simple fault tolerant write driver. + + This boot service protocol only 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. + + The implementation uses an FTW (Fault Tolerant Write) Work Space. + This work space is a memory copy of the work space on the Working Block, + the size of the work space is the FTW_WORK_SPACE_SIZE bytes. + + The work space stores each write record as EFI_FTW_RECORD structure. + The spare block stores the write buffer before write to the target block. + + The write record has three states to specify the different phase of write operation. + 1) WRITE_ALLOCATED is that the record is allocated in write space. + The information of write operation is stored in write record structure. + 2) SPARE_COMPLETED is that the data from write buffer is writed into the spare block as the backup. + 3) WRITE_COMPLETED is that the data is copied from the spare block to the target block. + + This driver operates the data as the whole size of spare block. + It first read the SpareAreaLength data from the target block into the spare memory buffer. + Then copy the write buffer data into the spare memory buffer. + Then write the spare memory buffer into the spare block. + Final copy the data from the spare block to the target block. + + To make this drive work well, the following conditions must be satisfied: + 1. The write NumBytes data must be fit within Spare area. + Offset + NumBytes <= SpareAreaLength + 2. The whole flash range has the same block size. + 3. Working block is an area which contains working space in its last block and has the same size as spare block. + 4. Working Block area must be in the single one Firmware Volume Block range which FVB protocol is produced on. + 5. Spare area must be in the single one Firmware Volume Block range which FVB protocol is produced on. + 6. Any write data area (SpareAreaLength Area) which the data will be written into must be + in the single one Firmware Volume Block range which FVB protocol is produced on. + 7. If write data area (such as Variable range) is enlarged, the spare area range must be enlarged. + The spare area must be enough large to store the write data before write them into the target range. + If one of them is not satisfied, FtwWrite may fail. + Usually, Spare area only takes one block. That's SpareAreaLength = BlockSize, NumberOfSpareBlock = 1. + +Copyright (c) 2006 - 2010, 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. + +**/ + +#include "FaultTolerantWrite.h" +EFI_EVENT mFvbRegistration = NULL; + + +/** + Retrive the FVB protocol interface by HANDLE. + + @param[in] FvBlockHandle The handle of FVB protocol that provides services for + reading, writing, and erasing the target block. + @param[out] FvBlock The interface of FVB protocol + + @retval EFI_SUCCESS The interface information for the specified protocol was returned. + @retval EFI_UNSUPPORTED The device does not support the FVB protocol. + @retval EFI_INVALID_PARAMETER FvBlockHandle is not a valid EFI_HANDLE or FvBlock is NULL. + +**/ +EFI_STATUS +FtwGetFvbByHandle ( + IN EFI_HANDLE FvBlockHandle, + OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock + ) +{ + // + // To get the FVB protocol interface on the handle + // + return gBS->HandleProtocol ( + FvBlockHandle, + &gEfiFirmwareVolumeBlockProtocolGuid, + (VOID **) FvBlock + ); +} + +/** + Retrive the Swap Address Range protocol interface. + + @param[out] SarProtocol The interface of SAR protocol + + @retval EFI_SUCCESS The SAR protocol instance was found and returned in SarProtocol. + @retval EFI_NOT_FOUND The SAR protocol instance was not found. + @retval EFI_INVALID_PARAMETER SarProtocol is NULL. + +**/ +EFI_STATUS +FtwGetSarProtocol ( + OUT VOID **SarProtocol + ) +{ + EFI_STATUS Status; + + // + // Locate Swap Address Range protocol + // + Status = gBS->LocateProtocol ( + &gEfiSwapAddressRangeProtocolGuid, + NULL, + SarProtocol + ); + return Status; +} + +/** + Function returns an array of handles that support the FVB protocol + in a buffer allocated from pool. + + @param[out] NumberHandles The number of handles returned in Buffer. + @param[out] Buffer A pointer to the buffer to return the requested + array of handles that support FVB protocol. + + @retval EFI_SUCCESS The array of handles was returned in Buffer, and the number of + handles in Buffer was returned in NumberHandles. + @retval EFI_NOT_FOUND No FVB handle was found. + @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the matching results. + @retval EFI_INVALID_PARAMETER NumberHandles is NULL or Buffer is NULL. + +**/ +EFI_STATUS +GetFvbCountAndBuffer ( + OUT UINTN *NumberHandles, + OUT EFI_HANDLE **Buffer + ) +{ + EFI_STATUS Status; + + // + // Locate all handles of Fvb protocol + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiFirmwareVolumeBlockProtocolGuid, + NULL, + NumberHandles, + Buffer + ); + return Status; +} + + +/** + Firmware Volume Block Protocol notification event handler. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context Pointer to the notification function's context. + +**/ +VOID +EFIAPI +FvbNotificationEvent ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + EFI_FAULT_TOLERANT_WRITE_PROTOCOL *FtwProtocol; + EFI_FTW_DEVICE *FtwDevice; + + // + // Just return to avoid install SMM FaultTolerantWriteProtocol again + // if Fault Tolerant Write protocol had been installed. + // + Status = gBS->LocateProtocol ( + &gEfiFaultTolerantWriteProtocolGuid, + NULL, + (VOID **) &FtwProtocol + ); + if (!EFI_ERROR (Status)) { + return ; + } + + // + // Found proper FVB protocol and initialize FtwDevice for protocol installation + // + FtwDevice = (EFI_FTW_DEVICE *)Context; + Status = InitFtwProtocol (FtwDevice); + if (EFI_ERROR(Status)) { + return ; + } + + // + // Install protocol interface + // + Status = gBS->InstallProtocolInterface ( + &FtwDevice->Handle, + &gEfiFaultTolerantWriteProtocolGuid, + EFI_NATIVE_INTERFACE, + &FtwDevice->FtwInstance + ); + ASSERT_EFI_ERROR (Status); + + Status = gBS->CloseEvent (Event); + ASSERT_EFI_ERROR (Status); + + return; +} + + +/** + This function is the entry point of the Fault Tolerant Write driver. + + @param[in] ImageHandle A handle for the image that is initializing this driver + @param[in] SystemTable A pointer to the EFI system table + + @retval EFI_SUCCESS The initialization finished successfully. + @retval EFI_OUT_OF_RESOURCES Allocate memory error + @retval EFI_INVALID_PARAMETER Workspace or Spare block does not exist + +**/ +EFI_STATUS +EFIAPI +FaultTolerantWriteInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_FTW_DEVICE *FtwDevice; + + // + // Allocate private data structure for FTW protocol and do some initialization + // + Status = InitFtwDevice (&FtwDevice); + if (EFI_ERROR(Status)) { + return Status; + } + + // + // Register FvbNotificationEvent () notify function. + // + EfiCreateProtocolNotifyEvent ( + &gEfiFirmwareVolumeBlockProtocolGuid, + TPL_CALLBACK, + FvbNotificationEvent, + (VOID *)FtwDevice, + &mFvbRegistration + ); + + return EFI_SUCCESS; +} diff --git a/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf b/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf index 1376959163..61139d486f 100644 --- a/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf +++ b/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf @@ -20,7 +20,7 @@ FILE_GUID = FE5CEA76-4F72-49e8-986F-2CD899DFFE5D MODULE_TYPE = DXE_DRIVER VERSION_STRING = 1.0 - ENTRY_POINT = InitializeFaultTolerantWrite + ENTRY_POINT = FaultTolerantWriteInitialize # # The following information is for reference only and not required by the build tools. @@ -32,6 +32,7 @@ FtwMisc.c UpdateWorkingBlock.c FaultTolerantWrite.c + FaultTolerantWriteDxe.c FaultTolerantWrite.h [Packages] diff --git a/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmm.c b/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmm.c new file mode 100644 index 0000000000..ffa7cb91e4 --- /dev/null +++ b/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmm.c @@ -0,0 +1,281 @@ +/** @file + + This is a simple fault tolerant write driver that is intended to use in the SMM environment. + + This boot service protocol only 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. + + The implementation uses an FTW (Fault Tolerant Write) Work Space. + This work space is a memory copy of the work space on the Working Block, + the size of the work space is the FTW_WORK_SPACE_SIZE bytes. + + The work space stores each write record as EFI_FTW_RECORD structure. + The spare block stores the write buffer before write to the target block. + + The write record has three states to specify the different phase of write operation. + 1) WRITE_ALLOCATED is that the record is allocated in write space. + The information of write operation is stored in write record structure. + 2) SPARE_COMPLETED is that the data from write buffer is writed into the spare block as the backup. + 3) WRITE_COMPLETED is that the data is copied from the spare block to the target block. + + This driver operates the data as the whole size of spare block. + It first read the SpareAreaLength data from the target block into the spare memory buffer. + Then copy the write buffer data into the spare memory buffer. + Then write the spare memory buffer into the spare block. + Final copy the data from the spare block to the target block. + + To make this drive work well, the following conditions must be satisfied: + 1. The write NumBytes data must be fit within Spare area. + Offset + NumBytes <= SpareAreaLength + 2. The whole flash range has the same block size. + 3. Working block is an area which contains working space in its last block and has the same size as spare block. + 4. Working Block area must be in the single one Firmware Volume Block range which FVB protocol is produced on. + 5. Spare area must be in the single one Firmware Volume Block range which FVB protocol is produced on. + 6. Any write data area (SpareAreaLength Area) which the data will be written into must be + in the single one Firmware Volume Block range which FVB protocol is produced on. + 7. If write data area (such as Variable range) is enlarged, the spare area range must be enlarged. + The spare area must be enough large to store the write data before write them into the target range. + If one of them is not satisfied, FtwWrite may fail. + Usually, Spare area only takes one block. That's SpareAreaLength = BlockSize, NumberOfSpareBlock = 1. + +Copyright (c) 2010, 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. + +**/ + +#include +#include "FaultTolerantWrite.h" +#include +#include +#include + +EFI_EVENT mFvbRegistration = NULL; +EFI_FTW_DEVICE *gFtwDevice = NULL; + +/** + Retrive the SMM FVB protocol interface by HANDLE. + + @param[in] FvBlockHandle The handle of SMM FVB protocol that provides services for + reading, writing, and erasing the target block. + @param[out] FvBlock The interface of SMM FVB protocol + + @retval EFI_SUCCESS The interface information for the specified protocol was returned. + @retval EFI_UNSUPPORTED The device does not support the SMM FVB protocol. + @retval EFI_INVALID_PARAMETER FvBlockHandle is not a valid EFI_HANDLE or FvBlock is NULL. + +**/ +EFI_STATUS +FtwGetFvbByHandle ( + IN EFI_HANDLE FvBlockHandle, + OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock + ) +{ + // + // To get the SMM FVB protocol interface on the handle + // + return gSmst->SmmHandleProtocol ( + FvBlockHandle, + &gEfiSmmFirmwareVolumeBlockProtocolGuid, + (VOID **) FvBlock + ); +} + +/** + Retrive the SMM Swap Address Range protocol interface. + + @param[out] SarProtocol The interface of SMM SAR protocol + + @retval EFI_SUCCESS The SMM SAR protocol instance was found and returned in SarProtocol. + @retval EFI_NOT_FOUND The SMM SAR protocol instance was not found. + @retval EFI_INVALID_PARAMETER SarProtocol is NULL. + +**/ +EFI_STATUS +FtwGetSarProtocol ( + OUT VOID **SarProtocol + ) +{ + EFI_STATUS Status; + + // + // Locate Smm Swap Address Range protocol + // + Status = gSmst->SmmLocateProtocol ( + &gEfiSmmSwapAddressRangeProtocolGuid, + NULL, + SarProtocol + ); + return Status; +} + +/** + Function returns an array of handles that support the SMM FVB protocol + in a buffer allocated from pool. + + @param[out] NumberHandles The number of handles returned in Buffer. + @param[out] Buffer A pointer to the buffer to return the requested + array of handles that support SMM FVB protocol. + + @retval EFI_SUCCESS The array of handles was returned in Buffer, and the number of + handles in Buffer was returned in NumberHandles. + @retval EFI_NOT_FOUND No SMM FVB handle was found. + @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the matching results. + @retval EFI_INVALID_PARAMETER NumberHandles is NULL or Buffer is NULL. + +**/ +EFI_STATUS +GetFvbCountAndBuffer ( + OUT UINTN *NumberHandles, + OUT EFI_HANDLE **Buffer + ) +{ + EFI_STATUS Status; + UINTN BufferSize; + + if ((NumberHandles == NULL) || (Buffer == NULL)) { + return EFI_INVALID_PARAMETER; + } + + BufferSize = 0; + *NumberHandles = 0; + *Buffer = NULL; + Status = gSmst->SmmLocateHandle ( + ByProtocol, + &gEfiSmmFirmwareVolumeBlockProtocolGuid, + NULL, + &BufferSize, + *Buffer + ); + if (EFI_ERROR(Status) && Status != EFI_BUFFER_TOO_SMALL) { + return EFI_NOT_FOUND; + } + + *Buffer = AllocatePool (BufferSize); + if (*Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = gSmst->SmmLocateHandle ( + ByProtocol, + &gEfiSmmFirmwareVolumeBlockProtocolGuid, + NULL, + &BufferSize, + *Buffer + ); + + *NumberHandles = BufferSize / sizeof(EFI_HANDLE); + if (EFI_ERROR(Status)) { + *NumberHandles = 0; + } + + return Status; +} + + +/** + SMM Firmware Volume Block Protocol notification event handler. + + @param[in] Protocol Points to the protocol's unique identifier + @param[in] Interface Points to the interface instance + @param[in] Handle The handle on which the interface was installed + + @retval EFI_SUCCESS SmmEventCallback runs successfully + + **/ +EFI_STATUS +EFIAPI +FvbNotificationEvent ( + IN CONST EFI_GUID *Protocol, + IN VOID *Interface, + IN EFI_HANDLE Handle + ) +{ + EFI_STATUS Status; + EFI_SMM_FAULT_TOLERANT_WRITE_PROTOCOL *FtwProtocol; + + // + // Just return to avoid install SMM FaultTolerantWriteProtocol again + // if SMM Fault Tolerant Write protocol had been installed. + // + Status = gSmst->SmmLocateProtocol ( + &gEfiSmmFaultTolerantWriteProtocolGuid, + NULL, + (VOID **) &FtwProtocol + ); + if (!EFI_ERROR (Status)) { + return EFI_SUCCESS; + } + + // + // Found proper FVB protocol and initialize FtwDevice for protocol installation + // + Status = InitFtwProtocol (gFtwDevice); + if (EFI_ERROR(Status)) { + return Status; + } + + // + // Install protocol interface + // + Status = gSmst->SmmInstallProtocolInterface ( + &gFtwDevice->Handle, + &gEfiSmmFaultTolerantWriteProtocolGuid, + EFI_NATIVE_INTERFACE, + &gFtwDevice->FtwInstance + ); + ASSERT_EFI_ERROR (Status); + + return EFI_SUCCESS; +} + + +/** + This function is the entry point of the Fault Tolerant Write driver. + + @param[in] ImageHandle A handle for the image that is initializing this driver + @param[in] SystemTable A pointer to the EFI system table + + @retval EFI_SUCCESS The initialization finished successfully. + @retval EFI_OUT_OF_RESOURCES Allocate memory error + @retval EFI_INVALID_PARAMETER Workspace or Spare block does not exist + +**/ +EFI_STATUS +EFIAPI +SmmFaultTolerantWriteInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // Allocate private data structure for SMM FTW protocol and do some initialization + // + Status = InitFtwDevice (&gFtwDevice); + if (EFI_ERROR(Status)) { + return Status; + } + + // + // Register FvbNotificationEvent () notify function. + // + Status = gSmst->SmmRegisterProtocolNotify ( + &gEfiSmmFirmwareVolumeBlockProtocolGuid, + FvbNotificationEvent, + &mFvbRegistration + ); + ASSERT_EFI_ERROR (Status); + + FvbNotificationEvent (NULL, NULL, NULL); + + return EFI_SUCCESS; +} diff --git a/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmm.inf b/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmm.inf new file mode 100644 index 0000000000..356172171d --- /dev/null +++ b/MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmm.inf @@ -0,0 +1,73 @@ +## @file +# This driver installs SMM Fault Tolerant Write (FTW) protocol, which provides fault +# tolerant write capability in SMM environment for block devices. Its implementation +# depends on the full functionality SMM FVB protocol that support read, write/erase +# flash access. +# +# Copyright (c) 2010, 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. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = SmmFaultTolerantWriteDxe + FILE_GUID = 470CB248-E8AC-473c-BB4F-81069A1FE6FD + MODULE_TYPE = DXE_SMM_DRIVER + VERSION_STRING = 1.0 + PI_SPECIFICATION_VERSION = 0x0001000A + ENTRY_POINT = SmmFaultTolerantWriteInitialize + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + FtwMisc.c + UpdateWorkingBlock.c + FaultTolerantWrite.c + FaultTolerantWriteSmm.c + FaultTolerantWrite.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + SmmServicesTableLib + MemoryAllocationLib + BaseMemoryLib + UefiDriverEntryPoint + DebugLib + UefiLib + +[Guids] + gEfiSystemNvDataFvGuid ## CONSUMES ## FV Signature of Working Space Header + +[Protocols] + gEfiSmmSwapAddressRangeProtocolGuid | PcdFullFtwServiceEnable ## CONSUMES + gEfiSmmFirmwareVolumeBlockProtocolGuid ## CONSUMES + gEfiSmmFaultTolerantWriteProtocolGuid ## PRODUCES + +[FeaturePcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdFullFtwServiceEnable + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase64 + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase64 + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize + +[Depex] + gEfiSmmFirmwareVolumeBlockProtocolGuid + diff --git a/MdeModulePkg/Universal/FaultTolerantWriteDxe/FtwMisc.c b/MdeModulePkg/Universal/FaultTolerantWriteDxe/FtwMisc.c index b8550f5d86..7a32f4db83 100644 --- a/MdeModulePkg/Universal/FaultTolerantWriteDxe/FtwMisc.c +++ b/MdeModulePkg/Universal/FaultTolerantWriteDxe/FtwMisc.c @@ -2,7 +2,7 @@ Internal generic functions to operate flash block. -Copyright (c) 2006 - 2008, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2010, 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 @@ -104,34 +104,6 @@ FtwEraseSpareBlock ( ); } -/** - Retrive the proper FVB protocol interface by HANDLE. - - - @param FvBlockHandle The handle of FVB protocol that provides services for - reading, writing, and erasing the target block. - @param FvBlock The interface of FVB protocol - - @retval EFI_SUCCESS The function completed successfully - @retval EFI_ABORTED The function could not complete successfully - -**/ -EFI_STATUS -FtwGetFvbByHandle ( - IN EFI_HANDLE FvBlockHandle, - OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock - ) -{ - // - // To get the FVB protocol interface on the handle - // - return gBS->HandleProtocol ( - FvBlockHandle, - &gEfiFirmwareVolumeBlockProtocolGuid, - (VOID **) FvBlock - ); -} - /** Is it in working block? @@ -195,13 +167,7 @@ GetFvbByAddress ( // // Locate all handles of Fvb protocol // - Status = gBS->LocateHandleBuffer ( - ByProtocol, - &gEfiFirmwareVolumeBlockProtocolGuid, - NULL, - &HandleCount, - &HandleBuffer - ); + Status = GetFvbCountAndBuffer (&HandleCount, &HandleBuffer); if (EFI_ERROR (Status)) { return NULL; } @@ -209,11 +175,7 @@ GetFvbByAddress ( // Get the FVB to access variable store // for (Index = 0; Index < HandleCount; Index += 1) { - Status = gBS->HandleProtocol ( - HandleBuffer[Index], - &gEfiFirmwareVolumeBlockProtocolGuid, - (VOID **) &Fvb - ); + Status = FtwGetFvbByHandle (HandleBuffer[Index], &Fvb); if (EFI_ERROR (Status)) { break; } @@ -269,7 +231,7 @@ IsBootBlock ( return FALSE; } - Status = gBS->LocateProtocol (&gEfiSwapAddressRangeProtocolGuid, NULL, (VOID **) &SarProtocol); + Status = FtwGetSarProtocol ((VOID **) &SarProtocol); if (EFI_ERROR (Status)) { return FALSE; } @@ -358,7 +320,7 @@ FlushSpareBlockToBootBlock ( // // Locate swap address range protocol // - Status = gBS->LocateProtocol (&gEfiSwapAddressRangeProtocolGuid, NULL, (VOID **) &SarProtocol); + Status = FtwGetSarProtocol ((VOID **) &SarProtocol); if (EFI_ERROR (Status)) { return Status; } @@ -969,3 +931,371 @@ GetPreviousRecordOfWrites ( *FtwRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) Ptr; return EFI_SUCCESS; } + +/** + Allocate private data for FTW driver and initialize it. + + @param[out] FtwData Pointer to the FTW device structure + + @retval EFI_SUCCESS Initialize the FTW device successfully. + @retval EFI_OUT_OF_RESOURCES Allocate memory error + @retval EFI_INVALID_PARAMETER Workspace or Spare block does not exist + +**/ +EFI_STATUS +InitFtwDevice ( + OUT EFI_FTW_DEVICE **FtwData + ) +{ + EFI_FTW_DEVICE *FtwDevice; + + // + // Allocate private data of this driver, + // Including the FtwWorkSpace[FTW_WORK_SPACE_SIZE]. + // + FtwDevice = AllocateZeroPool (sizeof (EFI_FTW_DEVICE) + PcdGet32 (PcdFlashNvStorageFtwWorkingSize)); + if (FtwDevice == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE. + // + FtwDevice->WorkSpaceLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwWorkingSize); + FtwDevice->SpareAreaLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwSpareSize); + if ((FtwDevice->WorkSpaceLength == 0) || (FtwDevice->SpareAreaLength == 0)) { + DEBUG ((EFI_D_ERROR, "Ftw: Workspace or Spare block does not exist!\n")); + FreePool (FtwDevice); + return EFI_INVALID_PARAMETER; + } + + FtwDevice->Signature = FTW_DEVICE_SIGNATURE; + FtwDevice->FtwFvBlock = NULL; + FtwDevice->FtwBackupFvb = NULL; + FtwDevice->FtwWorkSpaceLba = (EFI_LBA) (-1); + FtwDevice->FtwSpareLba = (EFI_LBA) (-1); + + FtwDevice->WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageFtwWorkingBase64); + if (FtwDevice->WorkSpaceAddress == 0) { + FtwDevice->WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwWorkingBase); + } + + FtwDevice->SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageFtwSpareBase64); + if (FtwDevice->SpareAreaAddress == 0) { + FtwDevice->SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwSpareBase); + } + + *FtwData = FtwDevice; + return EFI_SUCCESS; +} + + +/** + Initialization for Fault Tolerant Write is done in this handler. + + @param[in,out] FtwData Pointer to the FTW device structure + + @retval EFI_SUCCESS Initialize the FTW device successfully. + @retval EFI_NOT_FOUND No proper FVB protocol was found. + @retval EFI_ABORTED Some data can not be got or be invalid. + +**/ +EFI_STATUS +FindFvbForFtw ( + IN OUT EFI_FTW_DEVICE *FtwDevice + ) +{ + EFI_STATUS Status; + EFI_HANDLE *HandleBuffer; + UINTN HandleCount; + UINTN Index; + EFI_PHYSICAL_ADDRESS FvbBaseAddress; + EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb; + EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader; + EFI_FVB_ATTRIBUTES_2 Attributes; + EFI_FV_BLOCK_MAP_ENTRY *FvbMapEntry; + UINT32 LbaIndex; + + // + // Get all FVB handle. + // + Status = GetFvbCountAndBuffer (&HandleCount, &HandleBuffer); + if (EFI_ERROR (Status)) { + return EFI_NOT_FOUND; + } + + // + // Get the FVB to access variable store + // + Fvb = NULL; + for (Index = 0; Index < HandleCount; Index += 1) { + Status = FtwGetFvbByHandle (HandleBuffer[Index], &Fvb); + if (EFI_ERROR (Status)) { + Status = EFI_NOT_FOUND; + break; + } + + // + // Ensure this FVB protocol support Write operation. + // + Status = Fvb->GetAttributes (Fvb, &Attributes); + if (EFI_ERROR (Status) || ((Attributes & EFI_FVB2_WRITE_STATUS) == 0)) { + continue; + } + // + // Compare the address and select the right one + // + Status = Fvb->GetPhysicalAddress (Fvb, &FvbBaseAddress); + if (EFI_ERROR (Status)) { + continue; + } + + FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) ((UINTN) FvbBaseAddress); + if ((FtwDevice->FtwFvBlock == NULL) && (FtwDevice->WorkSpaceAddress >= FvbBaseAddress) && + ((FtwDevice->WorkSpaceAddress + FtwDevice->WorkSpaceLength) <= (FvbBaseAddress + FwVolHeader->FvLength)) + ) { + FtwDevice->FtwFvBlock = Fvb; + // + // To get the LBA of work space + // + if ((FwVolHeader->FvLength) > (FwVolHeader->HeaderLength)) { + // + // Now, one FV has one type of BlockLength + // + FvbMapEntry = &FwVolHeader->BlockMap[0]; + for (LbaIndex = 1; LbaIndex <= FvbMapEntry->NumBlocks; LbaIndex += 1) { + if ((FtwDevice->WorkSpaceAddress >= (FvbBaseAddress + FvbMapEntry->Length * (LbaIndex - 1))) + && (FtwDevice->WorkSpaceAddress < (FvbBaseAddress + FvbMapEntry->Length * LbaIndex))) { + FtwDevice->FtwWorkSpaceLba = LbaIndex - 1; + // + // Get the Work space size and Base(Offset) + // + FtwDevice->FtwWorkSpaceSize = FtwDevice->WorkSpaceLength; + FtwDevice->FtwWorkSpaceBase = (UINTN) (FtwDevice->WorkSpaceAddress - (FvbBaseAddress + FvbMapEntry->Length * (LbaIndex - 1))); + break; + } + } + } + } + + if ((FtwDevice->FtwBackupFvb == NULL) && (FtwDevice->SpareAreaAddress >= FvbBaseAddress) && + ((FtwDevice->SpareAreaAddress + FtwDevice->SpareAreaLength) <= (FvbBaseAddress + FwVolHeader->FvLength)) + ) { + FtwDevice->FtwBackupFvb = Fvb; + // + // To get the LBA of spare + // + if ((FwVolHeader->FvLength) > (FwVolHeader->HeaderLength)) { + // + // Now, one FV has one type of BlockLength + // + FvbMapEntry = &FwVolHeader->BlockMap[0]; + for (LbaIndex = 1; LbaIndex <= FvbMapEntry->NumBlocks; LbaIndex += 1) { + if ((FtwDevice->SpareAreaAddress >= (FvbBaseAddress + FvbMapEntry->Length * (LbaIndex - 1))) + && (FtwDevice->SpareAreaAddress < (FvbBaseAddress + FvbMapEntry->Length * LbaIndex))) { + // + // Get the NumberOfSpareBlock and BlockSize + // + FtwDevice->FtwSpareLba = LbaIndex - 1; + FtwDevice->BlockSize = FvbMapEntry->Length; + FtwDevice->NumberOfSpareBlock = FtwDevice->SpareAreaLength / FtwDevice->BlockSize; + // + // Check the range of spare area to make sure that it's in FV range + // + if ((FtwDevice->FtwSpareLba + FtwDevice->NumberOfSpareBlock) > FvbMapEntry->NumBlocks) { + DEBUG ((EFI_D_ERROR, "Ftw: Spare area is out of FV range\n")); + FreePool (HandleBuffer); + ASSERT (FALSE); + return EFI_ABORTED; + } + break; + } + } + } + } + } + FreePool (HandleBuffer); + + if ((FtwDevice->FtwBackupFvb == NULL) || (FtwDevice->FtwFvBlock == NULL) || + (FtwDevice->FtwWorkSpaceLba == (EFI_LBA) (-1)) || (FtwDevice->FtwSpareLba == (EFI_LBA) (-1))) { + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + + +/** + Initialization for Fault Tolerant Write protocol. + + @param[in,out] FtwData Pointer to the FTW device structure + + @retval EFI_SUCCESS Initialize the FTW protocol successfully. + @retval EFI_NOT_FOUND No proper FVB protocol was found. + +**/ +EFI_STATUS +InitFtwProtocol ( + IN OUT EFI_FTW_DEVICE *FtwDevice + ) +{ + EFI_STATUS Status; + EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb; + UINTN Length; + EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader; + UINTN Offset; + EFI_HANDLE FvbHandle; + + // + // Find the right SMM Fvb protocol instance for FTW. + // + Status = FindFvbForFtw (FtwDevice); + if (EFI_ERROR (Status)) { + return EFI_NOT_FOUND; + } + + // + // Calculate the start LBA of working block. Working block is an area which + // contains working space in its last block and has the same size as spare + // block, unless there are not enough blocks before the block that contains + // working space. + // + FtwDevice->FtwWorkBlockLba = FtwDevice->FtwWorkSpaceLba - FtwDevice->NumberOfSpareBlock + 1; + ASSERT ((INT64) (FtwDevice->FtwWorkBlockLba) >= 0); + + // + // Initialize other parameters, and set WorkSpace as FTW_ERASED_BYTE. + // + FtwDevice->FtwWorkSpace = (UINT8 *) (FtwDevice + 1); + FtwDevice->FtwWorkSpaceHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) FtwDevice->FtwWorkSpace; + + FtwDevice->FtwLastWriteHeader = NULL; + FtwDevice->FtwLastWriteRecord = NULL; + + // + // Refresh the working space data from working block + // + Status = WorkSpaceRefresh (FtwDevice); + ASSERT_EFI_ERROR (Status); + // + // If the working block workspace is not valid, try the spare block + // + if (!IsValidWorkSpace (FtwDevice->FtwWorkSpaceHeader)) { + // + // Read from spare block + // + Length = FtwDevice->FtwWorkSpaceSize; + Status = FtwDevice->FtwBackupFvb->Read ( + FtwDevice->FtwBackupFvb, + FtwDevice->FtwSpareLba, + FtwDevice->FtwWorkSpaceBase, + &Length, + FtwDevice->FtwWorkSpace + ); + ASSERT_EFI_ERROR (Status); + + // + // If spare block is valid, then replace working block content. + // + if (IsValidWorkSpace (FtwDevice->FtwWorkSpaceHeader)) { + Status = FlushSpareBlockToWorkingBlock (FtwDevice); + DEBUG ((EFI_D_ERROR, "Ftw: Restart working block update in InitFtwProtocol() - %r\n", Status)); + FtwAbort (&FtwDevice->FtwInstance); + // + // Refresh work space. + // + Status = WorkSpaceRefresh (FtwDevice); + ASSERT_EFI_ERROR (Status); + } else { + DEBUG ((EFI_D_ERROR, "Ftw: Both are invalid, init workspace\n")); + // + // If both are invalid, then initialize work space. + // + SetMem ( + FtwDevice->FtwWorkSpace, + FtwDevice->FtwWorkSpaceSize, + FTW_ERASED_BYTE + ); + InitWorkSpaceHeader (FtwDevice->FtwWorkSpaceHeader); + // + // Initialize the work space + // + Status = FtwReclaimWorkSpace (FtwDevice, FALSE); + ASSERT_EFI_ERROR (Status); + } + } + // + // If the FtwDevice->FtwLastWriteRecord is 1st record of write header && + // (! SpareComplete) THEN call Abort(). + // + if ((FtwDevice->FtwLastWriteHeader->HeaderAllocated == FTW_VALID_STATE) && + (FtwDevice->FtwLastWriteRecord->SpareComplete != FTW_VALID_STATE) && + IsFirstRecordOfWrites (FtwDevice->FtwLastWriteHeader, FtwDevice->FtwLastWriteRecord) + ) { + DEBUG ((EFI_D_ERROR, "Ftw: Init.. find first record not SpareCompleted, abort()\n")); + FtwAbort (&FtwDevice->FtwInstance); + } + // + // If Header is incompleted and the last record has completed, then + // call Abort() to set the Header->Complete FLAG. + // + if ((FtwDevice->FtwLastWriteHeader->Complete != FTW_VALID_STATE) && + (FtwDevice->FtwLastWriteRecord->DestinationComplete == FTW_VALID_STATE) && + IsLastRecordOfWrites (FtwDevice->FtwLastWriteHeader, FtwDevice->FtwLastWriteRecord) + ) { + DEBUG ((EFI_D_ERROR, "Ftw: Init.. find last record completed but header not, abort()\n")); + FtwAbort (&FtwDevice->FtwInstance); + } + // + // To check the workspace buffer following last Write header/records is EMPTY or not. + // If it's not EMPTY, FTW also need to call reclaim(). + // + FtwHeader = FtwDevice->FtwLastWriteHeader; + Offset = (UINT8 *) FtwHeader - FtwDevice->FtwWorkSpace; + if (FtwDevice->FtwWorkSpace[Offset] != FTW_ERASED_BYTE) { + Offset += WRITE_TOTAL_SIZE (FtwHeader->NumberOfWrites, FtwHeader->PrivateDataSize); + } + + if (!IsErasedFlashBuffer (FtwDevice->FtwWorkSpace + Offset, FtwDevice->FtwWorkSpaceSize - Offset)) { + Status = FtwReclaimWorkSpace (FtwDevice, TRUE); + ASSERT_EFI_ERROR (Status); + } + + // + // Restart if it's boot block + // + if ((FtwDevice->FtwLastWriteHeader->Complete != FTW_VALID_STATE) && + (FtwDevice->FtwLastWriteRecord->SpareComplete == FTW_VALID_STATE) + ) { + if (FtwDevice->FtwLastWriteRecord->BootBlockUpdate == FTW_VALID_STATE) { + Status = FlushSpareBlockToBootBlock (FtwDevice); + DEBUG ((EFI_D_ERROR, "Ftw: Restart boot block update - %r\n", Status)); + ASSERT_EFI_ERROR (Status); + FtwAbort (&FtwDevice->FtwInstance); + } else { + // + // if (SpareCompleted) THEN Restart to fault tolerant write. + // + FvbHandle = NULL; + FvbHandle = GetFvbByAddress (FtwDevice->FtwLastWriteRecord->FvBaseAddress, &Fvb); + if (FvbHandle != NULL) { + Status = FtwRestart (&FtwDevice->FtwInstance, FvbHandle); + DEBUG ((EFI_D_ERROR, "FtwLite: Restart last write - %r\n", Status)); + ASSERT_EFI_ERROR (Status); + } + FtwAbort (&FtwDevice->FtwInstance); + } + } + // + // Hook the protocol API + // + FtwDevice->FtwInstance.GetMaxBlockSize = FtwGetMaxBlockSize; + FtwDevice->FtwInstance.Allocate = FtwAllocate; + FtwDevice->FtwInstance.Write = FtwWrite; + FtwDevice->FtwInstance.Restart = FtwRestart; + FtwDevice->FtwInstance.Abort = FtwAbort; + FtwDevice->FtwInstance.GetLastWrite = FtwGetLastWrite; + + return EFI_SUCCESS; +} + diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/Reclaim.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/Reclaim.c index 79962daa64..11bf1a02b6 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/Reclaim.c +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/Reclaim.c @@ -3,7 +3,7 @@ Handles non-volatile variable store garbage collection, using FTW (Fault Tolerant Write) protocol. -Copyright (c) 2006 - 2008, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2010, 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 @@ -14,105 +14,23 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ - #include "Variable.h" -/** - Gets firmware volume block handle by given address. - - This function gets firmware volume block handle whose - address range contains the parameter Address. - - @param Address Address which should be contained - by returned FVB handle - @param FvbHandle Pointer to FVB handle for output - - @retval EFI_SUCCESS FVB handle successfully returned - @retval EFI_NOT_FOUND Fail to find FVB handle by address - -**/ -EFI_STATUS -GetFvbHandleByAddress ( - IN EFI_PHYSICAL_ADDRESS Address, - OUT EFI_HANDLE *FvbHandle - ) -{ - EFI_STATUS Status; - EFI_HANDLE *HandleBuffer; - UINTN HandleCount; - UINTN Index; - EFI_PHYSICAL_ADDRESS FvbBaseAddress; - EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb; - EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader; - EFI_FVB_ATTRIBUTES_2 Attributes; - - *FvbHandle = NULL; - // - // Locate all handles of Fvb protocol - // - Status = gBS->LocateHandleBuffer ( - ByProtocol, - &gEfiFirmwareVolumeBlockProtocolGuid, - NULL, - &HandleCount, - &HandleBuffer - ); - if (EFI_ERROR (Status)) { - return EFI_NOT_FOUND; - } - // - // Get the FVB to access variable store - // - for (Index = 0; Index < HandleCount; Index += 1) { - Status = gBS->HandleProtocol ( - HandleBuffer[Index], - &gEfiFirmwareVolumeBlockProtocolGuid, - (VOID **) &Fvb - ); - if (EFI_ERROR (Status)) { - Status = EFI_NOT_FOUND; - break; - } - - Status = Fvb->GetAttributes (Fvb, &Attributes); - if (EFI_ERROR (Status) || ((Attributes & EFI_FVB2_WRITE_STATUS) == 0)) { - continue; - } - // - // Compare the address and select the right one - // - Status = Fvb->GetPhysicalAddress (Fvb, &FvbBaseAddress); - if (EFI_ERROR (Status)) { - continue; - } - - FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) ((UINTN) FvbBaseAddress); - if ((Address >= FvbBaseAddress) && (Address <= (FvbBaseAddress + FwVolHeader->FvLength))) { - *FvbHandle = HandleBuffer[Index]; - Status = EFI_SUCCESS; - break; - } - } - - FreePool (HandleBuffer); - return Status; -} - /** Gets LBA of block and offset by given address. - This function gets the Logical Block Address (LBA) of firmware - volume block containing the given address, and the offset of + This function gets the Logical Block Address (LBA) of a firmware + volume block containing the given address, and the offset of the address on the block. @param Address Address which should be contained - by returned FVB handle - @param Lba Pointer to LBA for output - @param Offset Pointer to offset for output + by returned FVB handle. + @param Lba Pointer to LBA for output. + @param Offset Pointer to offset for output. - @retval EFI_SUCCESS LBA and offset successfully returned - @retval EFI_NOT_FOUND Fail to find FVB handle by address - @retval EFI_ABORTED Fail to find valid LBA and offset + @retval EFI_SUCCESS LBA and offset successfully returned. + @retval EFI_NOT_FOUND Fail to find FVB handle by address. + @retval EFI_ABORTED Fail to find valid LBA and offset. **/ EFI_STATUS @@ -123,7 +41,6 @@ GetLbaAndOffsetByAddress ( ) { EFI_STATUS Status; - EFI_HANDLE FvbHandle; EFI_PHYSICAL_ADDRESS FvbBaseAddress; EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb; EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader; @@ -132,25 +49,17 @@ GetLbaAndOffsetByAddress ( *Lba = (EFI_LBA) (-1); *Offset = 0; - + // - // Get the proper FVB + // Get the proper FVB protocol. // - Status = GetFvbHandleByAddress (Address, &FvbHandle); + Status = GetFvbInfoByAddress (Address, NULL, &Fvb); if (EFI_ERROR (Status)) { return Status; } - Status = gBS->HandleProtocol ( - FvbHandle, - &gEfiFirmwareVolumeBlockProtocolGuid, - (VOID **) &Fvb - ); - if (EFI_ERROR (Status)) { - return Status; - } // - // Get the Base Address of FV + // Get the Base Address of FV. // Status = Fvb->GetPhysicalAddress (Fvb, &FvbBaseAddress); if (EFI_ERROR (Status)) { @@ -160,24 +69,22 @@ GetLbaAndOffsetByAddress ( FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) ((UINTN) FvbBaseAddress); // - // Get the (LBA, Offset) of Address + // Get the (LBA, Offset) of Address. // - if ((Address >= FvbBaseAddress) && (Address <= (FvbBaseAddress + FwVolHeader->FvLength))) { - if ((FwVolHeader->FvLength) > (FwVolHeader->HeaderLength)) { - // - // BUGBUG: Assume one FV has one type of BlockLength - // - FvbMapEntry = &FwVolHeader->BlockMap[0]; - for (LbaIndex = 1; LbaIndex <= FvbMapEntry->NumBlocks; LbaIndex += 1) { - if (Address < (FvbBaseAddress + FvbMapEntry->Length * LbaIndex)) { - // - // Found the (Lba, Offset) - // - *Lba = LbaIndex - 1; - *Offset = (UINTN) (Address - (FvbBaseAddress + FvbMapEntry->Length * (LbaIndex - 1))); - return EFI_SUCCESS; - } - } + if ((FwVolHeader->FvLength) > (FwVolHeader->HeaderLength)) { + // + // BUGBUG: Assume one FV has one type of BlockLength. + // + FvbMapEntry = &FwVolHeader->BlockMap[0]; + for (LbaIndex = 1; LbaIndex <= FvbMapEntry->NumBlocks; LbaIndex += 1) { + if (Address < (FvbBaseAddress + FvbMapEntry->Length * LbaIndex)) { + // + // Found the (Lba, Offset). + // + *Lba = LbaIndex - 1; + *Offset = (UINTN) (Address - (FvbBaseAddress + FvbMapEntry->Length * (LbaIndex - 1))); + return EFI_SUCCESS; + } } } @@ -187,17 +94,17 @@ GetLbaAndOffsetByAddress ( /** Writes a buffer to variable storage space, in the working block. - This function writes a buffer to variable storage space into firmware + This function writes a buffer to variable storage space into a firmware volume block device. The destination is specified by parameter VariableBase. Fault Tolerant Write protocol is used for writing. @param VariableBase Base address of variable to write - @param Buffer Point to the data buffer - @param BufferSize The number of bytes of the data Buffer + @param Buffer Point to the data buffer. + @param BufferSize The number of bytes of the data Buffer. - @retval EFI_SUCCESS The function completed successfully - @retval EFI_NOT_FOUND Fail to locate Fault Tolerant Write protocol - @retval EFI_ABORTED The function could not complete successfully + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_NOT_FOUND Fail to locate Fault Tolerant Write protocol. + @retval EFI_ABORTED The function could not complete successfully. **/ EFI_STATUS @@ -216,35 +123,31 @@ FtwVariableSpace ( EFI_FAULT_TOLERANT_WRITE_PROTOCOL *FtwProtocol; // - // Locate fault tolerant write protocol + // Locate fault tolerant write protocol. // - Status = gBS->LocateProtocol ( - &gEfiFaultTolerantWriteProtocolGuid, - NULL, - (VOID **) &FtwProtocol - ); + Status = GetFtwProtocol((VOID **) &FtwProtocol); if (EFI_ERROR (Status)) { return EFI_NOT_FOUND; } // - // Locate Fvb handle by address + // Locate Fvb handle by address. // - Status = GetFvbHandleByAddress (VariableBase, &FvbHandle); + Status = GetFvbInfoByAddress (VariableBase, &FvbHandle, NULL); if (EFI_ERROR (Status)) { return Status; } // - // Get LBA and Offset by address + // Get LBA and Offset by address. // Status = GetLbaAndOffsetByAddress (VariableBase, &VarLba, &VarOffset); if (EFI_ERROR (Status)) { return EFI_ABORTED; } // - // Prepare for the variable data + // Prepare for the variable data. // FtwBufferSize = ((VARIABLE_STORE_HEADER *) ((UINTN) VariableBase))->Size; - FtwBuffer = AllocateRuntimePool (FtwBufferSize); + FtwBuffer = AllocatePool (FtwBufferSize); if (FtwBuffer == NULL) { return EFI_OUT_OF_RESOURCES; } @@ -253,17 +156,17 @@ FtwVariableSpace ( CopyMem (FtwBuffer, Buffer, BufferSize); // - // FTW write record + // FTW write record. // Status = FtwProtocol->Write ( - FtwProtocol, - VarLba, // LBA - VarOffset, // Offset - FtwBufferSize, // NumBytes - NULL, // PrivateData NULL - FvbHandle, // Fvb Handle - FtwBuffer // write buffer - ); + FtwProtocol, + VarLba, // LBA + VarOffset, // Offset + FtwBufferSize, // NumBytes + NULL, // PrivateData NULL + FvbHandle, // Fvb Handle + FtwBuffer // write buffer + ); FreePool (FtwBuffer); return Status; diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c index dca3e30370..9a2468efb0 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c @@ -1,7 +1,7 @@ /** @file - Implement all four UEFI Runtime Variable services for the nonvolatile - and volatile storage space and install variable architecture protocol. + The common variable operation routines shared by DXE_RINTIME variable + module and DXE_SMM variable module. Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
This program and the accompanying materials @@ -17,110 +17,16 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "Variable.h" VARIABLE_MODULE_GLOBAL *mVariableModuleGlobal; -EFI_EVENT mVirtualAddressChangeEvent = NULL; -EFI_HANDLE mHandle = NULL; /// -/// The current Hii implementation accesses this variable many times on every boot. -/// Other common variables are only accessed once. This is why this cache algorithm -/// only targets a single variable. Probably to get an performance improvement out of -/// a Cache you would need a cache that improves the search performance for a variable. +/// Define a memory cache that improves the search performance for a variable. /// -VARIABLE_CACHE_ENTRY mVariableCache[] = { - { - &gEfiGlobalVariableGuid, - L"Lang", - 0x00000000, - 0x00, - NULL - }, - { - &gEfiGlobalVariableGuid, - L"PlatformLang", - 0x00000000, - 0x00, - NULL - } -}; - -VARIABLE_INFO_ENTRY *gVariableInfo = NULL; -EFI_EVENT mFvbRegistration = NULL; - -/** - Update the variable region with Variable information. These are the same - arguments as the EFI Variable services. - - @param[in] VariableName Name of variable - - @param[in] VendorGuid Guid of variable - - @param[in] Data Variable data - - @param[in] DataSize Size of data. 0 means delete - - @param[in] Attributes Attribues of the variable - - @param[in] Variable The variable information which is used to keep track of variable usage. - - @retval EFI_SUCCESS The update operation is success. - - @retval EFI_OUT_OF_RESOURCES Variable region is full, can not write other data into this region. - -**/ -EFI_STATUS -EFIAPI -UpdateVariable ( - IN CHAR16 *VariableName, - IN EFI_GUID *VendorGuid, - IN VOID *Data, - IN UINTN DataSize, - IN UINT32 Attributes OPTIONAL, - IN VARIABLE_POINTER_TRACK *Variable - ); +VARIABLE_STORE_HEADER *mNvVariableCache = NULL; -/** - Acquires lock only at boot time. Simply returns at runtime. - - This is a temperary function which will be removed when - EfiAcquireLock() in UefiLib can handle the call in UEFI - Runtimer driver in RT phase. - It calls EfiAcquireLock() at boot time, and simply returns - at runtime. - - @param Lock A pointer to the lock to acquire - -**/ -VOID -AcquireLockOnlyAtBootTime ( - IN EFI_LOCK *Lock - ) -{ - if (!EfiAtRuntime ()) { - EfiAcquireLock (Lock); - } -} - -/** - Releases lock only at boot time. Simply returns at runtime. - - This is a temperary function which will be removed when - EfiReleaseLock() in UefiLib can handle the call in UEFI - Runtimer driver in RT phase. - It calls EfiReleaseLock() at boot time, and simply returns - at runtime. - - @param Lock A pointer to the lock to release - -**/ -VOID -ReleaseLockOnlyAtBootTime ( - IN EFI_LOCK *Lock - ) -{ - if (!EfiAtRuntime ()) { - EfiReleaseLock (Lock); - } -} +/// +/// The memory entry used for variable statistics data. +/// +VARIABLE_INFO_ENTRY *gVariableInfo = NULL; /** @@ -134,12 +40,12 @@ ReleaseLockOnlyAtBootTime ( the transaction. Data is allocated by this routine, but never freed. - @param[in] VariableName Name of the Variable to track - @param[in] VendorGuid Guid of the Variable to track - @param[in] Volatile TRUE if volatile FALSE if non-volatile - @param[in] Read TRUE if GetVariable() was called - @param[in] Write TRUE if SetVariable() was called - @param[in] Delete TRUE if deleted via SetVariable() + @param[in] VariableName Name of the Variable to track. + @param[in] VendorGuid Guid of the Variable to track. + @param[in] Volatile TRUE if volatile FALSE if non-volatile. + @param[in] Read TRUE if GetVariable() was called. + @param[in] Write TRUE if SetVariable() was called. + @param[in] Delete TRUE if deleted via SetVariable(). @param[in] Cache TRUE for a cache hit. **/ @@ -158,15 +64,15 @@ UpdateVariableInfo ( if (FeaturePcdGet (PcdVariableCollectStatistics)) { - if (EfiAtRuntime ()) { - // Don't collect statistics at runtime + if (AtRuntime ()) { + // Don't collect statistics at runtime. return; } if (gVariableInfo == NULL) { // - // on the first call allocate a entry and place a pointer to it in - // the EFI System Table + // On the first call allocate a entry and place a pointer to it in + // the EFI System Table. // gVariableInfo = AllocateZeroPool (sizeof (VARIABLE_INFO_ENTRY)); ASSERT (gVariableInfo != NULL); @@ -176,8 +82,6 @@ UpdateVariableInfo ( ASSERT (gVariableInfo->Name != NULL); StrCpy (gVariableInfo->Name, VariableName); gVariableInfo->Volatile = Volatile; - - gBS->InstallConfigurationTable (&gEfiVariableGuid, gVariableInfo); } @@ -204,7 +108,7 @@ UpdateVariableInfo ( if (Entry->Next == NULL) { // // If the entry is not in the table add it. - // Next iteration of the loop will fill in the data + // Next iteration of the loop will fill in the data. // Entry->Next = AllocateZeroPool (sizeof (VARIABLE_INFO_ENTRY)); ASSERT (Entry->Next != NULL); @@ -249,18 +153,18 @@ IsValidVariableHeader ( This function writes data to the FWH at the correct LBA even if the LBAs are fragmented. - @param Global Pointer to VARAIBLE_GLOBAL structure - @param Volatile Point out the Variable is Volatile or Non-Volatile - @param SetByIndex TRUE if target pointer is given as index - FALSE if target pointer is absolute - @param Fvb Pointer to the writable FVB protocol + @param Global Pointer to VARAIBLE_GLOBAL structure. + @param Volatile Point out the Variable is Volatile or Non-Volatile. + @param SetByIndex TRUE if target pointer is given as index. + FALSE if target pointer is absolute. + @param Fvb Pointer to the writable FVB protocol. @param DataPtrIndex Pointer to the Data from the end of VARIABLE_STORE_HEADER - structure - @param DataSize Size of data to be written - @param Buffer Pointer to the buffer from which data is written + structure. + @param DataSize Size of data to be written. + @param Buffer Pointer to the buffer from which data is written. - @retval EFI_INVALID_PARAMETER Parameters not valid - @retval EFI_SUCCESS Variable store successfully updated + @retval EFI_INVALID_PARAMETER Parameters not valid. + @retval EFI_SUCCESS Variable store successfully updated. **/ EFI_STATUS @@ -292,7 +196,7 @@ UpdateVariableStore ( DataPtr = DataPtrIndex; // - // Check if the Data is Volatile + // Check if the Data is Volatile. // if (!Volatile) { Status = Fvb->GetPhysicalAddress(Fvb, &FvVolHdr); @@ -301,7 +205,7 @@ UpdateVariableStore ( FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) ((UINTN) FvVolHdr); // // Data Pointer should point to the actual Address where data is to be - // written + // written. // if (SetByIndex) { DataPtr += mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase; @@ -313,7 +217,7 @@ UpdateVariableStore ( } else { // // Data Pointer should point to the actual Address where data is to be - // written + // written. // VolatileBase = (VARIABLE_STORE_HEADER *) ((UINTN) mVariableModuleGlobal->VariableGlobal.VolatileVariableBase); if (SetByIndex) { @@ -332,7 +236,7 @@ UpdateVariableStore ( } // - // If we are here we are dealing with Non-Volatile Variables + // If we are here we are dealing with Non-Volatile Variables. // LinearOffset = (UINTN) FwVolHeader; CurrWritePtr = (UINTN) DataPtr; @@ -394,9 +298,9 @@ UpdateVariableStore ( @param VarStoreHeader Pointer to the Variable Store Header. - @retval EfiRaw Variable store status is raw - @retval EfiValid Variable store status is valid - @retval EfiInvalid Variable store status is invalid + @retval EfiRaw Variable store status is raw. + @retval EfiValid Variable store status is valid. + @retval EfiInvalid Variable store status is invalid. **/ VARIABLE_STORE_STATUS @@ -430,9 +334,9 @@ GetVariableStoreStatus ( This code gets the size of name of variable. - @param Variable Pointer to the Variable Header + @param Variable Pointer to the Variable Header. - @return UINTN Size of variable in bytes + @return UINTN Size of variable in bytes. **/ UINTN @@ -453,9 +357,9 @@ NameSizeOfVariable ( This code gets the size of variable data. - @param Variable Pointer to the Variable Header + @param Variable Pointer to the Variable Header. - @return Size of variable in bytes + @return Size of variable in bytes. **/ UINTN @@ -476,9 +380,9 @@ DataSizeOfVariable ( This code gets the pointer to the variable name. - @param Variable Pointer to the Variable Header + @param Variable Pointer to the Variable Header. - @return Pointer to Variable Name which is Unicode encoding + @return Pointer to Variable Name which is Unicode encoding. **/ CHAR16 * @@ -494,9 +398,9 @@ GetVariableNamePtr ( This code gets the pointer to the variable data. - @param Variable Pointer to the Variable Header + @param Variable Pointer to the Variable Header. - @return Pointer to Variable Data + @return Pointer to Variable Data. **/ UINT8 * @@ -507,7 +411,7 @@ GetVariableDataPtr ( UINTN Value; // - // Be careful about pad size for alignment + // Be careful about pad size for alignment. // Value = (UINTN) GetVariableNamePtr (Variable); Value += NameSizeOfVariable (Variable); @@ -521,9 +425,9 @@ GetVariableDataPtr ( This code gets the pointer to the next variable header. - @param Variable Pointer to the Variable Header + @param Variable Pointer to the Variable Header. - @return Pointer to next variable header + @return Pointer to next variable header. **/ VARIABLE_HEADER * @@ -542,7 +446,7 @@ GetNextVariablePtr ( Value += GET_PAD_SIZE (DataSizeOfVariable (Variable)); // - // Be careful about pad size for alignment + // Be careful about pad size for alignment. // return (VARIABLE_HEADER *) HEADER_ALIGN (Value); } @@ -553,7 +457,7 @@ GetNextVariablePtr ( @param VarStoreHeader Pointer to the Variable Store Header. - @return Pointer to the first variable header + @return Pointer to the first variable header. **/ VARIABLE_HEADER * @@ -562,7 +466,7 @@ GetStartPointer ( ) { // - // The end of variable store + // The end of variable store. // return (VARIABLE_HEADER *) HEADER_ALIGN (VarStoreHeader + 1); } @@ -574,9 +478,9 @@ GetStartPointer ( This function gets pointer to the end of the variable storage area, according to the input variable store header. - @param VarStoreHeader Pointer to the Variable Store Header + @param VarStoreHeader Pointer to the Variable Store Header. - @return Pointer to the end of the variable storage area + @return Pointer to the end of the variable storage area. **/ VARIABLE_HEADER * @@ -595,11 +499,11 @@ GetEndPointer ( Variable store garbage collection and reclaim operation. - @param VariableBase Base address of variable store - @param LastVariableOffset Offset of last variable - @param IsVolatile The variable store is volatile or not, - if it is non-volatile, need FTW - @param UpdatingVariable Pointer to updateing variable. + @param VariableBase Base address of variable store. + @param LastVariableOffset Offset of last variable. + @param IsVolatile The variable store is volatile or not; + if it is non-volatile, need FTW. + @param UpdatingVariable Pointer to updating variable. @return EFI_OUT_OF_RESOURCES @return EFI_SUCCESS @@ -635,7 +539,7 @@ Reclaim ( VariableStoreHeader = (VARIABLE_STORE_HEADER *) ((UINTN) VariableBase); // - // recaluate the total size of Common/HwErr type variables in non-volatile area. + // Recalculate the total size of Common/HwErr type variables in non-volatile area. // if (!IsVolatile) { mVariableModuleGlobal->CommonVariableTotalSize = 0; @@ -673,13 +577,13 @@ Reclaim ( SetMem (ValidBuffer, MaximumBufferSize, 0xff); // - // Copy variable store header + // Copy variable store header. // CopyMem (ValidBuffer, VariableStoreHeader, sizeof (VARIABLE_STORE_HEADER)); CurrPtr = (UINT8 *) GetStartPointer ((VARIABLE_STORE_HEADER *) ValidBuffer); // - // Reinstall all ADDED variables as long as they are not identical to Updating Variable + // Reinstall all ADDED variables as long as they are not identical to Updating Variable. // Variable = GetStartPointer (VariableStoreHeader); while (IsValidVariableHeader (Variable)) { @@ -716,7 +620,7 @@ Reclaim ( } // - // Reinstall the variable being updated if it is not NULL + // Reinstall the variable being updated if it is not NULL. // if (UpdatingVariable != NULL) { VariableSize = (UINTN)(GetNextVariablePtr (UpdatingVariable)) - (UINTN)UpdatingVariable; @@ -730,7 +634,7 @@ Reclaim ( } // - // Reinstall all in delete transition variables + // Reinstall all in delete transition variables. // Variable = GetStartPointer (VariableStoreHeader); while (IsValidVariableHeader (Variable)) { @@ -762,7 +666,7 @@ Reclaim ( } if (!FoundAdded) { // - // Promote VAR_IN_DELETED_TRANSITION to VAR_ADDED + // Promote VAR_IN_DELETED_TRANSITION to VAR_ADDED. // VariableSize = (UINTN) NextVariable - (UINTN) Variable; CopyMem (CurrPtr, (UINT8 *) Variable, VariableSize); @@ -781,11 +685,11 @@ Reclaim ( if (IsVolatile) { // - // If volatile variable store, just copy valid buffer + // If volatile variable store, just copy valid buffer. // SetMem ((UINT8 *) (UINTN) VariableBase, VariableStoreHeader->Size, 0xff); CopyMem ((UINT8 *) (UINTN) VariableBase, ValidBuffer, (UINTN) (CurrPtr - (UINT8 *) ValidBuffer)); - Status = EFI_SUCCESS; + Status = EFI_SUCCESS; } else { // // If non-volatile variable store, perform FTW here. @@ -795,6 +699,7 @@ Reclaim ( ValidBuffer, (UINTN) (CurrPtr - (UINT8 *) ValidBuffer) ); + CopyMem (mNvVariableCache, (CHAR8 *)(UINTN)VariableBase, VariableStoreHeader->Size); } if (!EFI_ERROR (Status)) { *LastVariableOffset = (UINTN) (CurrPtr - (UINT8 *) ValidBuffer); @@ -808,126 +713,6 @@ Reclaim ( } -/** - Update the Cache with Variable information. These are the same - arguments as the EFI Variable services. - - @param[in] VariableName Name of variable - @param[in] VendorGuid Guid of variable - @param[in] Attributes Attribues of the variable - @param[in] DataSize Size of data. 0 means delete - @param[in] Data Variable data - -**/ -VOID -UpdateVariableCache ( - IN CHAR16 *VariableName, - IN EFI_GUID *VendorGuid, - IN UINT32 Attributes, - IN UINTN DataSize, - IN VOID *Data - ) -{ - VARIABLE_CACHE_ENTRY *Entry; - UINTN Index; - - if (EfiAtRuntime ()) { - // - // Don't use the cache at runtime - // - return; - } - - for (Index = 0, Entry = mVariableCache; Index < sizeof (mVariableCache)/sizeof (VARIABLE_CACHE_ENTRY); Index++, Entry++) { - if (CompareGuid (VendorGuid, Entry->Guid)) { - if (StrCmp (VariableName, Entry->Name) == 0) { - Entry->Attributes = Attributes; - if (DataSize == 0) { - // - // Delete Case - // - if (Entry->DataSize != 0) { - FreePool (Entry->Data); - } - Entry->DataSize = DataSize; - } else if (DataSize == Entry->DataSize) { - CopyMem (Entry->Data, Data, DataSize); - } else { - Entry->Data = AllocatePool (DataSize); - ASSERT (Entry->Data != NULL); - - Entry->DataSize = DataSize; - CopyMem (Entry->Data, Data, DataSize); - } - } - } - } -} - - -/** - Search the cache to check if the variable is in it. - - This function searches the variable cache. If the variable to find exists, return its data - and attributes. - - @param VariableName A Null-terminated Unicode string that is the name of the vendor's - variable. Each VariableName is unique for each - VendorGuid. - @param VendorGuid A unique identifier for the vendor - @param Attributes Pointer to the attributes bitmask of the variable for output. - @param DataSize On input, size of the buffer of Data. - On output, size of the variable's data. - @param Data Pointer to the data buffer for output. - - @retval EFI_SUCCESS VariableGuid & VariableName data was returned. - @retval EFI_NOT_FOUND No matching variable found in cache. - @retval EFI_BUFFER_TOO_SMALL *DataSize is smaller than size of the variable's data to return. - -**/ -EFI_STATUS -FindVariableInCache ( - IN CHAR16 *VariableName, - IN EFI_GUID *VendorGuid, - OUT UINT32 *Attributes OPTIONAL, - IN OUT UINTN *DataSize, - OUT VOID *Data - ) -{ - VARIABLE_CACHE_ENTRY *Entry; - UINTN Index; - - if (EfiAtRuntime ()) { - // Don't use the cache at runtime - return EFI_NOT_FOUND; - } - - for (Index = 0, Entry = mVariableCache; Index < sizeof (mVariableCache)/sizeof (VARIABLE_CACHE_ENTRY); Index++, Entry++) { - if (CompareGuid (VendorGuid, Entry->Guid)) { - if (StrCmp (VariableName, Entry->Name) == 0) { - if (Entry->DataSize == 0) { - // Variable was deleted so return not found - return EFI_NOT_FOUND; - } else if (Entry->DataSize > *DataSize) { - // If the buffer is too small return correct size - *DataSize = Entry->DataSize; - return EFI_BUFFER_TOO_SMALL; - } else { - *DataSize = Entry->DataSize; - // Return the data - CopyMem (Data, Entry->Data, Entry->DataSize); - if (Attributes != NULL) { - *Attributes = Entry->Attributes; - } - return EFI_SUCCESS; - } - } - } - } - - return EFI_NOT_FOUND; -} - /** Finds variable in storage blocks of volatile and non-volatile storage areas. @@ -936,7 +721,7 @@ FindVariableInCache ( qualified variable without comparing VariableName and VendorGuid. Otherwise, VariableName and VendorGuid are compared. - @param VariableName Name of the variable to be found + @param VariableName Name of the variable to be found. @param VendorGuid Vendor GUID to be found. @param PtrTrack VARIABLE_POINTER_TRACK structure for output, including the range searched and the target position. @@ -945,8 +730,8 @@ FindVariableInCache ( NV variable storage area, and a lock. @retval EFI_INVALID_PARAMETER If VariableName is not an empty string, while - VendorGuid is NULL - @retval EFI_SUCCESS Variable successfully found + VendorGuid is NULL. + @retval EFI_SUCCESS Variable successfully found. @retval EFI_NOT_FOUND Variable not found **/ @@ -966,12 +751,12 @@ FindVariable ( VOID *Point; // - // 0: Volatile, 1: Non-Volatile + // 0: Volatile, 1: Non-Volatile. // The index and attributes mapping must be kept in this order as RuntimeServiceGetNextVariableName - // make use of this mapping to implement search algorithme. + // make use of this mapping to implement search algorithm. // VariableStoreHeader[0] = (VARIABLE_STORE_HEADER *) ((UINTN) mVariableModuleGlobal->VariableGlobal.VolatileVariableBase); - VariableStoreHeader[1] = (VARIABLE_STORE_HEADER *) ((UINTN) mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase); + VariableStoreHeader[1] = mNvVariableCache; // // Start Pointers for the variable. @@ -985,7 +770,7 @@ FindVariable ( } // - // Find the variable by walk through volatile and then non-volatile variable store + // Find the variable by walk through volatile and then non-volatile variable store. // InDeletedVariable = NULL; InDeletedStorageIndex = 0; @@ -994,7 +779,7 @@ FindVariable ( if (Variable[Index]->State == VAR_ADDED || Variable[Index]->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED) ) { - if (!EfiAtRuntime () || ((Variable[Index]->Attributes & EFI_VARIABLE_RUNTIME_ACCESS) != 0)) { + if (!AtRuntime () || ((Variable[Index]->Attributes & EFI_VARIABLE_RUNTIME_ACCESS) != 0)) { if (VariableName[0] == 0) { if (Variable[Index]->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED)) { InDeletedVariable = Variable[Index]; @@ -1067,7 +852,7 @@ FindVariable ( @param Lang Configured language. @param Iso639Language A bool value to signify if the handler is operated on ISO639 or RFC4646. - @retval the index of language in the language codes. + @retval The index of language in the language codes. **/ UINTN @@ -1127,7 +912,7 @@ GetIndexFromSupportedLangCodes( /** Get language string from supported language codes according to index. - This code is used to get corresponding language string in supported language codes. It can handle + This code is used to get corresponding language strings in supported language codes. It can handle RFC4646 and ISO639 language tags. In ISO639 language tags, take 3-characters as a delimitation. Find language string according to the index. In RFC4646 language tags, take semicolon as a delimitation. Find language string according to the index. @@ -1144,10 +929,10 @@ GetIndexFromSupportedLangCodes( The return value is "fr". @param SupportedLang Platform supported language codes. - @param Index the index in supported language codes. + @param Index The index in supported language codes. @param Iso639Language A bool value to signify if the handler is operated on ISO639 or RFC4646. - @retval the language string in the language codes. + @retval The language string in the language codes. **/ CHAR8 * @@ -1165,18 +950,18 @@ GetLangFromSupportedLangCodes ( Supported = SupportedLang; if (Iso639Language) { // - // according to the index of Lang string in SupportedLang string to get the language. - // As this code will be invoked in RUNTIME, therefore there is not memory allocate/free operation. + // According to the index of Lang string in SupportedLang string to get the language. + // This code will be invoked in RUNTIME, therefore there is not a memory allocate/free operation. // In driver entry, it pre-allocates a runtime attribute memory to accommodate this string. // CompareLength = ISO_639_2_ENTRY_SIZE; mVariableModuleGlobal->Lang[CompareLength] = '\0'; return CopyMem (mVariableModuleGlobal->Lang, SupportedLang + Index * CompareLength, CompareLength); - + } else { while (TRUE) { // - // take semicolon as delimitation, sequentially traverse supported language codes. + // Take semicolon as delimitation, sequentially traverse supported language codes. // for (CompareLength = 0; *Supported != ';' && *Supported != '\0'; CompareLength++) { Supported++; @@ -1191,7 +976,7 @@ GetLangFromSupportedLangCodes ( } if (SubIndex == Index) { // - // according to the index of Lang string in SupportedLang string to get the language. + // According to the index of Lang string in SupportedLang string to get the language. // As this code will be invoked in RUNTIME, therefore there is not memory allocate/free operation. // In driver entry, it pre-allocates a runtime attribute memory to accommodate this string. // @@ -1199,7 +984,7 @@ GetLangFromSupportedLangCodes ( return CopyMem (mVariableModuleGlobal->PlatformLang, Supported - CompareLength, CompareLength); } SubIndex++; - + // // Skip ';' characters in Supported // @@ -1347,11 +1132,11 @@ VariableGetBestLanguage ( According to UEFI spec, PlatformLangCodes/LangCodes are only set once in firmware initialization, and are read-only. Therefore, in variable driver, only store the original value for other use. - @param[in] VariableName Name of variable + @param[in] VariableName Name of variable. - @param[in] Data Variable data + @param[in] Data Variable data. - @param[in] DataSize Size of data. 0 means delete + @param[in] DataSize Size of data. 0 means delete. **/ VOID @@ -1382,7 +1167,7 @@ AutoUpdateLangVariable( // // PlatformLangCodes is a volatile variable, so it can not be updated at runtime. // - if (EfiAtRuntime ()) { + if (AtRuntime ()) { return; } @@ -1412,7 +1197,7 @@ AutoUpdateLangVariable( // // LangCodes is a volatile variable, so it can not be updated at runtime. // - if (EfiAtRuntime ()) { + if (AtRuntime ()) { return; } @@ -1492,7 +1277,8 @@ AutoUpdateLangVariable( // FindVariable (L"Lang", &gEfiGlobalVariableGuid, &Variable, (VARIABLE_GLOBAL *)mVariableModuleGlobal); - Status = UpdateVariable (L"Lang", &gEfiGlobalVariableGuid, BestLang, ISO_639_2_ENTRY_SIZE + 1, Attributes, &Variable); + Status = UpdateVariable (L"Lang", &gEfiGlobalVariableGuid, BestLang, + ISO_639_2_ENTRY_SIZE + 1, Attributes, &Variable); DEBUG ((EFI_D_INFO, "Variable Driver Auto Update PlatformLang, PlatformLang:%a, Lang:%a\n", BestPlatformLang, BestLang)); @@ -1539,32 +1325,25 @@ AutoUpdateLangVariable( Update the variable region with Variable information. These are the same arguments as the EFI Variable services. - @param[in] VariableName Name of variable - - @param[in] VendorGuid Guid of variable - - @param[in] Data Variable data - - @param[in] DataSize Size of data. 0 means delete - - @param[in] Attributes Attribues of the variable - - @param[in] Variable The variable information which is used to keep track of variable usage. - + @param[in] VariableName Name of variable. + @param[in] VendorGuid Guid of variable. + @param[in] Data Variable data. + @param[in] DataSize Size of data. 0 means delete. + @param[in] Attributes Attribues of the variable. + @param[in] CacheVariable The variable information which is used to keep track of variable usage. + @retval EFI_SUCCESS The update operation is success. - @retval EFI_OUT_OF_RESOURCES Variable region is full, can not write other data into this region. **/ EFI_STATUS -EFIAPI UpdateVariable ( - IN CHAR16 *VariableName, - IN EFI_GUID *VendorGuid, - IN VOID *Data, - IN UINTN DataSize, - IN UINT32 Attributes OPTIONAL, - IN VARIABLE_POINTER_TRACK *Variable + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN VOID *Data, + IN UINTN DataSize, + IN UINT32 Attributes OPTIONAL, + IN VARIABLE_POINTER_TRACK *CacheVariable ) { EFI_STATUS Status; @@ -1579,19 +1358,47 @@ UpdateVariable ( EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb; UINT8 State; BOOLEAN Reclaimed; + VARIABLE_POINTER_TRACK *Variable; + VARIABLE_POINTER_TRACK NvVariable; + VARIABLE_STORE_HEADER *VariableStoreHeader; + UINTN CacheOffset; + if (CacheVariable->Volatile) { + Variable = CacheVariable; + } else { + if (mVariableModuleGlobal->FvbInstance == NULL) { + // + // Trying to update NV variable prior to the installation of EFI_VARIABLE_WRITE_ARCH_PROTOCOL + // + return EFI_NOT_AVAILABLE_YET; + } + + // + // CacheVariable points to the variable in the memory copy of Flash area + // Now let Variable points to the same variable in Flash area. + // + VariableStoreHeader = (VARIABLE_STORE_HEADER *) ((UINTN) mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase); + Variable = &NvVariable; + Variable->StartPtr = GetStartPointer (VariableStoreHeader); + Variable->EndPtr = GetEndPointer (VariableStoreHeader); + if (CacheVariable->CurrPtr == NULL) { + Variable->CurrPtr = NULL; + } else { + Variable->CurrPtr = (VARIABLE_HEADER *)((UINTN)Variable->StartPtr + ((UINTN)CacheVariable->CurrPtr - (UINTN)CacheVariable->StartPtr)); + } + Variable->Volatile = FALSE; + } + Fvb = mVariableModuleGlobal->FvbInstance; Reclaimed = FALSE; if (Variable->CurrPtr != NULL) { // - // Update/Delete existing variable + // Update/Delete existing variable. // - Volatile = Variable->Volatile; - - if (EfiAtRuntime ()) { + if (AtRuntime ()) { // - // If EfiAtRuntime and the variable is Volatile and Runtime Access, + // If AtRuntime and the variable is Volatile and Runtime Access, // the volatile is ReadOnly, and SetVariable should be aborted and // return EFI_WRITE_PROTECTED. // @@ -1600,16 +1407,17 @@ UpdateVariable ( goto Done; } // - // Only variable have NV attribute can be updated/deleted in Runtime + // Only variable that have NV attributes can be updated/deleted in Runtime. // if ((Variable->CurrPtr->Attributes & EFI_VARIABLE_NON_VOLATILE) == 0) { Status = EFI_INVALID_PARAMETER; goto Done; } } + // // Setting a data variable with no access, or zero DataSize attributes - // specified causes it to be deleted. + // causes it to be deleted. // if (DataSize == 0 || (Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == 0) { State = Variable->CurrPtr->State; @@ -1625,26 +1433,28 @@ UpdateVariable ( &State ); if (!EFI_ERROR (Status)) { - UpdateVariableInfo (VariableName, VendorGuid, Volatile, FALSE, FALSE, TRUE, FALSE); - UpdateVariableCache (VariableName, VendorGuid, Attributes, DataSize, Data); + UpdateVariableInfo (VariableName, VendorGuid, Variable->Volatile, FALSE, FALSE, TRUE, FALSE); + if (!Variable->Volatile) { + CacheVariable->CurrPtr->State = State; + } } goto Done; } // - // If the variable is marked valid and the same data has been passed in + // If the variable is marked valid, and the same data has been passed in, // then return to the caller immediately. // if (DataSizeOfVariable (Variable->CurrPtr) == DataSize && (CompareMem (Data, GetVariableDataPtr (Variable->CurrPtr), DataSize) == 0)) { - UpdateVariableInfo (VariableName, VendorGuid, Volatile, FALSE, TRUE, FALSE, FALSE); + UpdateVariableInfo (VariableName, VendorGuid, Variable->Volatile, FALSE, TRUE, FALSE, FALSE); Status = EFI_SUCCESS; goto Done; } else if ((Variable->CurrPtr->State == VAR_ADDED) || (Variable->CurrPtr->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION))) { // - // Mark the old variable as in delete transition + // Mark the old variable as in delete transition. // State = Variable->CurrPtr->State; State &= VAR_IN_DELETED_TRANSITION; @@ -1661,15 +1471,18 @@ UpdateVariable ( if (EFI_ERROR (Status)) { goto Done; } + if (!Variable->Volatile) { + CacheVariable->CurrPtr->State = State; + } } } else { // - // Not found existing variable. Create a new variable + // Not found existing variable. Create a new variable. // // // Make sure we are trying to create a new variable. - // Setting a data variable with no access, or zero DataSize attributes means to delete it. + // Setting a data variable with zero DataSize or no access attributes means to delete it. // if (DataSize == 0 || (Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == 0) { Status = EFI_NOT_FOUND; @@ -1677,9 +1490,9 @@ UpdateVariable ( } // - // Only variable have NV|RT attribute can be created in Runtime + // Only variable have NV|RT attribute can be created in Runtime. // - if (EfiAtRuntime () && + if (AtRuntime () && (((Attributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0) || ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0))) { Status = EFI_INVALID_PARAMETER; goto Done; @@ -1689,6 +1502,7 @@ UpdateVariable ( // // Function part - create a new variable and copy the data. // Both update a variable and create a variable will come here. + // // Tricky part: Use scratch data area at the end of volatile variable store // as a temporary storage. @@ -1703,9 +1517,9 @@ UpdateVariable ( // // NextVariable->State = VAR_ADDED; // - NextVariable->Reserved = 0; - VarNameOffset = sizeof (VARIABLE_HEADER); - VarNameSize = StrSize (VariableName); + NextVariable->Reserved = 0; + VarNameOffset = sizeof (VARIABLE_HEADER); + VarNameSize = StrSize (VariableName); CopyMem ( (UINT8 *) ((UINTN) NextVariable + VarNameOffset), VariableName, @@ -1721,7 +1535,7 @@ UpdateVariable ( // // There will be pad bytes after Data, the NextVariable->NameSize and // NextVariable->DataSize should not include pad size so that variable - // service can get actual size in GetVariable + // service can get actual size in GetVariable. // NextVariable->NameSize = (UINT32)VarNameSize; NextVariable->DataSize = (UINT32)DataSize; @@ -1733,7 +1547,7 @@ UpdateVariable ( VarSize = VarDataOffset + DataSize + GET_PAD_SIZE (DataSize); if ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) { // - // Create a nonvolatile variable + // Create a nonvolatile variable. // Volatile = FALSE; NonVolatileVarableStoreSize = ((VARIABLE_STORE_HEADER *)(UINTN)(mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase))->Size; @@ -1741,12 +1555,12 @@ UpdateVariable ( && ((VarSize + mVariableModuleGlobal->HwErrVariableTotalSize) > PcdGet32 (PcdHwErrStorageSize))) || (((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == 0) && ((VarSize + mVariableModuleGlobal->CommonVariableTotalSize) > NonVolatileVarableStoreSize - sizeof (VARIABLE_STORE_HEADER) - PcdGet32 (PcdHwErrStorageSize)))) { - if (EfiAtRuntime ()) { + if (AtRuntime ()) { Status = EFI_OUT_OF_RESOURCES; goto Done; } // - // Perform garbage collection & reclaim operation + // Perform garbage collection & reclaim operation. // Status = Reclaim (mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase, &mVariableModuleGlobal->NonVolatileLastVariableOffset, FALSE, Variable->CurrPtr); @@ -1754,7 +1568,7 @@ UpdateVariable ( goto Done; } // - // If still no enough space, return out of resources + // If still no enough space, return out of resources. // if ((((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != 0) && ((VarSize + mVariableModuleGlobal->HwErrVariableTotalSize) > PcdGet32 (PcdHwErrStorageSize))) @@ -1766,7 +1580,7 @@ UpdateVariable ( Reclaimed = TRUE; } // - // Three steps + // Four steps // 1. Write variable header // 2. Set variable state to header valid // 3. Write variable data @@ -1775,6 +1589,7 @@ UpdateVariable ( // // Step 1: // + CacheOffset = mVariableModuleGlobal->NonVolatileLastVariableOffset; Status = UpdateVariableStore ( &mVariableModuleGlobal->VariableGlobal, FALSE, @@ -1798,9 +1613,9 @@ UpdateVariable ( FALSE, TRUE, Fvb, - mVariableModuleGlobal->NonVolatileLastVariableOffset, - sizeof (VARIABLE_HEADER), - (UINT8 *) NextVariable + mVariableModuleGlobal->NonVolatileLastVariableOffset + OFFSET_OF (VARIABLE_HEADER, State), + sizeof (UINT8), + &NextVariable->State ); if (EFI_ERROR (Status)) { @@ -1831,9 +1646,9 @@ UpdateVariable ( FALSE, TRUE, Fvb, - mVariableModuleGlobal->NonVolatileLastVariableOffset, - sizeof (VARIABLE_HEADER), - (UINT8 *) NextVariable + mVariableModuleGlobal->NonVolatileLastVariableOffset + OFFSET_OF (VARIABLE_HEADER, State), + sizeof (UINT8), + &NextVariable->State ); if (EFI_ERROR (Status)) { @@ -1847,16 +1662,20 @@ UpdateVariable ( } else { mVariableModuleGlobal->CommonVariableTotalSize += HEADER_ALIGN (VarSize); } + // + // update the memory copy of Flash region. + // + CopyMem ((UINT8 *)mNvVariableCache + CacheOffset, (UINT8 *)NextVariable, VarSize); } else { // - // Create a volatile variable + // Create a volatile variable. // Volatile = TRUE; if ((UINT32) (VarSize + mVariableModuleGlobal->VolatileLastVariableOffset) > ((VARIABLE_STORE_HEADER *) ((UINTN) (mVariableModuleGlobal->VariableGlobal.VolatileVariableBase)))->Size) { // - // Perform garbage collection & reclaim operation + // Perform garbage collection & reclaim operation. // Status = Reclaim (mVariableModuleGlobal->VariableGlobal.VolatileVariableBase, &mVariableModuleGlobal->VolatileLastVariableOffset, TRUE, Variable->CurrPtr); @@ -1864,7 +1683,7 @@ UpdateVariable ( goto Done; } // - // If still no enough space, return out of resources + // If still no enough space, return out of resources. // if ((UINT32) (VarSize + mVariableModuleGlobal->VolatileLastVariableOffset) > ((VARIABLE_STORE_HEADER *) ((UINTN) (mVariableModuleGlobal->VariableGlobal.VolatileVariableBase)))->Size @@ -1894,7 +1713,7 @@ UpdateVariable ( } // - // Mark the old variable as deleted + // Mark the old variable as deleted. // if (!Reclaimed && !EFI_ERROR (Status) && Variable->CurrPtr != NULL) { State = Variable->CurrPtr->State; @@ -1909,11 +1728,13 @@ UpdateVariable ( sizeof (UINT8), &State ); + if (!EFI_ERROR (Status) && !Variable->Volatile) { + CacheVariable->CurrPtr->State = State; + } } if (!EFI_ERROR (Status)) { UpdateVariableInfo (VariableName, VendorGuid, Volatile, FALSE, TRUE, FALSE, FALSE); - UpdateVariableCache (VariableName, VendorGuid, Attributes, DataSize, Data); } Done: @@ -1931,15 +1752,15 @@ Done: data, this value contains the required size. @param Data Data pointer. - @return EFI_INVALID_PARAMETER Invalid parameter - @return EFI_SUCCESS Find the specified variable - @return EFI_NOT_FOUND Not found - @return EFI_BUFFER_TO_SMALL DataSize is too small for the result + @return EFI_INVALID_PARAMETER Invalid parameter. + @return EFI_SUCCESS Find the specified variable. + @return EFI_NOT_FOUND Not found. + @return EFI_BUFFER_TO_SMALL DataSize is too small for the result. **/ EFI_STATUS EFIAPI -RuntimeServiceGetVariable ( +VariableServiceGetVariable ( IN CHAR16 *VariableName, IN EFI_GUID *VendorGuid, OUT UINT32 *Attributes OPTIONAL, @@ -1956,16 +1777,6 @@ RuntimeServiceGetVariable ( } AcquireLockOnlyAtBootTime(&mVariableModuleGlobal->VariableGlobal.VariableServicesLock); - - // - // Find existing variable - // - Status = FindVariableInCache (VariableName, VendorGuid, Attributes, DataSize, Data); - if ((Status == EFI_BUFFER_TOO_SMALL) || (Status == EFI_SUCCESS)){ - // Hit in the Cache - UpdateVariableInfo (VariableName, VendorGuid, FALSE, TRUE, FALSE, FALSE, TRUE); - goto Done; - } Status = FindVariable (VariableName, VendorGuid, &Variable, &mVariableModuleGlobal->VariableGlobal); if (Variable.CurrPtr == NULL || EFI_ERROR (Status)) { @@ -1991,7 +1802,6 @@ RuntimeServiceGetVariable ( *DataSize = VarDataSize; UpdateVariableInfo (VariableName, VendorGuid, Variable.Volatile, TRUE, FALSE, FALSE, FALSE); - UpdateVariableCache (VariableName, VendorGuid, Variable.CurrPtr->Attributes, VarDataSize, Data); Status = EFI_SUCCESS; goto Done; @@ -2012,19 +1822,19 @@ Done: This code Finds the Next available variable. - @param VariableNameSize Size of the variable name - @param VariableName Pointer to variable name - @param VendorGuid Variable Vendor Guid + @param VariableNameSize Size of the variable name. + @param VariableName Pointer to variable name. + @param VendorGuid Variable Vendor Guid. - @return EFI_INVALID_PARAMETER Invalid parameter - @return EFI_SUCCESS Find the specified variable - @return EFI_NOT_FOUND Not found - @return EFI_BUFFER_TO_SMALL DataSize is too small for the result + @return EFI_INVALID_PARAMETER Invalid parameter. + @return EFI_SUCCESS Find the specified variable. + @return EFI_NOT_FOUND Not found. + @return EFI_BUFFER_TO_SMALL DataSize is too small for the result. **/ EFI_STATUS EFIAPI -RuntimeServiceGetNextVariableName ( +VariableServiceGetNextVariableName ( IN OUT UINTN *VariableNameSize, IN OUT CHAR16 *VariableName, IN OUT EFI_GUID *VendorGuid @@ -2047,7 +1857,7 @@ RuntimeServiceGetNextVariableName ( if (VariableName[0] != 0) { // - // If variable name is not NULL, get next variable + // If variable name is not NULL, get next variable. // Variable.CurrPtr = GetNextVariablePtr (Variable.CurrPtr); } @@ -2055,7 +1865,7 @@ RuntimeServiceGetNextVariableName ( while (TRUE) { // // If both volatile and non-volatile variable store are parsed, - // return not found + // return not found. // if (Variable.CurrPtr >= Variable.EndPtr || Variable.CurrPtr == NULL) { Variable.Volatile = (BOOLEAN) (Variable.Volatile ^ ((BOOLEAN) 0x1)); @@ -2076,7 +1886,7 @@ RuntimeServiceGetNextVariableName ( // Variable is found // if (IsValidVariableHeader (Variable.CurrPtr) && Variable.CurrPtr->State == VAR_ADDED) { - if ((EfiAtRuntime () && ((Variable.CurrPtr->Attributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0)) == 0) { + if ((AtRuntime () && ((Variable.CurrPtr->Attributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0)) == 0) { VarNameSize = NameSizeOfVariable (Variable.CurrPtr); ASSERT (VarNameSize != 0); @@ -2113,23 +1923,23 @@ Done: This code sets variable in storage blocks (Volatile or Non-Volatile). - @param VariableName Name of Variable to be found - @param VendorGuid Variable vendor GUID + @param VariableName Name of Variable to be found. + @param VendorGuid Variable vendor GUID. @param Attributes Attribute value of the variable found @param DataSize Size of Data found. If size is less than the data, this value contains the required size. - @param Data Data pointer + @param Data Data pointer. - @return EFI_INVALID_PARAMETER Invalid parameter - @return EFI_SUCCESS Set successfully - @return EFI_OUT_OF_RESOURCES Resource not enough to set variable - @return EFI_NOT_FOUND Not found - @return EFI_WRITE_PROTECTED Variable is read-only + @return EFI_INVALID_PARAMETER Invalid parameter. + @return EFI_SUCCESS Set successfully. + @return EFI_OUT_OF_RESOURCES Resource not enough to set variable. + @return EFI_NOT_FOUND Not found. + @return EFI_WRITE_PROTECTED Variable is read-only. **/ EFI_STATUS EFIAPI -RuntimeServiceSetVariable ( +VariableServiceSetVariable ( IN CHAR16 *VariableName, IN EFI_GUID *VendorGuid, IN UINT32 Attributes, @@ -2143,11 +1953,11 @@ RuntimeServiceSetVariable ( EFI_PHYSICAL_ADDRESS Point; // - // Check input parameters + // Check input parameters. // if (VariableName == NULL || VariableName[0] == 0 || VendorGuid == NULL) { return EFI_INVALID_PARAMETER; - } + } if (DataSize != 0 && Data == NULL) { return EFI_INVALID_PARAMETER; @@ -2161,7 +1971,7 @@ RuntimeServiceSetVariable ( } // - // Make sure if runtime bit is set, boot service bit is set also + // Make sure if runtime bit is set, boot service bit is set also. // if ((Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == EFI_VARIABLE_RUNTIME_ACCESS) { return EFI_INVALID_PARAMETER; @@ -2178,7 +1988,7 @@ RuntimeServiceSetVariable ( return EFI_INVALID_PARAMETER; } // - // According to UEFI spec, HARDWARE_ERROR_RECORD variable name convention should be L"HwErrRecXXXX" + // According to UEFI spec, HARDWARE_ERROR_RECORD variable name convention should be L"HwErrRecXXXX". // if (StrnCmp(VariableName, L"HwErrRec", StrLen(L"HwErrRec")) != 0) { return EFI_INVALID_PARAMETER; @@ -2197,12 +2007,12 @@ RuntimeServiceSetVariable ( AcquireLockOnlyAtBootTime(&mVariableModuleGlobal->VariableGlobal.VariableServicesLock); // - // Consider reentrant in MCA/INIT/NMI. It needs be reupdated; + // Consider reentrant in MCA/INIT/NMI. It needs be reupdated. // if (1 < InterlockedIncrement (&mVariableModuleGlobal->VariableGlobal.ReentrantState)) { - Point = mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase;; + Point = mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase; // - // Parse non-volatile variable data and get last variable offset + // Parse non-volatile variable data and get last variable offset. // NextVariable = GetStartPointer ((VARIABLE_STORE_HEADER *) (UINTN) Point); while ((NextVariable < GetEndPointer ((VARIABLE_STORE_HEADER *) (UINTN) Point)) @@ -2213,12 +2023,12 @@ RuntimeServiceSetVariable ( } // - // Check whether the input variable is already existed + // Check whether the input variable is already existed. // FindVariable (VariableName, VendorGuid, &Variable, &mVariableModuleGlobal->VariableGlobal); // - // Hook the operation of setting PlatformLangCodes/PlatformLang and LangCodes/Lang + // Hook the operation of setting PlatformLangCodes/PlatformLang and LangCodes/Lang. // AutoUpdateLangVariable (VariableName, Data, DataSize); @@ -2250,7 +2060,7 @@ RuntimeServiceSetVariable ( **/ EFI_STATUS EFIAPI -RuntimeServiceQueryVariableInfo ( +VariableServiceQueryVariableInfo ( IN UINT32 Attributes, OUT UINT64 *MaximumVariableStorageSize, OUT UINT64 *RemainingVariableStorageSize, @@ -2281,7 +2091,7 @@ RuntimeServiceQueryVariableInfo ( // Make sure if runtime bit is set, boot service bit is set also. // return EFI_INVALID_PARAMETER; - } else if (EfiAtRuntime () && ((Attributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0)) { + } else if (AtRuntime () && ((Attributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0)) { // // Make sure RT Attribute is set if we are in Runtime phase. // @@ -2309,7 +2119,7 @@ RuntimeServiceQueryVariableInfo ( // // Query is Non-Volatile related. // - VariableStoreHeader = (VARIABLE_STORE_HEADER *) ((UINTN) mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase); + VariableStoreHeader = mNvVariableCache; } // @@ -2348,9 +2158,9 @@ RuntimeServiceQueryVariableInfo ( NextVariable = GetNextVariablePtr (Variable); VariableSize = (UINT64) (UINTN) NextVariable - (UINT64) (UINTN) Variable; - if (EfiAtRuntime ()) { + if (AtRuntime ()) { // - // we don't take the state of the variables in mind + // We don't take the state of the variables in mind // when calculating RemainingVariableStorageSize, // since the space occupied by variables not marked with // VAR_ADDED is not allowed to be reclaimed in Runtime. @@ -2362,7 +2172,7 @@ RuntimeServiceQueryVariableInfo ( } } else { // - // Only care about Variables with State VAR_ADDED,because + // Only care about Variables with State VAR_ADDED, because // the space not marked as VAR_ADDED is reclaimable now. // if (Variable->State == VAR_ADDED) { @@ -2375,7 +2185,7 @@ RuntimeServiceQueryVariableInfo ( } // - // Go to the next one + // Go to the next one. // Variable = NextVariable; } @@ -2398,21 +2208,12 @@ RuntimeServiceQueryVariableInfo ( /** - Notification function of EVT_GROUP_READY_TO_BOOT event group. - - This is a notification function registered on EVT_GROUP_READY_TO_BOOT event group. - When the Boot Manager is about to load and execute a boot option, it reclaims variable - storage if free size is below the threshold. - - @param Event Event whose notification function is being invoked - @param Context Pointer to the notification function's context - + This function reclaims variable storage if free size is below the threshold. + **/ VOID -EFIAPI ReclaimForOS( - EFI_EVENT Event, - VOID *Context + VOID ) { EFI_STATUS Status; @@ -2443,10 +2244,81 @@ ReclaimForOS( } } + /** - Initializes variable store area for non-volatile and volatile variable. + Initializes variable write service after FVB was ready. - @param FvbProtocol Pointer to an instance of EFI Firmware Volume Block Protocol. + @retval EFI_SUCCESS Function successfully executed. + @retval Others Fail to initialize the variable service. + +**/ +EFI_STATUS +VariableWriteServiceInitialize ( + VOID + ) +{ + EFI_STATUS Status; + VARIABLE_STORE_HEADER *VariableStoreHeader; + UINTN Index; + UINT8 Data; + EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor; + EFI_PHYSICAL_ADDRESS BaseAddress; + UINT64 Length; + EFI_PHYSICAL_ADDRESS VariableStoreBase; + UINT64 VariableStoreLength; + + VariableStoreBase = mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase; + VariableStoreHeader = (VARIABLE_STORE_HEADER *)(UINTN)VariableStoreBase; + VariableStoreLength = VariableStoreHeader->Size; + + // + // Check if the free area is really free. + // + for (Index = mVariableModuleGlobal->NonVolatileLastVariableOffset; Index < VariableStoreLength; Index++) { + Data = ((UINT8 *) mNvVariableCache)[Index]; + if (Data != 0xff) { + // + // There must be something wrong in variable store, do reclaim operation. + // + Status = Reclaim ( + mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase, + &mVariableModuleGlobal->NonVolatileLastVariableOffset, + FALSE, + NULL + ); + if (EFI_ERROR (Status)) { + return Status; + } + break; + } + } + + // + // Mark the variable storage region of the FLASH as RUNTIME. + // + BaseAddress = VariableStoreBase & (~EFI_PAGE_MASK); + Length = VariableStoreLength + (VariableStoreBase - BaseAddress); + Length = (Length + EFI_PAGE_SIZE - 1) & (~EFI_PAGE_MASK); + + Status = gDS->GetMemorySpaceDescriptor (BaseAddress, &GcdDescriptor); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "Variable driver failed to add EFI_MEMORY_RUNTIME attribute to Flash.\n")); + } else { + Status = gDS->SetMemorySpaceAttributes ( + BaseAddress, + Length, + GcdDescriptor.Attributes | EFI_MEMORY_RUNTIME + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "Variable driver failed to add EFI_MEMORY_RUNTIME attribute to Flash.\n")); + } + } + return EFI_SUCCESS; +} + + +/** + Initializes variable store area for non-volatile and volatile variable. @retval EFI_SUCCESS Function successfully executed. @retval EFI_OUT_OF_RESOURCES Fail to allocate enough memory resource. @@ -2454,7 +2326,7 @@ ReclaimForOS( **/ EFI_STATUS VariableCommonInitialize ( - IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvbProtocol + VOID ) { EFI_STATUS Status; @@ -2462,18 +2334,11 @@ VariableCommonInitialize ( VARIABLE_STORE_HEADER *VariableStoreHeader; VARIABLE_HEADER *NextVariable; EFI_PHYSICAL_ADDRESS TempVariableStoreHeader; - EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor; - EFI_PHYSICAL_ADDRESS BaseAddress; - UINT64 Length; - UINTN Index; - UINT8 Data; EFI_PHYSICAL_ADDRESS VariableStoreBase; UINT64 VariableStoreLength; - EFI_EVENT ReadyToBootEvent; UINTN ScratchSize; UINTN VariableSize; - Status = EFI_SUCCESS; // // Allocate runtime memory for variable driver global structure. // @@ -2482,7 +2347,7 @@ VariableCommonInitialize ( return EFI_OUT_OF_RESOURCES; } - EfiInitializeLock(&mVariableModuleGlobal->VariableGlobal.VariableServicesLock, TPL_NOTIFY); + InitializeLock (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock, TPL_NOTIFY); // // Note that in EdkII variable driver implementation, Hardware Error Record type variable @@ -2505,139 +2370,69 @@ VariableCommonInitialize ( SetMem (VolatileVariableStore, PcdGet32 (PcdVariableStoreSize) + ScratchSize, 0xff); // - // Variable Specific Data + // Initialize Variable Specific Data. // mVariableModuleGlobal->VariableGlobal.VolatileVariableBase = (EFI_PHYSICAL_ADDRESS) (UINTN) VolatileVariableStore; mVariableModuleGlobal->VolatileLastVariableOffset = (UINTN) GetStartPointer (VolatileVariableStore) - (UINTN) VolatileVariableStore; - mVariableModuleGlobal->FvbInstance = FvbProtocol; + mVariableModuleGlobal->FvbInstance = NULL; CopyGuid (&VolatileVariableStore->Signature, &gEfiVariableGuid); - VolatileVariableStore->Size = PcdGet32 (PcdVariableStoreSize); - VolatileVariableStore->Format = VARIABLE_STORE_FORMATTED; - VolatileVariableStore->State = VARIABLE_STORE_HEALTHY; - VolatileVariableStore->Reserved = 0; - VolatileVariableStore->Reserved1 = 0; + VolatileVariableStore->Size = PcdGet32 (PcdVariableStoreSize); + VolatileVariableStore->Format = VARIABLE_STORE_FORMATTED; + VolatileVariableStore->State = VARIABLE_STORE_HEALTHY; + VolatileVariableStore->Reserved = 0; + VolatileVariableStore->Reserved1 = 0; // - // Get non volatile varaible store + // Get non-volatile varaible store. // TempVariableStoreHeader = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageVariableBase64); if (TempVariableStoreHeader == 0) { TempVariableStoreHeader = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageVariableBase); } - - VariableStoreBase = TempVariableStoreHeader + \ + VariableStoreBase = TempVariableStoreHeader + \ + (((EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)(TempVariableStoreHeader)) -> HeaderLength); + VariableStoreLength = (UINT64) PcdGet32 (PcdFlashNvStorageVariableSize) - \ (((EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)(TempVariableStoreHeader)) -> HeaderLength); - VariableStoreLength = (UINT64) PcdGet32 (PcdFlashNvStorageVariableSize) - \ - (((EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)(TempVariableStoreHeader)) -> HeaderLength); - // - // Mark the variable storage region of the FLASH as RUNTIME - // - BaseAddress = VariableStoreBase & (~EFI_PAGE_MASK); - Length = VariableStoreLength + (VariableStoreBase - BaseAddress); - Length = (Length + EFI_PAGE_SIZE - 1) & (~EFI_PAGE_MASK); - - Status = gDS->GetMemorySpaceDescriptor (BaseAddress, &GcdDescriptor); - if (EFI_ERROR (Status)) { - goto Done; - } - Status = gDS->SetMemorySpaceAttributes ( - BaseAddress, - Length, - GcdDescriptor.Attributes | EFI_MEMORY_RUNTIME - ); - if (EFI_ERROR (Status)) { + mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase = VariableStoreBase; + VariableStoreHeader = (VARIABLE_STORE_HEADER *)(UINTN)VariableStoreBase; + if (GetVariableStoreStatus (VariableStoreHeader) != EfiValid) { + Status = EFI_VOLUME_CORRUPTED; + DEBUG((EFI_D_INFO, "Variable Store header is corrupted\n")); goto Done; - } + } + ASSERT(VariableStoreHeader->Size == VariableStoreLength); + // - // Get address of non volatile variable store base + // Parse non-volatile variable data and get last variable offset. // - mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase = VariableStoreBase; - VariableStoreHeader = (VARIABLE_STORE_HEADER *)(UINTN)VariableStoreBase; - if (GetVariableStoreStatus (VariableStoreHeader) == EfiValid) { - if (~VariableStoreHeader->Size == 0) { - Status = UpdateVariableStore ( - &mVariableModuleGlobal->VariableGlobal, - FALSE, - FALSE, - mVariableModuleGlobal->FvbInstance, - (UINTN) &VariableStoreHeader->Size, - sizeof (UINT32), - (UINT8 *) &VariableStoreLength - ); - // - // As Variables are stored in NV storage, which are slow devices,such as flash. - // Variable operation may skip checking variable program result to improve performance, - // We can assume Variable program is OK through some check point. - // Variable Store Size Setting should be the first Variable write operation, - // We can assume all Read/Write is OK if we can set Variable store size successfully. - // If write fail, we will assert here - // - ASSERT(VariableStoreHeader->Size == VariableStoreLength); - - if (EFI_ERROR (Status)) { - goto Done; - } - } - - // - // Parse non-volatile variable data and get last variable offset - // - NextVariable = GetStartPointer ((VARIABLE_STORE_HEADER *)(UINTN)VariableStoreBase); - Status = EFI_SUCCESS; - - while (IsValidVariableHeader (NextVariable)) { - VariableSize = NextVariable->NameSize + NextVariable->DataSize + sizeof (VARIABLE_HEADER); - if ((NextVariable->Attributes & (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_HARDWARE_ERROR_RECORD)) == (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_HARDWARE_ERROR_RECORD)) { - mVariableModuleGlobal->HwErrVariableTotalSize += HEADER_ALIGN (VariableSize); - } else { - mVariableModuleGlobal->CommonVariableTotalSize += HEADER_ALIGN (VariableSize); - } - - NextVariable = GetNextVariablePtr (NextVariable); + NextVariable = GetStartPointer ((VARIABLE_STORE_HEADER *)(UINTN)VariableStoreBase); + while (IsValidVariableHeader (NextVariable)) { + VariableSize = NextVariable->NameSize + NextVariable->DataSize + sizeof (VARIABLE_HEADER); + if ((NextVariable->Attributes & (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_HARDWARE_ERROR_RECORD)) == (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_HARDWARE_ERROR_RECORD)) { + mVariableModuleGlobal->HwErrVariableTotalSize += HEADER_ALIGN (VariableSize); + } else { + mVariableModuleGlobal->CommonVariableTotalSize += HEADER_ALIGN (VariableSize); } - mVariableModuleGlobal->NonVolatileLastVariableOffset = (UINTN) NextVariable - (UINTN) VariableStoreBase; - - // - // Check if the free area is really free. - // - for (Index = mVariableModuleGlobal->NonVolatileLastVariableOffset; Index < VariableStoreHeader->Size; Index++) { - Data = ((UINT8 *) (UINTN) mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase)[Index]; - if (Data != 0xff) { - // - // There must be something wrong in variable store, do reclaim operation. - // - Status = Reclaim ( - mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase, - &mVariableModuleGlobal->NonVolatileLastVariableOffset, - FALSE, - NULL - ); - - if (EFI_ERROR (Status)) { - goto Done; - } - - break; - } - } + NextVariable = GetNextVariablePtr (NextVariable); + } - // - // Register the event handling function to reclaim variable for OS usage. - // - Status = EfiCreateEventReadyToBootEx ( - TPL_NOTIFY, - ReclaimForOS, - NULL, - &ReadyToBootEvent - ); - } else { - Status = EFI_VOLUME_CORRUPTED; - DEBUG((EFI_D_INFO, "Variable Store header is corrupted\n")); + mVariableModuleGlobal->NonVolatileLastVariableOffset = (UINTN) NextVariable - (UINTN) VariableStoreBase; + + // + // Allocate runtime memory used for a memory copy of the FLASH region. + // Keep the memory and the FLASH in sync as updates occur + // + mNvVariableCache = AllocateRuntimeZeroPool ((UINTN)VariableStoreLength); + if (mNvVariableCache == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; } + CopyMem (mNvVariableCache, (CHAR8 *)(UINTN)VariableStoreBase, (UINTN)VariableStoreLength); + Status = EFI_SUCCESS; Done: if (EFI_ERROR (Status)) { @@ -2648,97 +2443,45 @@ Done: return Status; } -/** - Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE - - This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event. - It convers pointer to new virtual address. - - @param Event Event whose notification function is being invoked - @param Context Pointer to the notification function's context - -**/ -VOID -EFIAPI -VariableClassAddressChangeEvent ( - IN EFI_EVENT Event, - IN VOID *Context - ) -{ - EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->GetBlockSize); - EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->GetPhysicalAddress); - EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->GetAttributes); - EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->SetAttributes); - EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->Read); - EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->Write); - EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->EraseBlocks); - EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance); - EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->PlatformLangCodes); - EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->LangCodes); - EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->PlatformLang); - EfiConvertPointer ( - 0x0, - (VOID **) &mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase - ); - EfiConvertPointer ( - 0x0, - (VOID **) &mVariableModuleGlobal->VariableGlobal.VolatileVariableBase - ); - EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal); -} /** - Firmware Volume Block Protocol notification event handler. + Get the proper fvb handle and/or fvb protocol by the given Flash address. - Discover NV Variable Store and install Variable Arch Protocol. + @param[in] Address The Flash address. + @param[out] FvbHandle In output, if it is not NULL, it points to the proper FVB handle. + @param[out] FvbProtocol In output, if it is not NULL, it points to the proper FVB protocol. - @param[in] Event Event whose notification function is being invoked. - @param[in] Context Pointer to the notification function's context. **/ -VOID -EFIAPI -FvbNotificationEvent ( - IN EFI_EVENT Event, - IN VOID *Context +EFI_STATUS +GetFvbInfoByAddress ( + IN EFI_PHYSICAL_ADDRESS Address, + OUT EFI_HANDLE *FvbHandle OPTIONAL, + OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvbProtocol OPTIONAL ) { - EFI_STATUS Status; - EFI_HANDLE *HandleBuffer; - UINTN HandleCount; - UINTN Index; - EFI_PHYSICAL_ADDRESS FvbBaseAddress; - EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb; - EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader; - EFI_FVB_ATTRIBUTES_2 Attributes; - EFI_SYSTEM_TABLE *SystemTable; - EFI_PHYSICAL_ADDRESS NvStorageVariableBase; - - SystemTable = (EFI_SYSTEM_TABLE *)Context; - Fvb = NULL; - + EFI_STATUS Status; + EFI_HANDLE *HandleBuffer; + UINTN HandleCount; + UINTN Index; + EFI_PHYSICAL_ADDRESS FvbBaseAddress; + EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb; + EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader; + EFI_FVB_ATTRIBUTES_2 Attributes; + // - // Locate all handles of Fvb protocol + // Get all FVB handles. // - Status = gBS->LocateHandleBuffer ( - ByProtocol, - &gEfiFirmwareVolumeBlockProtocolGuid, - NULL, - &HandleCount, - &HandleBuffer - ); + Status = GetFvbCountAndBuffer (&HandleCount, &HandleBuffer); if (EFI_ERROR (Status)) { - return ; + return EFI_NOT_FOUND; } - + // - // Get the FVB to access variable store + // Get the FVB to access variable store. // + Fvb = NULL; for (Index = 0; Index < HandleCount; Index += 1, Status = EFI_NOT_FOUND, Fvb = NULL) { - Status = gBS->HandleProtocol ( - HandleBuffer[Index], - &gEfiFirmwareVolumeBlockProtocolGuid, - (VOID **) &Fvb - ); + Status = GetFvbByHandle (HandleBuffer[Index], &Fvb); if (EFI_ERROR (Status)) { Status = EFI_NOT_FOUND; break; @@ -2751,8 +2494,9 @@ FvbNotificationEvent ( if (EFI_ERROR (Status) || ((Attributes & EFI_FVB2_WRITE_STATUS) == 0)) { continue; } + // - // Compare the address and select the right one + // Compare the address and select the right one. // Status = Fvb->GetPhysicalAddress (Fvb, &FvbBaseAddress); if (EFI_ERROR (Status)) { @@ -2760,87 +2504,23 @@ FvbNotificationEvent ( } FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) ((UINTN) FvbBaseAddress); - NvStorageVariableBase = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageVariableBase64); - if (NvStorageVariableBase == 0) { - NvStorageVariableBase = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageVariableBase); - } - - if ((NvStorageVariableBase >= FvbBaseAddress) && (NvStorageVariableBase < (FvbBaseAddress + FwVolHeader->FvLength))) { - Status = EFI_SUCCESS; + if ((Address >= FvbBaseAddress) && (Address < (FvbBaseAddress + FwVolHeader->FvLength))) { + if (FvbHandle != NULL) { + *FvbHandle = HandleBuffer[Index]; + } + if (FvbProtocol != NULL) { + *FvbProtocol = Fvb; + } + Status = EFI_SUCCESS; break; } } - FreePool (HandleBuffer); - if (!EFI_ERROR (Status) && Fvb != NULL) { - // - // Close the notify event to avoid install gEfiVariableArchProtocolGuid & gEfiVariableWriteArchProtocolGuid again. - // - Status = gBS->CloseEvent (Event); - ASSERT_EFI_ERROR (Status); - Status = VariableCommonInitialize (Fvb); - ASSERT_EFI_ERROR (Status); - - SystemTable->RuntimeServices->GetVariable = RuntimeServiceGetVariable; - SystemTable->RuntimeServices->GetNextVariableName = RuntimeServiceGetNextVariableName; - SystemTable->RuntimeServices->SetVariable = RuntimeServiceSetVariable; - SystemTable->RuntimeServices->QueryVariableInfo = RuntimeServiceQueryVariableInfo; - - // - // Now install the Variable Runtime Architectural Protocol on a new handle - // - Status = gBS->InstallMultipleProtocolInterfaces ( - &mHandle, - &gEfiVariableArchProtocolGuid, NULL, - &gEfiVariableWriteArchProtocolGuid, NULL, - NULL - ); - ASSERT_EFI_ERROR (Status); - - Status = gBS->CreateEventEx ( - EVT_NOTIFY_SIGNAL, - TPL_NOTIFY, - VariableClassAddressChangeEvent, - NULL, - &gEfiEventVirtualAddressChangeGuid, - &mVirtualAddressChangeEvent - ); - ASSERT_EFI_ERROR (Status); + if (Fvb == NULL) { + Status = EFI_NOT_FOUND; } - -} - -/** - Variable Driver main entry point. The Variable driver places the 4 EFI - runtime services in the EFI System Table and installs arch protocols - for variable read and write services being availible. It also registers - notification function for EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event. - - @param[in] ImageHandle The firmware allocated handle for the EFI image. - @param[in] SystemTable A pointer to the EFI System Table. - @retval EFI_SUCCESS Variable service successfully initialized. - -**/ -EFI_STATUS -EFIAPI -VariableServiceInitialize ( - IN EFI_HANDLE ImageHandle, - IN EFI_SYSTEM_TABLE *SystemTable - ) -{ - // - // Register FvbNotificationEvent () notify function. - // - EfiCreateProtocolNotifyEvent ( - &gEfiFirmwareVolumeBlockProtocolGuid, - TPL_CALLBACK, - FvbNotificationEvent, - (VOID *)SystemTable, - &mFvbRegistration - ); - - return EFI_SUCCESS; + return Status; } diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.h b/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.h index 91fdf9dbfd..7cc039dea8 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.h +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.h @@ -1,7 +1,7 @@ /** @file The internal header file includes the common header files, defines - internal structure and functions used by RuntimeVariable module. + internal structure and functions used by Variable modules. Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
This program and the accompanying materials @@ -82,17 +82,17 @@ typedef struct { /** Writes a buffer to variable storage space, in the working block. - This function writes a buffer to variable storage space into firmware - volume block device. The destination is specified by parameter + This function writes a buffer to variable storage space into a firmware + volume block device. The destination is specified by the parameter VariableBase. Fault Tolerant Write protocol is used for writing. - @param VariableBase Base address of variable to write - @param Buffer Point to the data buffer - @param BufferSize The number of bytes of the data Buffer + @param VariableBase Base address of the variable to write. + @param Buffer Point to the data buffer. + @param BufferSize The number of bytes of the data Buffer. - @retval EFI_SUCCESS The function completed successfully - @retval EFI_NOT_FOUND Fail to locate Fault Tolerant Write protocol - @retval EFI_ABORTED The function could not complete successfully + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_NOT_FOUND Fail to locate Fault Tolerant Write protocol. + @retval EFI_ABORTED The function could not complete successfully. **/ EFI_STATUS @@ -103,4 +103,329 @@ FtwVariableSpace ( ); +/** + Update the variable region with Variable information. These are the same + arguments as the EFI Variable services. + + @param[in] VariableName Name of variable. + + @param[in] VendorGuid Guid of variable. + + @param[in] Data Variable data. + + @param[in] DataSize Size of data. 0 means delete. + + @param[in] Attributes Attribues of the variable. + + @param[in] Variable The variable information that is used to keep track of variable usage. + + @retval EFI_SUCCESS The update operation is success. + + @retval EFI_OUT_OF_RESOURCES Variable region is full, cannot write other data into this region. + +**/ +EFI_STATUS +UpdateVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN VOID *Data, + IN UINTN DataSize, + IN UINT32 Attributes OPTIONAL, + IN VARIABLE_POINTER_TRACK *Variable + ); + + +/** + Return TRUE if ExitBootServices () has been called. + + @retval TRUE If ExitBootServices () has been called. +**/ +BOOLEAN +AtRuntime ( + VOID + ); + +/** + Initializes a basic mutual exclusion lock. + + This function initializes a basic mutual exclusion lock to the released state + and returns the lock. Each lock provides mutual exclusion access at its task + priority level. Since there is no preemption or multiprocessor support in EFI, + acquiring the lock only consists of raising to the locks TPL. + If Lock is NULL, then ASSERT(). + If Priority is not a valid TPL value, then ASSERT(). + + @param Lock A pointer to the lock data structure to initialize. + @param Priority EFI TPL is associated with the lock. + + @return The lock. + +**/ +EFI_LOCK * +InitializeLock ( + IN OUT EFI_LOCK *Lock, + IN EFI_TPL Priority + ); + + +/** + Acquires lock only at boot time. Simply returns at runtime. + + This is a temperary function that will be removed when + EfiAcquireLock() in UefiLib can handle the call in UEFI + Runtimer driver in RT phase. + It calls EfiAcquireLock() at boot time, and simply returns + at runtime. + + @param Lock A pointer to the lock to acquire. + +**/ +VOID +AcquireLockOnlyAtBootTime ( + IN EFI_LOCK *Lock + ); + + +/** + Releases lock only at boot time. Simply returns at runtime. + + This is a temperary function which will be removed when + EfiReleaseLock() in UefiLib can handle the call in UEFI + Runtimer driver in RT phase. + It calls EfiReleaseLock() at boot time and simply returns + at runtime. + + @param Lock A pointer to the lock to release. + +**/ +VOID +ReleaseLockOnlyAtBootTime ( + IN EFI_LOCK *Lock + ); + +/** + Retrive the FVB protocol interface by HANDLE. + + @param[in] FvBlockHandle The handle of FVB protocol that provides services for + reading, writing, and erasing the target block. + @param[out] FvBlock The interface of FVB protocol + + @retval EFI_SUCCESS The interface information for the specified protocol was returned. + @retval EFI_UNSUPPORTED The device does not support the FVB protocol. + @retval EFI_INVALID_PARAMETER FvBlockHandle is not a valid EFI_HANDLE or FvBlock is NULL. + +**/ +EFI_STATUS +GetFvbByHandle ( + IN EFI_HANDLE FvBlockHandle, + OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock + ); + + +/** + Retrive the Swap Address Range protocol interface. + + @param[out] SarProtocol The interface of SAR protocol + + @retval EFI_SUCCESS The SAR protocol instance was found and returned in SarProtocol. + @retval EFI_NOT_FOUND The SAR protocol instance was not found. + @retval EFI_INVALID_PARAMETER SarProtocol is NULL. + +**/ +EFI_STATUS +GetSarProtocol ( + OUT VOID **SarProtocol + ); + +/** + Function returns an array of handles that support the FVB protocol + in a buffer allocated from pool. + + @param[out] NumberHandles The number of handles returned in Buffer. + @param[out] Buffer A pointer to the buffer to return the requested + array of handles that support FVB protocol. + + @retval EFI_SUCCESS The array of handles was returned in Buffer, and the number of + handles in Buffer was returned in NumberHandles. + @retval EFI_NOT_FOUND No FVB handle was found. + @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the matching results. + @retval EFI_INVALID_PARAMETER NumberHandles is NULL or Buffer is NULL. + +**/ +EFI_STATUS +GetFvbCountAndBuffer ( + OUT UINTN *NumberHandles, + OUT EFI_HANDLE **Buffer + ); + +/** + Initializes variable store area for non-volatile and volatile variable. + + @retval EFI_SUCCESS Function successfully executed. + @retval EFI_OUT_OF_RESOURCES Fail to allocate enough memory resource. + +**/ +EFI_STATUS +VariableCommonInitialize ( + VOID + ); + +/** + This function reclaims variable storage if free size is below the threshold. + +**/ +VOID +ReclaimForOS( + VOID + ); + + +/** + Initializes variable write service after FVB was ready. + + @retval EFI_SUCCESS Function successfully executed. + @retval Others Fail to initialize the variable service. + +**/ +EFI_STATUS +VariableWriteServiceInitialize ( + VOID + ); + +/** + Retrive the SMM Fault Tolerent Write protocol interface. + + @param[out] FtwProtocol The interface of SMM Ftw protocol + + @retval EFI_SUCCESS The SMM SAR protocol instance was found and returned in SarProtocol. + @retval EFI_NOT_FOUND The SMM SAR protocol instance was not found. + @retval EFI_INVALID_PARAMETER SarProtocol is NULL. + +**/ +EFI_STATUS +GetFtwProtocol ( + OUT VOID **FtwProtocol + ); + +/** + Get the proper fvb handle and/or fvb protocol by the given Flash address. + + @param[in] Address The Flash address. + @param[out] FvbHandle In output, if it is not NULL, it points to the proper FVB handle. + @param[out] FvbProtocol In output, if it is not NULL, it points to the proper FVB protocol. + +**/ +EFI_STATUS +GetFvbInfoByAddress ( + IN EFI_PHYSICAL_ADDRESS Address, + OUT EFI_HANDLE *FvbHandle OPTIONAL, + OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvbProtocol OPTIONAL + ); + +/** + + This code finds variable in storage blocks (Volatile or Non-Volatile). + + @param VariableName Name of Variable to be found. + @param VendorGuid Variable vendor GUID. + @param Attributes Attribute value of the variable found. + @param DataSize Size of Data found. If size is less than the + data, this value contains the required size. + @param Data Data pointer. + + @return EFI_INVALID_PARAMETER Invalid parameter. + @return EFI_SUCCESS Find the specified variable. + @return EFI_NOT_FOUND Not found. + @return EFI_BUFFER_TO_SMALL DataSize is too small for the result. + +**/ +EFI_STATUS +EFIAPI +VariableServiceGetVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + OUT UINT32 *Attributes OPTIONAL, + IN OUT UINTN *DataSize, + OUT VOID *Data + ); + +/** + + This code Finds the Next available variable. + + @param VariableNameSize Size of the variable name. + @param VariableName Pointer to variable name. + @param VendorGuid Variable Vendor Guid. + + @return EFI_INVALID_PARAMETER Invalid parameter. + @return EFI_SUCCESS Find the specified variable. + @return EFI_NOT_FOUND Not found. + @return EFI_BUFFER_TO_SMALL DataSize is too small for the result. + +**/ +EFI_STATUS +EFIAPI +VariableServiceGetNextVariableName ( + IN OUT UINTN *VariableNameSize, + IN OUT CHAR16 *VariableName, + IN OUT EFI_GUID *VendorGuid + ); + +/** + + This code sets variable in storage blocks (Volatile or Non-Volatile). + + @param VariableName Name of Variable to be found. + @param VendorGuid Variable vendor GUID. + @param Attributes Attribute value of the variable found + @param DataSize Size of Data found. If size is less than the + data, this value contains the required size. + @param Data Data pointer. + + @return EFI_INVALID_PARAMETER Invalid parameter. + @return EFI_SUCCESS Set successfully. + @return EFI_OUT_OF_RESOURCES Resource not enough to set variable. + @return EFI_NOT_FOUND Not found. + @return EFI_WRITE_PROTECTED Variable is read-only. + +**/ +EFI_STATUS +EFIAPI +VariableServiceSetVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN UINT32 Attributes, + IN UINTN DataSize, + IN VOID *Data + ); + +/** + + This code returns information about the EFI variables. + + @param Attributes Attributes bitmask to specify the type of variables + on which to return information. + @param MaximumVariableStorageSize Pointer to the maximum size of the storage space available + for the EFI variables associated with the attributes specified. + @param RemainingVariableStorageSize Pointer to the remaining size of the storage space available + for EFI variables associated with the attributes specified. + @param MaximumVariableSize Pointer to the maximum size of an individual EFI variables + associated with the attributes specified. + + @return EFI_INVALID_PARAMETER An invalid combination of attribute bits was supplied. + @return EFI_SUCCESS Query successfully. + @return EFI_UNSUPPORTED The attribute is not supported on this platform. + +**/ +EFI_STATUS +EFIAPI +VariableServiceQueryVariableInfo ( + IN UINT32 Attributes, + OUT UINT64 *MaximumVariableStorageSize, + OUT UINT64 *RemainingVariableStorageSize, + OUT UINT64 *MaximumVariableSize + ); + +extern VARIABLE_MODULE_GLOBAL *mVariableModuleGlobal; + #endif diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c new file mode 100644 index 0000000000..f9f20bdf6d --- /dev/null +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c @@ -0,0 +1,402 @@ +/** @file + + Implement all four UEFI Runtime Variable services for the nonvolatile + and volatile storage space and install variable architecture protocol. + +Copyright (c) 2006 - 2010, 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. + +**/ + +#include "Variable.h" + +extern VARIABLE_STORE_HEADER *mNvVariableCache; +VARIABLE_INFO_ENTRY *gVariableInfo; +EFI_HANDLE mHandle = NULL; +EFI_EVENT mVirtualAddressChangeEvent = NULL; +EFI_EVENT mFtwRegistration = NULL; + +/** + Return TRUE if ExitBootServices () has been called + + @retval TRUE If ExitBootServices () has been called +**/ +BOOLEAN +AtRuntime ( + VOID + ) +{ + return EfiAtRuntime (); +} + + +/** + Initializes a basic mutual exclusion lock. + + This function initializes a basic mutual exclusion lock to the released state + and returns the lock. Each lock provides mutual exclusion access at its task + priority level. Since there is no preemption or multiprocessor support in EFI, + acquiring the lock only consists of raising to the locks TPL. + If Lock is NULL, then ASSERT(). + If Priority is not a valid TPL value, then ASSERT(). + + @param Lock A pointer to the lock data structure to initialize. + @param Priority EFI TPL is associated with the lock. + + @return The lock. + +**/ +EFI_LOCK * +InitializeLock ( + IN OUT EFI_LOCK *Lock, + IN EFI_TPL Priority + ) +{ + return EfiInitializeLock (Lock, Priority); +} + + +/** + Acquires lock only at boot time. Simply returns at runtime. + + This is a temperary function that will be removed when + EfiAcquireLock() in UefiLib can handle the call in UEFI + Runtimer driver in RT phase. + It calls EfiAcquireLock() at boot time, and simply returns + at runtime. + + @param Lock A pointer to the lock to acquire. + +**/ +VOID +AcquireLockOnlyAtBootTime ( + IN EFI_LOCK *Lock + ) +{ + if (!AtRuntime ()) { + EfiAcquireLock (Lock); + } +} + + +/** + Releases lock only at boot time. Simply returns at runtime. + + This is a temperary function which will be removed when + EfiReleaseLock() in UefiLib can handle the call in UEFI + Runtimer driver in RT phase. + It calls EfiReleaseLock() at boot time and simply returns + at runtime. + + @param Lock A pointer to the lock to release. + +**/ +VOID +ReleaseLockOnlyAtBootTime ( + IN EFI_LOCK *Lock + ) +{ + if (!AtRuntime ()) { + EfiReleaseLock (Lock); + } +} + +/** + Retrive the Fault Tolerent Write protocol interface. + + @param[out] FtwProtocol The interface of Ftw protocol + + @retval EFI_SUCCESS The FTW protocol instance was found and returned in FtwProtocol. + @retval EFI_NOT_FOUND The FTW protocol instance was not found. + @retval EFI_INVALID_PARAMETER SarProtocol is NULL. + +**/ +EFI_STATUS +GetFtwProtocol ( + OUT VOID **FtwProtocol + ) +{ + EFI_STATUS Status; + + // + // Locate Fault Tolerent Write protocol + // + Status = gBS->LocateProtocol ( + &gEfiFaultTolerantWriteProtocolGuid, + NULL, + FtwProtocol + ); + return Status; +} + +/** + Retrive the FVB protocol interface by HANDLE. + + @param[in] FvBlockHandle The handle of FVB protocol that provides services for + reading, writing, and erasing the target block. + @param[out] FvBlock The interface of FVB protocol + + @retval EFI_SUCCESS The interface information for the specified protocol was returned. + @retval EFI_UNSUPPORTED The device does not support the FVB protocol. + @retval EFI_INVALID_PARAMETER FvBlockHandle is not a valid EFI_HANDLE or FvBlock is NULL. + +**/ +EFI_STATUS +GetFvbByHandle ( + IN EFI_HANDLE FvBlockHandle, + OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock + ) +{ + // + // To get the FVB protocol interface on the handle + // + return gBS->HandleProtocol ( + FvBlockHandle, + &gEfiFirmwareVolumeBlockProtocolGuid, + (VOID **) FvBlock + ); +} + + +/** + Function returns an array of handles that support the FVB protocol + in a buffer allocated from pool. + + @param[out] NumberHandles The number of handles returned in Buffer. + @param[out] Buffer A pointer to the buffer to return the requested + array of handles that support FVB protocol. + + @retval EFI_SUCCESS The array of handles was returned in Buffer, and the number of + handles in Buffer was returned in NumberHandles. + @retval EFI_NOT_FOUND No FVB handle was found. + @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the matching results. + @retval EFI_INVALID_PARAMETER NumberHandles is NULL or Buffer is NULL. + +**/ +EFI_STATUS +GetFvbCountAndBuffer ( + OUT UINTN *NumberHandles, + OUT EFI_HANDLE **Buffer + ) +{ + EFI_STATUS Status; + + // + // Locate all handles of Fvb protocol + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiFirmwareVolumeBlockProtocolGuid, + NULL, + NumberHandles, + Buffer + ); + return Status; +} + + +/** + Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE. + + This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event. + It convers pointer to new virtual address. + + @param Event Event whose notification function is being invoked. + @param Context Pointer to the notification function's context. + +**/ +VOID +EFIAPI +VariableClassAddressChangeEvent ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->GetBlockSize); + EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->GetPhysicalAddress); + EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->GetAttributes); + EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->SetAttributes); + EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->Read); + EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->Write); + EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->EraseBlocks); + EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance); + EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->PlatformLangCodes); + EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->LangCodes); + EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->PlatformLang); + EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase); + EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->VariableGlobal.VolatileVariableBase); + EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal); + EfiConvertPointer (0x0, (VOID **) &mNvVariableCache); +} + + +/** + Notification function of EVT_GROUP_READY_TO_BOOT event group. + + This is a notification function registered on EVT_GROUP_READY_TO_BOOT event group. + When the Boot Manager is about to load and execute a boot option, it reclaims variable + storage if free size is below the threshold. + + @param Event Event whose notification function is being invoked. + @param Context Pointer to the notification function's context. + +**/ +VOID +EFIAPI +OnReadyToBoot ( + EFI_EVENT Event, + VOID *Context + ) +{ + ReclaimForOS (); +} + + +/** + Fault Tolerant Write protocol notification event handler. + + Non-Volatile variable write may needs FTW protocol to reclaim when + writting variable. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context Pointer to the notification function's context. + +**/ +VOID +EFIAPI +FtwNotificationEvent ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvbProtocol; + EFI_FAULT_TOLERANT_WRITE_PROTOCOL *FtwProtocol; + EFI_PHYSICAL_ADDRESS NvStorageVariableBase; + + // + // Ensure FTW protocol is installed. + // + Status = GetFtwProtocol (&FtwProtocol); + if (EFI_ERROR (Status)) { + return ; + } + + // + // Find the proper FVB protocol for variable. + // + NvStorageVariableBase = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageVariableBase64); + if (NvStorageVariableBase == 0) { + NvStorageVariableBase = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageVariableBase); + } + Status = GetFvbInfoByAddress (NvStorageVariableBase, NULL, &FvbProtocol); + if (EFI_ERROR (Status)) { + return ; + } + mVariableModuleGlobal->FvbInstance = FvbProtocol; + + Status = VariableWriteServiceInitialize (); + ASSERT_EFI_ERROR (Status); + + // + // Install the Variable Write Architectural protocol. // + Status = gBS->InstallProtocolInterface ( + &mHandle, + &gEfiVariableWriteArchProtocolGuid, + EFI_NATIVE_INTERFACE, + NULL + ); + ASSERT_EFI_ERROR (Status); + + // + // Close the notify event to avoid install gEfiVariableWriteArchProtocolGuid again. + // + gBS->CloseEvent (Event); + +} + + +/** + Variable Driver main entry point. The Variable driver places the 4 EFI + runtime services in the EFI System Table and installs arch protocols + for variable read and write services being availible. It also registers + a notification function for an EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS Variable service successfully initialized. + +**/ +EFI_STATUS +EFIAPI +VariableServiceInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_EVENT ReadyToBootEvent; + + Status = VariableCommonInitialize (); + ASSERT_EFI_ERROR (Status); + + SystemTable->RuntimeServices->GetVariable = VariableServiceGetVariable; + SystemTable->RuntimeServices->GetNextVariableName = VariableServiceGetNextVariableName; + SystemTable->RuntimeServices->SetVariable = VariableServiceSetVariable; + SystemTable->RuntimeServices->QueryVariableInfo = VariableServiceQueryVariableInfo; + + // + // Now install the Variable Runtime Architectural protocol on a new handle. + // + Status = gBS->InstallProtocolInterface ( + &mHandle, + &gEfiVariableArchProtocolGuid, + EFI_NATIVE_INTERFACE, + NULL + ); + ASSERT_EFI_ERROR (Status); + + // + // Register FtwNotificationEvent () notify function. + // + EfiCreateProtocolNotifyEvent ( + &gEfiFaultTolerantWriteProtocolGuid, + TPL_CALLBACK, + FtwNotificationEvent, + (VOID *)SystemTable, + &mFtwRegistration + ); + + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + VariableClassAddressChangeEvent, + NULL, + &gEfiEventVirtualAddressChangeGuid, + &mVirtualAddressChangeEvent + ); + ASSERT_EFI_ERROR (Status); + + // + // Register the event handling function to reclaim variable for OS usage. + // + Status = EfiCreateEventReadyToBootEx ( + TPL_NOTIFY, + OnReadyToBoot, + NULL, + &ReadyToBootEvent + ); + + if (FeaturePcdGet (PcdVariableCollectStatistics)) { + gBS->InstallConfigurationTable (&gEfiVariableGuid, gVariableInfo); + } + return EFI_SUCCESS; +} + diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf index bde1d04386..15e000e69b 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf @@ -33,6 +33,7 @@ [Sources] Reclaim.c Variable.c + VariableDxe.c Variable.h [Packages] @@ -76,7 +77,7 @@ gEfiMdeModulePkgTokenSpaceGuid.PcdVariableCollectStatistics ## SOMETIME_CONSUMES (statistic the information of variable.) [Depex] - gEfiFirmwareVolumeBlockProtocolGuid AND gEfiFaultTolerantWriteProtocolGuid + TRUE # [Event] # ## @@ -84,4 +85,5 @@ # # # EVENT_TYPE_NOTIFY_SIGNAL ## PRODUCES # -# \ No newline at end of file +# + diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c new file mode 100644 index 0000000000..75a5163cb9 --- /dev/null +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c @@ -0,0 +1,585 @@ +/** @file + + The sample implementation for SMM variable protocol. And this driver + implements an SMI handler to communicate with the DXE runtime driver + to provide variable services. + +Copyright (c) 2010, 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. + +**/ +#include +#include + +#include "Variable.h" +#include "VariableSmmCommon.h" + +extern SMM_VARIABLE_COMMUNICATE_VARIABLE_INFO_ENTRY *gVariableInfo; +EFI_HANDLE mSmmVariableHandle = NULL; +EFI_HANDLE mVariableHandle = NULL; +BOOLEAN mAtRuntime = FALSE; +EFI_GUID mZeroGuid = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}}; +EFI_GUID mSmmVariableWriteGuid = EFI_SMM_VARIABLE_WRITE_GUID; + +EFI_SMM_VARIABLE_PROTOCOL gSmmVariable = { + VariableServiceGetVariable, + VariableServiceGetNextVariableName, + VariableServiceSetVariable, + VariableServiceQueryVariableInfo +}; + + +/** + Return TRUE if ExitBootServices () has been called. + + @retval TRUE If ExitBootServices () has been called. +**/ +BOOLEAN +AtRuntime ( + VOID + ) +{ + return mAtRuntime; +} + +/** + Initializes a basic mutual exclusion lock. + + This function initializes a basic mutual exclusion lock to the released state + and returns the lock. Each lock provides mutual exclusion access at its task + priority level. Since there is no preemption or multiprocessor support in EFI, + acquiring the lock only consists of raising to the locks TPL. + If Lock is NULL, then ASSERT(). + If Priority is not a valid TPL value, then ASSERT(). + + @param Lock A pointer to the lock data structure to initialize. + @param Priority EFI TPL is associated with the lock. + + @return The lock. + +**/ +EFI_LOCK * +InitializeLock ( + IN OUT EFI_LOCK *Lock, + IN EFI_TPL Priority + ) +{ + return Lock; +} + +/** + Acquires lock only at boot time. Simply returns at runtime. + + This is a temperary function that will be removed when + EfiAcquireLock() in UefiLib can handle the call in UEFI + Runtimer driver in RT phase. + It calls EfiAcquireLock() at boot time, and simply returns + at runtime. + + @param Lock A pointer to the lock to acquire. + +**/ +VOID +AcquireLockOnlyAtBootTime ( + IN EFI_LOCK *Lock + ) +{ + +} + + +/** + Releases lock only at boot time. Simply returns at runtime. + + This is a temperary function which will be removed when + EfiReleaseLock() in UefiLib can handle the call in UEFI + Runtimer driver in RT phase. + It calls EfiReleaseLock() at boot time and simply returns + at runtime. + + @param Lock A pointer to the lock to release. + +**/ +VOID +ReleaseLockOnlyAtBootTime ( + IN EFI_LOCK *Lock + ) +{ + +} + +/** + Retrive the SMM Fault Tolerent Write protocol interface. + + @param[out] FtwProtocol The interface of SMM Ftw protocol + + @retval EFI_SUCCESS The SMM FTW protocol instance was found and returned in FtwProtocol. + @retval EFI_NOT_FOUND The SMM FTW protocol instance was not found. + @retval EFI_INVALID_PARAMETER SarProtocol is NULL. + +**/ +EFI_STATUS +GetFtwProtocol ( + OUT VOID **FtwProtocol + ) +{ + EFI_STATUS Status; + + // + // Locate Smm Fault Tolerent Write protocol + // + Status = gSmst->SmmLocateProtocol ( + &gEfiSmmFaultTolerantWriteProtocolGuid, + NULL, + FtwProtocol + ); + return Status; +} + + +/** + Retrive the SMM FVB protocol interface by HANDLE. + + @param[in] FvBlockHandle The handle of SMM FVB protocol that provides services for + reading, writing, and erasing the target block. + @param[out] FvBlock The interface of SMM FVB protocol + + @retval EFI_SUCCESS The interface information for the specified protocol was returned. + @retval EFI_UNSUPPORTED The device does not support the SMM FVB protocol. + @retval EFI_INVALID_PARAMETER FvBlockHandle is not a valid EFI_HANDLE or FvBlock is NULL. + +**/ +EFI_STATUS +GetFvbByHandle ( + IN EFI_HANDLE FvBlockHandle, + OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL **FvBlock + ) +{ + // + // To get the SMM FVB protocol interface on the handle + // + return gSmst->SmmHandleProtocol ( + FvBlockHandle, + &gEfiSmmFirmwareVolumeBlockProtocolGuid, + (VOID **) FvBlock + ); +} + + +/** + Function returns an array of handles that support the SMM FVB protocol + in a buffer allocated from pool. + + @param[out] NumberHandles The number of handles returned in Buffer. + @param[out] Buffer A pointer to the buffer to return the requested + array of handles that support SMM FVB protocol. + + @retval EFI_SUCCESS The array of handles was returned in Buffer, and the number of + handles in Buffer was returned in NumberHandles. + @retval EFI_NOT_FOUND No SMM FVB handle was found. + @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the matching results. + @retval EFI_INVALID_PARAMETER NumberHandles is NULL or Buffer is NULL. + +**/ +EFI_STATUS +GetFvbCountAndBuffer ( + OUT UINTN *NumberHandles, + OUT EFI_HANDLE **Buffer + ) +{ + EFI_STATUS Status; + UINTN BufferSize; + + if ((NumberHandles == NULL) || (Buffer == NULL)) { + return EFI_INVALID_PARAMETER; + } + + BufferSize = 0; + *NumberHandles = 0; + *Buffer = NULL; + Status = gSmst->SmmLocateHandle ( + ByProtocol, + &gEfiSmmFirmwareVolumeBlockProtocolGuid, + NULL, + &BufferSize, + *Buffer + ); + if (EFI_ERROR(Status) && Status != EFI_BUFFER_TOO_SMALL) { + return EFI_NOT_FOUND; + } + + *Buffer = AllocatePool (BufferSize); + if (*Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = gSmst->SmmLocateHandle ( + ByProtocol, + &gEfiSmmFirmwareVolumeBlockProtocolGuid, + NULL, + &BufferSize, + *Buffer + ); + + *NumberHandles = BufferSize / sizeof(EFI_HANDLE); + if (EFI_ERROR(Status)) { + *NumberHandles = 0; + } + + return Status; +} + + +/** + Get the variable statistics information from the information buffer pointed by gVariableInfo. + + @param[in, out] InfoEntry A pointer to the buffer of variable information entry. + On input, point to the variable information returned last time. if + InfoEntry->VendorGuid is zero, return the first information. + On output, point to the next variable information. + @param[in, out] InfoSize On input, the size of the variable information buffer. + On output, the returned variable information size. + + @retval EFI_SUCCESS The variable information is found and returned successfully. + @retval EFI_UNSUPPORTED No variable inoformation exists in variable driver. The + PcdVariableCollectStatistics should be set TRUE to support it. + @retval EFI_BUFFER_TOO_SMALL The buffer is too small to hold the next variable information. + +**/ +EFI_STATUS +SmmVariableGetStatistics ( + IN OUT SMM_VARIABLE_COMMUNICATE_VARIABLE_INFO_ENTRY *InfoEntry, + IN OUT UINTN *InfoSize + ) +{ + SMM_VARIABLE_COMMUNICATE_VARIABLE_INFO_ENTRY *VariableInfo; + UINTN NameLength; + UINTN StatisticsInfoSize; + CHAR16 *InfoName; + + ASSERT (InfoEntry != NULL); + VariableInfo = gVariableInfo; + if (VariableInfo == NULL) { + return EFI_UNSUPPORTED; + } + + StatisticsInfoSize = sizeof (SMM_VARIABLE_COMMUNICATE_VARIABLE_INFO_ENTRY) + StrSize (VariableInfo->Name); + if (*InfoSize < sizeof (SMM_VARIABLE_COMMUNICATE_VARIABLE_INFO_ENTRY)) { + *InfoSize = StatisticsInfoSize; + return EFI_BUFFER_TOO_SMALL; + } + InfoName = (CHAR16 *)(InfoEntry + 1); + + if (CompareGuid (&InfoEntry->VendorGuid, &mZeroGuid)) { + // + // Return the first variable info + // + CopyMem (InfoEntry, VariableInfo, sizeof (SMM_VARIABLE_COMMUNICATE_VARIABLE_INFO_ENTRY)); + CopyMem (InfoName, VariableInfo->Name, StrSize (VariableInfo->Name)); + *InfoSize = StatisticsInfoSize; + return EFI_SUCCESS; + } + + // + // Get the next variable info + // + while (VariableInfo != NULL) { + if (CompareGuid (&VariableInfo->VendorGuid, &InfoEntry->VendorGuid)) { + NameLength = StrSize (VariableInfo->Name); + if (NameLength == StrSize (InfoName)) { + if (CompareMem (VariableInfo->Name, InfoName, NameLength) == 0) { + // + // Find the match one + // + VariableInfo = VariableInfo->Next; + break; + } + } + } + VariableInfo = VariableInfo->Next; + }; + + if (VariableInfo == NULL) { + *InfoSize = 0; + return EFI_SUCCESS; + } + + // + // Output the new variable info + // + StatisticsInfoSize = sizeof (SMM_VARIABLE_COMMUNICATE_VARIABLE_INFO_ENTRY) + StrSize (VariableInfo->Name); + if (*InfoSize < StatisticsInfoSize) { + *InfoSize = StatisticsInfoSize; + return EFI_BUFFER_TOO_SMALL; + } + + CopyMem (InfoEntry, VariableInfo, sizeof (SMM_VARIABLE_COMMUNICATE_VARIABLE_INFO_ENTRY)); + CopyMem (InfoName, VariableInfo->Name, StrSize (VariableInfo->Name)); + *InfoSize = StatisticsInfoSize; + + return EFI_SUCCESS; +} + + +/** + Communication service SMI Handler entry. + + This SMI handler provides services for the variable wrapper driver. + + @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param[in] RegisterContext Points to an optional handler context which was specified when the + handler was registered. + @param[in, out] CommBuffer A pointer to a collection of data in memory that will + be conveyed from a non-SMM environment into an SMM environment. + @param[in, out] CommBufferSize The size of the CommBuffer. + + @retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers + should still be called. + @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should + still be called. + @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still + be called. + @retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced. +**/ +EFI_STATUS +EFIAPI +SmmVariableHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *RegisterContext, + IN OUT VOID *CommBuffer, + IN OUT UINTN *CommBufferSize + ) +{ + EFI_STATUS Status; + SMM_VARIABLE_COMMUNICATE_HEADER *SmmVariableFunctionHeader; + SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *SmmVariableHeader; + SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME *GetNextVariableName; + SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *QueryVariableInfo; + SMM_VARIABLE_COMMUNICATE_VARIABLE_INFO_ENTRY *VariableInfo; + UINTN InfoSize; + + ASSERT (CommBuffer != NULL); + + SmmVariableFunctionHeader = (SMM_VARIABLE_COMMUNICATE_HEADER *)CommBuffer; + switch (SmmVariableFunctionHeader->Function) { + case SMM_VARIABLE_FUNCTION_GET_VARIABLE: + SmmVariableHeader = (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *) SmmVariableFunctionHeader->Data; + Status = VariableServiceGetVariable ( + SmmVariableHeader->Name, + &SmmVariableHeader->Guid, + &SmmVariableHeader->Attributes, + &SmmVariableHeader->DataSize, + (UINT8 *)SmmVariableHeader->Name + SmmVariableHeader->NameSize + ); + break; + + case SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME: + GetNextVariableName = (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME *) SmmVariableFunctionHeader->Data; + Status = VariableServiceGetNextVariableName ( + &GetNextVariableName->NameSize, + GetNextVariableName->Name, + &GetNextVariableName->Guid + ); + break; + + case SMM_VARIABLE_FUNCTION_SET_VARIABLE: + SmmVariableHeader = (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *) SmmVariableFunctionHeader->Data; + Status = VariableServiceSetVariable ( + SmmVariableHeader->Name, + &SmmVariableHeader->Guid, + SmmVariableHeader->Attributes, + SmmVariableHeader->DataSize, + (UINT8 *)SmmVariableHeader->Name + SmmVariableHeader->NameSize + ); + break; + + case SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO: + QueryVariableInfo = (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *) SmmVariableFunctionHeader->Data; + Status = VariableServiceQueryVariableInfo ( + QueryVariableInfo->Attributes, + &QueryVariableInfo->MaximumVariableStorageSize, + &QueryVariableInfo->RemainingVariableStorageSize, + &QueryVariableInfo->MaximumVariableSize + ); + break; + + case SMM_VARIABLE_FUNCTION_READY_TO_BOOT: + ReclaimForOS (); + Status = EFI_SUCCESS; + break; + + case SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE: + mAtRuntime = TRUE; + Status = EFI_SUCCESS; + break; + + case SMM_VARIABLE_FUNCTION_GET_STATISTICS: + VariableInfo = (SMM_VARIABLE_COMMUNICATE_VARIABLE_INFO_ENTRY *) SmmVariableFunctionHeader->Data; + InfoSize = *CommBufferSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_HEADER, Data); + Status = SmmVariableGetStatistics (VariableInfo, &InfoSize); + *CommBufferSize = InfoSize + OFFSET_OF (SMM_VARIABLE_COMMUNICATE_HEADER, Data); + break; + + default: + ASSERT (FALSE); + Status = EFI_UNSUPPORTED; + } + + SmmVariableFunctionHeader->ReturnStatus = Status; + + return EFI_SUCCESS; +} + + +/** + SMM Fault Tolerant Write protocol notification event handler. + + Non-Volatile variable write may needs FTW protocol to reclaim when + writting variable. + + @param Protocol Points to the protocol's unique identifier + @param Interface Points to the interface instance + @param Handle The handle on which the interface was installed + + @retval EFI_SUCCESS SmmEventCallback runs successfully + @retval EFI_NOT_FOUND The Fvb protocol for variable is not found. + + **/ +EFI_STATUS +EFIAPI +SmmFtwNotificationEvent ( + IN CONST EFI_GUID *Protocol, + IN VOID *Interface, + IN EFI_HANDLE Handle + ) +{ + EFI_STATUS Status; + EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvbProtocol; + EFI_SMM_FAULT_TOLERANT_WRITE_PROTOCOL *FtwProtocol; + EFI_PHYSICAL_ADDRESS NvStorageVariableBase; + + if (mVariableModuleGlobal->FvbInstance != NULL) { + return EFI_SUCCESS; + } + + // + // Ensure SMM FTW protocol is installed. + // + Status = GetFtwProtocol (&FtwProtocol); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Find the proper FVB protocol for variable. + // + NvStorageVariableBase = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageVariableBase64); + if (NvStorageVariableBase == 0) { + NvStorageVariableBase = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageVariableBase); + } + Status = GetFvbInfoByAddress (NvStorageVariableBase, NULL, &FvbProtocol); + if (EFI_ERROR (Status)) { + return EFI_NOT_FOUND; + } + + mVariableModuleGlobal->FvbInstance = FvbProtocol; + + Status = VariableWriteServiceInitialize (); + ASSERT_EFI_ERROR (Status); + + // + // Notify the variable wrapper driver the variable write service is ready + // + Status = gBS->InstallProtocolInterface ( + &mSmmVariableHandle, + &mSmmVariableWriteGuid, + EFI_NATIVE_INTERFACE, + NULL + ); + ASSERT_EFI_ERROR (Status); + + return EFI_SUCCESS; +} + + +/** + Variable Driver main entry point. The Variable driver places the 4 EFI + runtime services in the EFI System Table and installs arch protocols + for variable read and write services being availible. It also registers + a notification function for an EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS Variable service successfully initialized. + +**/ +EFI_STATUS +EFIAPI +VariableServiceInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_HANDLE VariableHandle; + VOID *SmmFtwRegistration; + + // + // Variable initialize. + // + Status = VariableCommonInitialize (); + ASSERT_EFI_ERROR (Status); + + // + // Install the Smm Variable Protocol on a new handle. + // + VariableHandle = NULL; + Status = gSmst->SmmInstallProtocolInterface ( + &VariableHandle, + &gEfiSmmVariableProtocolGuid, + EFI_NATIVE_INTERFACE, + &gSmmVariable + ); + ASSERT_EFI_ERROR (Status); + + /// + /// Register SMM variable SMI handler + /// + VariableHandle = NULL; + Status = gSmst->SmiHandlerRegister (SmmVariableHandler, &gEfiSmmVariableProtocolGuid, &VariableHandle); + ASSERT_EFI_ERROR (Status); + + // + // Notify the variable wrapper driver the variable service is ready + // + Status = SystemTable->BootServices->InstallProtocolInterface ( + &mVariableHandle, + &gEfiSmmVariableProtocolGuid, + EFI_NATIVE_INTERFACE, + &gSmmVariable + ); + ASSERT_EFI_ERROR (Status); + + // + // Register FtwNotificationEvent () notify function. + // + Status = gSmst->SmmRegisterProtocolNotify ( + &gEfiSmmFaultTolerantWriteProtocolGuid, + SmmFtwNotificationEvent, + &SmmFtwRegistration + ); + ASSERT_EFI_ERROR (Status); + + SmmFtwNotificationEvent (NULL, NULL, NULL); + + return EFI_SUCCESS; +} + + diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf new file mode 100644 index 0000000000..6a52c6f490 --- /dev/null +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf @@ -0,0 +1,86 @@ +## @file +# Component description file for SMM Variable module. +# +# This module installs SMM variable protocol into SMM protocol database, +# which can be used by SMM driver, and installs SMM variable protocol +# into BS protocol database, which can be used to notify the SMM Runtime +# Dxe driver that the SMM variable service is ready. +# This module should be used with SMM Runtime DXE module together. The +# SMM Runtime DXE module would install variable arch protocol and variable +# write arch protocol based on SMM variable module. +# Copyright (c) 2010, 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. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = VariableSmm + FILE_GUID = 23A089B3-EED5-4ac5-B2AB-43E3298C2343 + MODULE_TYPE = DXE_SMM_DRIVER + VERSION_STRING = 1.0 + PI_SPECIFICATION_VERSION = 0x0001000A + ENTRY_POINT = VariableServiceInitialize + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + + +[Sources] + Reclaim.c + Variable.c + VariableSmm.c + Variable.h + VariableSmmCommon.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + UefiDriverEntryPoint + MemoryAllocationLib + BaseLib + SynchronizationLib + UefiLib + SmmLib + SmmServicesTableLib + BaseMemoryLib + DebugLib + DxeServicesTableLib + +[Protocols] + gEfiSmmFirmwareVolumeBlockProtocolGuid ## SOMETIMES_CONSUMES + gEfiSmmVariableProtocolGuid ## ALWAYS_PRODUCES + gEfiSmmFaultTolerantWriteProtocolGuid ## SOMETIMES_CONSUMES + +[Guids] + gEfiVariableGuid ## PRODUCES ## Configuration Table Guid + gEfiGlobalVariableGuid ## PRODUCES ## Variable Guid + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase64 + gEfiMdeModulePkgTokenSpaceGuid.PcdMaxVariableSize + gEfiMdeModulePkgTokenSpaceGuid.PcdMaxHardwareErrorVariableSize + gEfiMdeModulePkgTokenSpaceGuid.PcdVariableStoreSize + gEfiMdeModulePkgTokenSpaceGuid.PcdHwErrStorageSize + +[FeaturePcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdVariableCollectStatistics ## SOMETIME_CONSUMES (statistic the information of variable.) + +[Depex] + TRUE + + \ No newline at end of file diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmCommon.h b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmCommon.h new file mode 100644 index 0000000000..06541ecb0f --- /dev/null +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmCommon.h @@ -0,0 +1,40 @@ +/** @file + + The internal header file includes the common header files shared + by VariableSmm module and VariableSmmRuntimeDxe module. + +Copyright (c) 2010, 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. + +**/ + +#ifndef _VARIABLE_SMM_COMMON_H_ +#define _VARIABLE_SMM_COMMON_H_ + +#include + +#include +#include +#include + +#define EFI_SMM_VARIABLE_WRITE_GUID \ + { 0x93ba1826, 0xdffb, 0x45dd, { 0x82, 0xa7, 0xe7, 0xdc, 0xaa, 0x3b, 0xbd, 0xf3 } } + +/// +/// Size of SMM communicate header, without including the payload. +/// +#define SMM_COMMUNICATE_HEADER_SIZE (OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data)) + +/// +/// Size of SMM variable communicate header, without including the payload. +/// +#define SMM_VARIABLE_COMMUNICATE_HEADER_SIZE (OFFSET_OF (SMM_VARIABLE_COMMUNICATE_HEADER, Data)) + + +#endif diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c new file mode 100644 index 0000000000..ac8b96c766 --- /dev/null +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c @@ -0,0 +1,650 @@ +/** @file + + Implement all four UEFI Runtime Variable services for the nonvolatile + and volatile storage space and install variable architecture protocol + based on SMM variable module. + +Copyright (c) 2010, 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. + +**/ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "VariableSmmCommon.h" + +EFI_HANDLE mHandle = NULL; +EFI_SMM_VARIABLE_PROTOCOL *mSmmVariable = NULL; +EFI_EVENT mVirtualAddressChangeEvent = NULL; +EFI_SMM_COMMUNICATION_PROTOCOL *mSmmCommunication = NULL; +UINT8 *mVariableBuffer = NULL; +UINT8 *mVariableBufferPhysical = NULL; +EFI_GUID mSmmVariableWriteGuid = EFI_SMM_VARIABLE_WRITE_GUID; +UINTN mVariableBufferSize; + + +/** + Initialize the communicate buffer using DataSize and Function. + + The communicate size is: SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + + DataSize. + + @param[out] DataPtr Points to the data in the communicate buffer. + @param[in] DataSize The data size to send to SMM. + @param[in] Function The function number to initialize the communicate header. + + @retval EFI_INVALID_PARAMETER The data size is too big. + @retval EFI_SUCCESS Find the specified variable. + +**/ +EFI_STATUS +InitCommunicateBuffer ( + OUT VOID **DataPtr OPTIONAL, + IN UINTN DataSize, + IN UINTN Function + ) +{ + EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; + SMM_VARIABLE_COMMUNICATE_HEADER *SmmVariableFunctionHeader; + + + if (DataSize + SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE > mVariableBufferSize) { + return EFI_INVALID_PARAMETER; + } + + SmmCommunicateHeader = (EFI_SMM_COMMUNICATE_HEADER *) mVariableBuffer; + CopyGuid (&SmmCommunicateHeader->HeaderGuid, &gEfiSmmVariableProtocolGuid); + SmmCommunicateHeader->MessageLength = DataSize + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE; + + SmmVariableFunctionHeader = (SMM_VARIABLE_COMMUNICATE_HEADER *) SmmCommunicateHeader->Data; + SmmVariableFunctionHeader->Function = Function; + if (DataPtr != NULL) { + *DataPtr = SmmVariableFunctionHeader->Data; + } + + return EFI_SUCCESS; +} + + +/** + Send the data in communicate buffer to SMM. + + @param[in] DataSize This size of the function header and the data. + + @RetVal EFI_SUCCESS Success is returned from the functin in SMM. + @RetVal Others Failure is returned from the function in SMM. + +**/ +EFI_STATUS +SendCommunicateBuffer ( + IN UINTN DataSize + ) +{ + EFI_STATUS Status; + UINTN CommSize; + EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; + SMM_VARIABLE_COMMUNICATE_HEADER *SmmVariableFunctionHeader; + + CommSize = DataSize + SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE; + Status = mSmmCommunication->Communicate (mSmmCommunication, mVariableBufferPhysical, &CommSize); + ASSERT_EFI_ERROR (Status); + + SmmCommunicateHeader = (EFI_SMM_COMMUNICATE_HEADER *) mVariableBuffer; + SmmVariableFunctionHeader = (SMM_VARIABLE_COMMUNICATE_HEADER *)SmmCommunicateHeader->Data; + return SmmVariableFunctionHeader->ReturnStatus; +} + + +/** + This code finds variable in storage blocks (Volatile or Non-Volatile). + + @param[in] VariableName Name of Variable to be found. + @param[in] VendorGuid Variable vendor GUID. + @param[out] Attributes Attribute value of the variable found. + @param[in, out] DataSize Size of Data found. If size is less than the + data, this value contains the required size. + @param[out] Data Data pointer. + + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_SUCCESS Find the specified variable. + @retval EFI_NOT_FOUND Not found. + @retval EFI_BUFFER_TO_SMALL DataSize is too small for the result. + +**/ +EFI_STATUS +EFIAPI +RuntimeServiceGetVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + OUT UINT32 *Attributes OPTIONAL, + IN OUT UINTN *DataSize, + OUT VOID *Data + ) +{ + EFI_STATUS Status; + UINTN PayloadSize; + SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *SmmVariableHeader; + + if (VariableName == NULL || VendorGuid == NULL || DataSize == NULL) { + return EFI_INVALID_PARAMETER; + } + + if ((*DataSize != 0) && (Data == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Init the communicate buffer. The buffer data size is: + // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize. + // + PayloadSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + StrSize (VariableName); + Status = InitCommunicateBuffer (&SmmVariableHeader, PayloadSize, SMM_VARIABLE_FUNCTION_GET_VARIABLE); + if (EFI_ERROR (Status)) { + return Status; + } + ASSERT (SmmVariableHeader != NULL); + + CopyGuid (&SmmVariableHeader->Guid, VendorGuid); + SmmVariableHeader->DataSize = *DataSize; + SmmVariableHeader->NameSize = StrSize (VariableName); + if (Attributes == NULL) { + SmmVariableHeader->Attributes = 0; + } else { + SmmVariableHeader->Attributes = *Attributes; + } + CopyMem (SmmVariableHeader->Name, VariableName, SmmVariableHeader->NameSize); + + // + // Send data to SMM. + // + Status = SendCommunicateBuffer (PayloadSize); + + // + // Get data from SMM. + // + *DataSize = SmmVariableHeader->DataSize; + if (Attributes != NULL) { + *Attributes = SmmVariableHeader->Attributes; + } + + if (EFI_ERROR (Status)) { + return Status; + } + + CopyMem (Data, (UINT8 *)SmmVariableHeader->Name + SmmVariableHeader->NameSize, SmmVariableHeader->DataSize); + + return Status; +} + + +/** + This code Finds the Next available variable. + + @param[in, out] VariableNameSize Size of the variable name. + @param[in, out] VariableName Pointer to variable name. + @param[in, out] VendorGuid Variable Vendor Guid. + + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_SUCCESS Find the specified variable. + @retval EFI_NOT_FOUND Not found. + @retval EFI_BUFFER_TO_SMALL DataSize is too small for the result. + +**/ +EFI_STATUS +EFIAPI +RuntimeServiceGetNextVariableName ( + IN OUT UINTN *VariableNameSize, + IN OUT CHAR16 *VariableName, + IN OUT EFI_GUID *VendorGuid + ) +{ + EFI_STATUS Status; + UINTN PayloadSize; + SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME *SmmGetNextVariableName; + + if (VariableNameSize == NULL || VariableName == NULL || VendorGuid == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Init the communicate buffer. The buffer data size is: + // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize. + // + PayloadSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name) + *VariableNameSize; + Status = InitCommunicateBuffer (&SmmGetNextVariableName, PayloadSize, SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME); + if (EFI_ERROR (Status)) { + return Status; + } + ASSERT (SmmGetNextVariableName != NULL); + + SmmGetNextVariableName->NameSize = *VariableNameSize; + CopyGuid (&SmmGetNextVariableName->Guid, VendorGuid); + CopyMem (SmmGetNextVariableName->Name, VariableName, *VariableNameSize); + + // + // Send data to SMM + // + Status = SendCommunicateBuffer (PayloadSize); + + // + // Get data from SMM. + // + *VariableNameSize = SmmGetNextVariableName->NameSize; + if (EFI_ERROR (Status)) { + return Status; + } + + CopyGuid (VendorGuid, &SmmGetNextVariableName->Guid); + CopyMem (VariableName, SmmGetNextVariableName->Name, SmmGetNextVariableName->NameSize); + + return Status; +} + +/** + This code sets variable in storage blocks (Volatile or Non-Volatile). + + @param[in] VariableName Name of Variable to be found. + @param[in] VendorGuid Variable vendor GUID. + @param[in] Attributes Attribute value of the variable found + @param[in] DataSize Size of Data found. If size is less than the + data, this value contains the required size. + @param[in] Data Data pointer. + + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_SUCCESS Set successfully. + @retval EFI_OUT_OF_RESOURCES Resource not enough to set variable. + @retval EFI_NOT_FOUND Not found. + @retval EFI_WRITE_PROTECTED Variable is read-only. + +**/ +EFI_STATUS +EFIAPI +RuntimeServiceSetVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN UINT32 Attributes, + IN UINTN DataSize, + IN VOID *Data + ) +{ + EFI_STATUS Status; + UINTN PayloadSize; + SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *SmmVariableHeader; + + // + // Check input parameters. + // + if (VariableName == NULL || VariableName[0] == 0 || VendorGuid == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (DataSize != 0 && Data == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Init the communicate buffer. The buffer data size is: + // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize. + // + PayloadSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + StrSize (VariableName) + DataSize; + Status = InitCommunicateBuffer (&SmmVariableHeader, PayloadSize, SMM_VARIABLE_FUNCTION_SET_VARIABLE); + if (EFI_ERROR (Status)) { + return Status; + } + ASSERT (SmmVariableHeader != NULL); + + CopyGuid ((EFI_GUID *) &SmmVariableHeader->Guid, VendorGuid); + SmmVariableHeader->DataSize = DataSize; + SmmVariableHeader->NameSize = StrSize (VariableName); + SmmVariableHeader->Attributes = Attributes; + CopyMem (SmmVariableHeader->Name, VariableName, SmmVariableHeader->NameSize); + CopyMem ((UINT8 *) SmmVariableHeader->Name + SmmVariableHeader->NameSize, Data, DataSize); + + // + // Send data to SMM. + // + Status = SendCommunicateBuffer (PayloadSize); + + return Status; +} + + +/** + This code returns information about the EFI variables. + + @param[in] Attributes Attributes bitmask to specify the type of variables + on which to return information. + @param[out] MaximumVariableStorageSize Pointer to the maximum size of the storage space available + for the EFI variables associated with the attributes specified. + @param[out] RemainingVariableStorageSize Pointer to the remaining size of the storage space available + for EFI variables associated with the attributes specified. + @param[out] MaximumVariableSize Pointer to the maximum size of an individual EFI variables + associated with the attributes specified. + + @retval EFI_INVALID_PARAMETER An invalid combination of attribute bits was supplied. + @retval EFI_SUCCESS Query successfully. + @retval EFI_UNSUPPORTED The attribute is not supported on this platform. + +**/ +EFI_STATUS +EFIAPI +RuntimeServiceQueryVariableInfo ( + IN UINT32 Attributes, + OUT UINT64 *MaximumVariableStorageSize, + OUT UINT64 *RemainingVariableStorageSize, + OUT UINT64 *MaximumVariableSize + ) +{ + EFI_STATUS Status; + UINTN PayloadSize; + SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *SmmQueryVariableInfo; + + if(MaximumVariableStorageSize == NULL || RemainingVariableStorageSize == NULL || MaximumVariableSize == NULL || Attributes == 0) { + return EFI_INVALID_PARAMETER; + } + + // + // Init the communicate buffer. The buffer data size is: + // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize; + // + PayloadSize = sizeof (SMM_VARIABLE_COMMUNICATE_VARIABLE_INFO_ENTRY); + Status = InitCommunicateBuffer (&SmmQueryVariableInfo, PayloadSize, SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO); + if (EFI_ERROR (Status)) { + return Status; + } + ASSERT (SmmQueryVariableInfo != NULL); + + SmmQueryVariableInfo->Attributes = Attributes; + + // + // Send data to SMM. + // + Status = SendCommunicateBuffer (PayloadSize); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get data from SMM. + // + *MaximumVariableSize = SmmQueryVariableInfo->MaximumVariableSize; + *MaximumVariableStorageSize = SmmQueryVariableInfo->MaximumVariableStorageSize; + *RemainingVariableStorageSize = SmmQueryVariableInfo->RemainingVariableStorageSize; + + return EFI_SUCCESS; +} + + +/** + Exit Boot Services Event notification handler. + + Notify SMM variable driver about the event. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context Pointer to the notification function's context. + +**/ +VOID +EFIAPI +OnExitBootServices ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // Init the communicate buffer. The buffer data size is: + // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE. + // + InitCommunicateBuffer (NULL, 0, SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE); + + // + // Send data to SMM. + // + SendCommunicateBuffer (0); +} + + +/** + On Ready To Boot Services Event notification handler. + + Notify SMM variable driver about the event. + + @param[in] Event Event whose notification function is being invoked + @param[in] Context Pointer to the notification function's context + +**/ +VOID +EFIAPI +OnReadyToBoot ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // Init the communicate buffer. The buffer data size is: + // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE. + // + InitCommunicateBuffer (NULL, 0, SMM_VARIABLE_FUNCTION_READY_TO_BOOT); + + // + // Send data to SMM. + // + SendCommunicateBuffer (0); +} + + +/** + Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE. + + This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event. + It convers pointer to new virtual address. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context Pointer to the notification function's context. + +**/ +VOID +EFIAPI +VariableAddressChangeEvent ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EfiConvertPointer (0x0, (VOID **) &mVariableBuffer); + EfiConvertPointer (0x0, (VOID **) &mSmmCommunication); +} + + +/** + Initialize variable service and install Variable Architectural protocol. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context Pointer to the notification function's context. + +**/ +VOID +EFIAPI +SmmVariableReady ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + + Status = gBS->LocateProtocol (&gEfiSmmVariableProtocolGuid, NULL, (VOID **)&mSmmVariable); + if (EFI_ERROR (Status)) { + return; + } + + Status = gBS->LocateProtocol (&gEfiSmmCommunicationProtocolGuid, NULL, (VOID **) &mSmmCommunication); + ASSERT_EFI_ERROR (Status); + + // + // Allocate memory for variable store. + // + mVariableBufferSize = SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE; + mVariableBufferSize += MAX (PcdGet32 (PcdMaxVariableSize), PcdGet32 (PcdMaxHardwareErrorVariableSize)); + mVariableBuffer = AllocateRuntimePool (mVariableBufferSize); + ASSERT (mVariableBuffer != NULL); + + // + // Save the buffer physical address used for SMM conmunication. + // + mVariableBufferPhysical = mVariableBuffer; + + gRT->GetVariable = RuntimeServiceGetVariable; + gRT->GetNextVariableName = RuntimeServiceGetNextVariableName; + gRT->SetVariable = RuntimeServiceSetVariable; + gRT->QueryVariableInfo = RuntimeServiceQueryVariableInfo; + + // + // Install the Variable Architectural Protocol on a new handle. + // + Status = gBS->InstallProtocolInterface ( + &mHandle, + &gEfiVariableArchProtocolGuid, + EFI_NATIVE_INTERFACE, + NULL + ); + ASSERT_EFI_ERROR (Status); +} + + +/** + SMM Non-Volatile variable write service is ready notify event handler. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context Pointer to the notification function's context. + +**/ +VOID +EFIAPI +SmmVariableWriteReady ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + VOID *ProtocolOps; + + // + // Check whether the protocol is installed or not. + // + Status = gBS->LocateProtocol (&mSmmVariableWriteGuid, NULL, (VOID **) &ProtocolOps); + if (EFI_ERROR (Status)) { + return; + } + + Status = gBS->InstallProtocolInterface ( + &mHandle, + &gEfiVariableWriteArchProtocolGuid, + EFI_NATIVE_INTERFACE, + NULL + ); + ASSERT_EFI_ERROR (Status); +} + + +/** + Variable Driver main entry point. The Variable driver places the 4 EFI + runtime services in the EFI System Table and installs arch protocols + for variable read and write services being availible. It also registers + a notification function for an EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS Variable service successfully initialized. + +**/ +EFI_STATUS +EFIAPI +VariableSmmRuntimeInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + VOID *SmmVariableRegistration; + VOID *SmmVariableWriteRegistration; + EFI_EVENT OnReadyToBootEvent; + EFI_EVENT ExitBootServiceEvent; + + // + // Smm variable service is ready + // + EfiCreateProtocolNotifyEvent ( + &gEfiSmmVariableProtocolGuid, + TPL_CALLBACK, + SmmVariableReady, + NULL, + &SmmVariableRegistration + ); + + // + // Smm Non-Volatile variable write service is ready + // + EfiCreateProtocolNotifyEvent ( + &mSmmVariableWriteGuid, + TPL_CALLBACK, + SmmVariableWriteReady, + NULL, + &SmmVariableWriteRegistration + ); + + // + // Register the event to reclaim variable for OS usage. + // + EfiCreateEventReadyToBootEx ( + TPL_NOTIFY, + OnReadyToBoot, + NULL, + &OnReadyToBootEvent + ); + + // + // Register the event to inform SMM variable that it is at runtime. + // + gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + OnExitBootServices, + NULL, + &gEfiEventExitBootServicesGuid, + &ExitBootServiceEvent + ); + + // + // Register the event to convert the pointer for runtime. + // + gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + VariableAddressChangeEvent, + NULL, + &gEfiEventVirtualAddressChangeGuid, + &mVirtualAddressChangeEvent + ); + + return EFI_SUCCESS; +} + diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf new file mode 100644 index 0000000000..9f1a6ff7e1 --- /dev/null +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf @@ -0,0 +1,69 @@ +## @file +# Component description file for Variable SmmRuntimeDxe module. +# +# This module is the Runtime DXE part correspond to SMM variable module. It +# installs variable arch protocol and variable write arch protocol and works +# with SMM variable module together. +# Copyright (c) 2010, 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. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = VariableSmmRuntimeDxe + FILE_GUID = 9F7DCADE-11EA-448a-A46F-76E003657DD1 + MODULE_TYPE = DXE_RUNTIME_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = VariableSmmRuntimeInitialize + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# +# VIRTUAL_ADDRESS_MAP_CALLBACK = VariableAddressChangeEvent +# + +[Sources] + VariableSmmRuntimeDxe.c + VariableSmmCommon.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + SecurityPkg/SecurityPkg.dec + +[LibraryClasses] + MemoryAllocationLib + BaseLib + UefiBootServicesTableLib + DebugLib + UefiRuntimeLib + DxeServicesTableLib + UefiDriverEntryPoint + PcdLib + +[Protocols] + gEfiVariableWriteArchProtocolGuid ## ALWAYS_PRODUCES + gEfiVariableArchProtocolGuid ## ALWAYS_PRODUCES + gEfiSmmCommunicationProtocolGuid + gEfiSmmVariableProtocolGuid + +[Guids] + gEfiEventVirtualAddressChangeGuid ## PRODUCES ## Event + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdMaxVariableSize + gEfiMdeModulePkgTokenSpaceGuid.PcdMaxHardwareErrorVariableSize + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase + +[Depex] + gEfiSmmCommunicationProtocolGuid -- 2.39.2