From e20465f15ec92df6aaa61f95483397d0f63638d0 Mon Sep 17 00:00:00 2001 From: Guo Mang Date: Fri, 23 Dec 2016 13:10:05 +0800 Subject: BroxtonSiPkg: Add SampleCode/MdeModulePkg Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Guo Mang --- .../MdeModulePkg/Include/Guid/AcpiS3Context.h | 71 + .../MdeModulePkg/Include/Guid/ConsoleOutDevice.h | 25 + .../Include/Guid/MemoryTypeInformation.h | 38 + .../SampleCode/MdeModulePkg/Include/Ppi/Capsule.h | 125 + .../MdeModulePkg/Include/Ppi/SmmAccess.h | 145 + .../MdeModulePkg/Include/Ppi/SmmControl.h | 95 + .../MdeModulePkg/Include/Protocol/SmmVariable.h | 41 + .../Universal/Variable/Pei/PeiVariable.uni | Bin 0 -> 1950 bytes .../Universal/Variable/Pei/PeiVariableExtra.uni | Bin 0 -> 1358 bytes .../MdeModulePkg/Universal/Variable/Pei/Variable.c | 1383 ++++++ .../MdeModulePkg/Universal/Variable/Pei/Variable.h | 152 + .../Universal/Variable/Pei/VariablePei.inf | 75 + .../Universal/Variable/RuntimeDxe/Measurement.c | 260 ++ .../Universal/Variable/RuntimeDxe/TcgMorLockDxe.c | 93 + .../Universal/Variable/RuntimeDxe/TcgMorLockSmm.c | 407 ++ .../Universal/Variable/RuntimeDxe/VarCheck.c | 161 + .../Universal/Variable/RuntimeDxe/Variable.c | 4818 ++++++++++++++++++++ .../Universal/Variable/RuntimeDxe/Variable.h | 841 ++++ .../Universal/Variable/RuntimeDxe/VariableDxe.c | 436 ++ .../Universal/Variable/RuntimeDxe/VariableExLib.c | 291 ++ .../Variable/RuntimeDxe/VariableRuntimeDxe.inf | 129 + .../Variable/RuntimeDxe/VariableRuntimeDxe.uni | Bin 0 -> 3158 bytes .../RuntimeDxe/VariableRuntimeDxeExtra.uni | Bin 0 -> 1370 bytes .../Universal/Variable/RuntimeDxe/VariableSmm.c | 911 ++++ .../Universal/Variable/RuntimeDxe/VariableSmm.inf | 114 + .../Universal/Variable/RuntimeDxe/VariableSmm.uni | Bin 0 -> 4462 bytes .../Variable/RuntimeDxe/VariableSmmExtra.uni | Bin 0 -> 1344 bytes .../Variable/RuntimeDxe/VariableSmmRuntimeDxe.c | 1228 +++++ .../Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf | 81 + .../Variable/RuntimeDxe/VariableSmmRuntimeDxe.uni | Bin 0 -> 3028 bytes .../RuntimeDxe/VariableSmmRuntimeDxeExtra.uni | Bin 0 -> 1384 bytes 31 files changed, 11920 insertions(+) create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Include/Guid/AcpiS3Context.h create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Include/Guid/ConsoleOutDevice.h create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Include/Guid/MemoryTypeInformation.h create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Include/Ppi/Capsule.h create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Include/Ppi/SmmAccess.h create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Include/Ppi/SmmControl.h create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Include/Protocol/SmmVariable.h create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/Pei/PeiVariable.uni create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/Pei/PeiVariableExtra.uni create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/Pei/Variable.c create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/Pei/Variable.h create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/Pei/VariablePei.inf create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/Measurement.c create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/TcgMorLockDxe.c create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/TcgMorLockSmm.c create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VarCheck.c create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.h create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableExLib.c create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.uni create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxeExtra.uni create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.uni create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmExtra.uni create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.uni create mode 100644 Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxeExtra.uni (limited to 'Silicon/BroxtonSoC') diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Include/Guid/AcpiS3Context.h b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Include/Guid/AcpiS3Context.h new file mode 100644 index 0000000000..26f3133213 --- /dev/null +++ b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Include/Guid/AcpiS3Context.h @@ -0,0 +1,71 @@ +/** @file + Get Pci Express address library implementation. + + Copyright (c) 2011 - 2016, 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 _ACPI_S3_DATA_H_ +#define _ACPI_S3_DATA_H_ + +#include + +#define SMM_S3_RESUME_SMM_32 SIGNATURE_64 ('S','M','M','S','3','_','3','2') +#define SMM_S3_RESUME_SMM_64 SIGNATURE_64 ('S','M','M','S','3','_','6','4') + +#pragma pack(1) + +typedef struct { + UINT64 Signature; + EFI_PHYSICAL_ADDRESS SmmS3ResumeEntryPoint; + EFI_PHYSICAL_ADDRESS SmmS3StackBase; + UINT64 SmmS3StackSize; + UINT64 SmmS3Cr0; + UINT64 SmmS3Cr3; + UINT64 SmmS3Cr4; + UINT16 ReturnCs; + EFI_PHYSICAL_ADDRESS ReturnEntryPoint; + EFI_PHYSICAL_ADDRESS ReturnContext1; + EFI_PHYSICAL_ADDRESS ReturnContext2; + EFI_PHYSICAL_ADDRESS ReturnStackPointer; + EFI_PHYSICAL_ADDRESS Smst; +} SMM_S3_RESUME_STATE; + +typedef struct { + EFI_PHYSICAL_ADDRESS AcpiFacsTable; + EFI_PHYSICAL_ADDRESS IdtrProfile; + EFI_PHYSICAL_ADDRESS S3NvsPageTableAddress; + EFI_PHYSICAL_ADDRESS BootScriptStackBase; + UINT64 BootScriptStackSize; + EFI_PHYSICAL_ADDRESS S3DebugBufferAddress; +} ACPI_S3_CONTEXT; + +typedef struct { + UINT16 ReturnCs; + UINT64 ReturnStatus; + EFI_PHYSICAL_ADDRESS ReturnEntryPoint; + EFI_PHYSICAL_ADDRESS ReturnStackPointer; + EFI_PHYSICAL_ADDRESS AsmTransferControl; + IA32_DESCRIPTOR Idtr; +} PEI_S3_RESUME_STATE; + +#pragma pack() + +#define EFI_ACPI_S3_CONTEXT_GUID \ + { \ + 0xef98d3a, 0x3e33, 0x497a, {0xa4, 0x1, 0x77, 0xbe, 0x3e, 0xb7, 0x4f, 0x38} \ + } + +extern EFI_GUID gEfiAcpiS3ContextGuid; +extern EFI_GUID gEfiAcpiVariableGuid; + +#endif + diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Include/Guid/ConsoleOutDevice.h b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Include/Guid/ConsoleOutDevice.h new file mode 100644 index 0000000000..3284418edf --- /dev/null +++ b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Include/Guid/ConsoleOutDevice.h @@ -0,0 +1,25 @@ +/** @file + This GUID can be installed to the device handle to specify that the device is the console-out device. + + Copyright (c) 2006 - 2016, 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 __CONSOLE_OUT_DEVICE_H__ +#define __CONSOLE_OUT_DEVICE_H__ + +#define EFI_CONSOLE_OUT_DEVICE_GUID \ + { 0xd3b36f2c, 0xd551, 0x11d4, {0x9a, 0x46, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } } + +extern EFI_GUID gEfiConsoleOutDeviceGuid; + +#endif + diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Include/Guid/MemoryTypeInformation.h b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Include/Guid/MemoryTypeInformation.h new file mode 100644 index 0000000000..c441fff6ff --- /dev/null +++ b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Include/Guid/MemoryTypeInformation.h @@ -0,0 +1,38 @@ +/** @file + This file defines: + * Memory Type Information GUID for HOB and Variable. + * Memory Type Information Variable Name. + * Memory Type Information GUID HOB data structure. + + The memory type information HOB and variable can + be used to store the information for each memory type in Variable or HOB. + + Copyright (c) 2006 - 2016, 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 __MEMORY_TYPE_INFORMATION_GUID_H__ +#define __MEMORY_TYPE_INFORMATION_GUID_H__ + +#define EFI_MEMORY_TYPE_INFORMATION_GUID \ + { 0x4c19049f,0x4137,0x4dd3, { 0x9c,0x10,0x8b,0x97,0xa8,0x3f,0xfd,0xfa } } + +#define EFI_MEMORY_TYPE_INFORMATION_VARIABLE_NAME L"MemoryTypeInformation" + +extern EFI_GUID gEfiMemoryTypeInformationGuid; + +typedef struct { + UINT32 Type; ///< EFI memory type defined in UEFI specification. + UINT32 NumberOfPages; ///< The pages of this type memory. +} EFI_MEMORY_TYPE_INFORMATION; + +#endif + diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Include/Ppi/Capsule.h b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Include/Ppi/Capsule.h new file mode 100644 index 0000000000..b46056970b --- /dev/null +++ b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Include/Ppi/Capsule.h @@ -0,0 +1,125 @@ +/** @file + Defines the APIs that enable PEI services to work with + the underlying capsule capabilities of the platform. + + Copyright (c) 2006 - 2016, 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 _PEI_CAPSULE_PPI_H_ +#define _PEI_CAPSULE_PPI_H_ + +/// +/// Global ID for the PEI_CAPSULE_PPI. +/// +#define PEI_CAPSULE_PPI_GUID \ + { \ + 0x3acf33ee, 0xd892, 0x40f4, {0xa2, 0xfc, 0x38, 0x54, 0xd2, 0xe1, 0x32, 0x3d } \ + } + +/// +/// Forward declaration for the PEI_CAPSULE_PPI. +/// +typedef struct _PEI_CAPSULE_PPI PEI_CAPSULE_PPI; + +/** + Upon determining that there is a capsule to operate on, this service + will use a series of EFI_CAPSULE_BLOCK_DESCRIPTOR entries to determine + the current location of the various capsule fragments and coalesce them + into a contiguous region of system memory. + + @param[in] PeiServices Pointer to the PEI Services Table. + @param[out] MemoryBase Pointer to the base of a block of memory into which the buffers will be coalesced. + On output, this variable will hold the base address + of a coalesced capsule. + @param[out] MemorySize Size of the memory region pointed to by MemoryBase. + On output, this variable will contain the size of the + coalesced capsule. + + @retval EFI_NOT_FOUND If: boot modecould not be determined, or the + boot mode is not flash-update, or the capsule descriptors were not found. + @retval EFI_BUFFER_TOO_SMALL The capsule could not be coalesced in the provided memory region. + @retval EFI_SUCCESS There was no capsule, or the capsule was processed successfully. + +**/ +typedef +EFI_STATUS +(EFIAPI *PEI_CAPSULE_COALESCE)( + IN EFI_PEI_SERVICES **PeiServices, + IN OUT VOID **MemoryBase, + IN OUT UINTN *MemSize + ); + +/** + Determine if a capsule needs to be processed. + The means by which the presence of a capsule is determined is platform + specific. For example, an implementation could be driven by the presence + of a Capsule EFI Variable containing a list of EFI_CAPSULE_BLOCK_DESCRIPTOR + entries. If present, return EFI_SUCCESS, otherwise return EFI_NOT_FOUND. + + @param[in] PeiServices Pointer to the PEI Services Table. + + @retval EFI_SUCCESS If a capsule is available. + @retval EFI_NOT_FOUND No capsule detected. + +**/ +typedef +EFI_STATUS +(EFIAPI *PEI_CAPSULE_CHECK_CAPSULE_UPDATE)( + IN EFI_PEI_SERVICES **PeiServices + ); + +/** + The Capsule PPI service that gets called after memory is available. The + capsule coalesce function, which must be called first, returns a base + address and size. Once the memory init PEIM has discovered memory, + it should call this function and pass in the base address and size + returned by the Coalesce() function. Then this function can create a + capsule HOB and return. + + @par Notes: + This function assumes it will not be called until the + actual capsule update. + + @param[in] PeiServices Pointer to the PEI Services Table. + @param[in] CapsuleBase Address returned by the capsule coalesce function. + @param[in] CapsuleSize Value returned by the capsule coalesce function. + + @retval EFI_VOLUME_CORRUPTED CapsuleBase does not appear to point to a + coalesced capsule. + @retval EFI_SUCCESS Capsule HOB was created successfully. + +**/ +typedef +EFI_STATUS +(EFIAPI *PEI_CAPSULE_CREATE_STATE)( + IN EFI_PEI_SERVICES **PeiServices, + IN VOID *CapsuleBase, + IN UINTN CapsuleSize + ); + +/// +/// This PPI provides several services in PEI to work with the underlying +/// capsule capabilities of the platform. These services include the ability +/// for PEI to coalesce a capsule from a scattered set of memory locations +/// into a contiguous space in memory, detect if a capsule is present for +/// processing, and once memory is available, create a HOB for the capsule. +/// +struct _PEI_CAPSULE_PPI { + PEI_CAPSULE_COALESCE Coalesce; + PEI_CAPSULE_CHECK_CAPSULE_UPDATE CheckCapsuleUpdate; + PEI_CAPSULE_CREATE_STATE CreateState; +}; + +extern EFI_GUID gPeiCapsulePpiGuid; + +#endif // #ifndef _PEI_CAPSULE_PPI_H_ + diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Include/Ppi/SmmAccess.h b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Include/Ppi/SmmAccess.h new file mode 100644 index 0000000000..2a4af449f2 --- /dev/null +++ b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Include/Ppi/SmmAccess.h @@ -0,0 +1,145 @@ +/** @file + EFI SMM Access PPI definition. + + This PPI is used to control the visibility of the SMRAM on the platform. + It abstracts the location and characteristics of SMRAM. The expectation is + that the north bridge or memory controller would publish this PPI. + + The principal functionality found in the memory controller includes the following: + - Exposing the SMRAM to all non-SMM agents, or the "open" state + - Shrouding the SMRAM to all but the SMM agents, or the "closed" state + - Preserving the system integrity, or "locking" the SMRAM, such that the settings cannot be + perturbed by either boot service or runtime agents + + Copyright (c) 2010 - 2016, 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_ACCESS_PPI_H_ +#define _SMM_ACCESS_PPI_H_ + +#define PEI_SMM_ACCESS_PPI_GUID \ + { 0x268f33a9, 0xcccd, 0x48be, { 0x88, 0x17, 0x86, 0x5, 0x3a, 0xc3, 0x2e, 0xd6 }} + +typedef struct _PEI_SMM_ACCESS_PPI PEI_SMM_ACCESS_PPI; + +/** + Opens the SMRAM area to be accessible by a PEIM driver. + + This function "opens" SMRAM so that it is visible while not inside of SMM. The function should + return EFI_UNSUPPORTED if the hardware does not support hiding of SMRAM. The function + should return EFI_DEVICE_ERROR if the SMRAM configuration is locked. + + @param[in] PeiServices General purpose services available to every PEIM. + @param[in] This The pointer to the SMM Access Interface. + @param[in] DescriptorIndex The region of SMRAM to Open. + + @retval EFI_SUCCESS The region was successfully opened. + @retval EFI_DEVICE_ERROR The region could not be opened because locked by chipset. + @retval EFI_INVALID_PARAMETER The descriptor index was out of bounds. + +**/ +typedef +EFI_STATUS +(EFIAPI *PEI_SMM_OPEN)( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_SMM_ACCESS_PPI *This, + IN UINTN DescriptorIndex + ); + +/** + Inhibits access to the SMRAM. + + This function "closes" SMRAM so that it is not visible while outside of SMM. The function should + return EFI_UNSUPPORTED if the hardware does not support hiding of SMRAM. + + @param[in] PeiServices General purpose services available to every PEIM. + @param[in] This The pointer to the SMM Access Interface. + @param[in] DescriptorIndex The region of SMRAM to Close. + + @retval EFI_SUCCESS The region was successfully closed. + @retval EFI_DEVICE_ERROR The region could not be closed because locked by chipset. + @retval EFI_INVALID_PARAMETER The descriptor index was out of bounds. + +**/ +typedef +EFI_STATUS +(EFIAPI *PEI_SMM_CLOSE)( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_SMM_ACCESS_PPI *This, + IN UINTN DescriptorIndex + ); + +/** + Inhibits access to the SMRAM. + + This function prohibits access to the SMRAM region. This function is usually implemented such + that it is a write-once operation. + + @param[in] PeiServices General purpose services available to every PEIM. + @param[in] This The pointer to the SMM Access Interface. + @param[in] DescriptorIndex The region of SMRAM to Close. + + @retval EFI_SUCCESS The region was successfully locked. + @retval EFI_DEVICE_ERROR The region could not be locked because at least + one range is still open. + @retval EFI_INVALID_PARAMETER The descriptor index was out of bounds. + +**/ +typedef +EFI_STATUS +(EFIAPI *PEI_SMM_LOCK)( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_SMM_ACCESS_PPI *This, + IN UINTN DescriptorIndex + ); + +/** + Queries the memory controller for the possible regions that will support SMRAM. + + @param[in] PeiServices General purpose services available to every PEIM. + @param[in] This The pointer to the SmmAccessPpi Interface. + @param[in, out] SmramMapSize The pointer to the variable containing size of the + buffer to contain the description information. + @param[in, out] SmramMap The buffer containing the data describing the Smram + region descriptors. + + @retval EFI_BUFFER_TOO_SMALL The user did not provide a sufficient buffer. + @retval EFI_SUCCESS The user provided a sufficiently-sized buffer. + +**/ +typedef +EFI_STATUS +(EFIAPI *PEI_SMM_CAPABILITIES)( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_SMM_ACCESS_PPI *This, + IN OUT UINTN *SmramMapSize, + IN OUT EFI_SMRAM_DESCRIPTOR *SmramMap + ); + +/// +/// EFI SMM Access PPI is used to control the visibility of the SMRAM on the platform. +/// It abstracts the location and characteristics of SMRAM. The expectation is +/// that the north bridge or memory controller would publish this PPI. +/// +struct _PEI_SMM_ACCESS_PPI { + PEI_SMM_OPEN Open; + PEI_SMM_CLOSE Close; + PEI_SMM_LOCK Lock; + PEI_SMM_CAPABILITIES GetCapabilities; + BOOLEAN LockState; + BOOLEAN OpenState; +}; + +extern EFI_GUID gPeiSmmAccessPpiGuid; + +#endif + diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Include/Ppi/SmmControl.h b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Include/Ppi/SmmControl.h new file mode 100644 index 0000000000..e7b67c5a6f --- /dev/null +++ b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Include/Ppi/SmmControl.h @@ -0,0 +1,95 @@ +/** @file + EFI SMM Control PPI definition. + + This PPI is used to initiate SMI/PMI activations. This protocol could be published by either: + - A processor driver to abstract the SMI/PMI IPI + - The driver that abstracts the ASIC that is supporting the APM port, such as the ICH in an + Intel chipset + Because of the possibility of performing SMI or PMI IPI transactions, the ability to generate this + event from a platform chipset agent is an optional capability for both IA-32 and Itanium-based + systems. + + Copyright (c) 2010 - 2016, 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_CONTROL_PPI_H_ +#define _SMM_CONTROL_PPI_H_ + +#define PEI_SMM_CONTROL_PPI_GUID \ + { 0x61c68702, 0x4d7e, 0x4f43, 0x8d, 0xef, 0xa7, 0x43, 0x5, 0xce, 0x74, 0xc5 } + +typedef struct _PEI_SMM_CONTROL_PPI PEI_SMM_CONTROL_PPI; + +/** + Invokes SMI activation from either the preboot or runtime environment. + + @param[in] PeiServices General purpose services available to every PEIM. + @param[in] This The PEI_SMM_CONTROL_PPI instance. + @param[in] ArgumentBuffer The optional sized data to pass into the protocol activation. + @param[in] ArgumentBufferSize The optional size of the data. + @param[in] Periodic An optional mechanism to periodically repeat activation. + @param[in] ActivationInterval An optional parameter to repeat at this period one + time or, if the Periodic Boolean is set, periodically. + + @retval EFI_SUCCESS The SMI/PMI has been engendered. + @retval EFI_DEVICE_ERROR The timing is unsupported. + @retval EFI_INVALID_PARAMETER The activation period is unsupported. + @retval EFI_NOT_STARTED The SMM base service has not been initialized. + +**/ +typedef +EFI_STATUS +(EFIAPI *PEI_SMM_ACTIVATE) ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_SMM_CONTROL_PPI * This, + IN OUT INT8 *ArgumentBuffer OPTIONAL, + IN OUT UINTN *ArgumentBufferSize OPTIONAL, + IN BOOLEAN Periodic OPTIONAL, + IN UINTN ActivationInterval OPTIONAL + ); + +/** + Clears any system state that was created in response to the Active call. + + @param[in] PeiServices General purpose services available to every PEIM. + @param[in] This The PEI_SMM_CONTROL_PPI instance. + @param[in] Periodic Optional parameter to repeat at this period one + time or, if the Periodic Boolean is set, periodically. + + @retval EFI_SUCCESS The SMI/PMI has been engendered. + @retval EFI_DEVICE_ERROR The source could not be cleared. + @retval EFI_INVALID_PARAMETER The service did not support the Periodic input argument. + +**/ +typedef +EFI_STATUS +(EFIAPI *PEI_SMM_DEACTIVATE) ( + IN EFI_PEI_SERVICES **PeiServices, + IN PEI_SMM_CONTROL_PPI * This, + IN BOOLEAN Periodic OPTIONAL + ); + +/// +/// PEI SMM Control PPI is used to initiate SMI/PMI activations. This protocol could be published by either: +/// - A processor driver to abstract the SMI/PMI IPI +/// - The driver that abstracts the ASIC that is supporting the APM port, such as the ICH in an +/// Intel chipset +/// +struct _PEI_SMM_CONTROL_PPI { + PEI_SMM_ACTIVATE Trigger; + PEI_SMM_DEACTIVATE Clear; +}; + +extern EFI_GUID gPeiSmmControlPpiGuid; + +#endif + diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Include/Protocol/SmmVariable.h b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Include/Protocol/SmmVariable.h new file mode 100644 index 0000000000..f46073c561 --- /dev/null +++ b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Include/Protocol/SmmVariable.h @@ -0,0 +1,41 @@ +/** @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 - 2016, 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/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/Pei/PeiVariable.uni b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/Pei/PeiVariable.uni new file mode 100644 index 0000000000..91194881dd Binary files /dev/null and b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/Pei/PeiVariable.uni differ diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/Pei/PeiVariableExtra.uni b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/Pei/PeiVariableExtra.uni new file mode 100644 index 0000000000..b52ff23520 Binary files /dev/null and b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/Pei/PeiVariableExtra.uni differ diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/Pei/Variable.c b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/Pei/Variable.c new file mode 100644 index 0000000000..f6c4227069 --- /dev/null +++ b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/Pei/Variable.c @@ -0,0 +1,1383 @@ +/** @file + Implement ReadOnly Variable Services required by PEIM and install + PEI ReadOnly Varaiable2 PPI. These services operates the non volatile storage space. + + Copyright (c) 2006 - 2016, 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" + +// +// Module globals +// +EFI_PEI_READ_ONLY_VARIABLE2_PPI mVariablePpi = { + PeiGetVariable, + PeiGetNextVariableName +}; + +EFI_PEI_PPI_DESCRIPTOR mPpiListVariable = { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEfiPeiReadOnlyVariable2PpiGuid, + &mVariablePpi +}; + + +/** + Provide the functionality of the variable services. + + @param[in] FileHandle Handle of the file being invoked. + Type EFI_PEI_FILE_HANDLE is defined in FfsFindNextFile(). + @param[in] PeiServices General purpose services available to every PEIM. + + @retval EFI_SUCCESS If the interface could be successfully installed + @retval Others Returned from PeiServicesInstallPpi() + +**/ +EFI_STATUS +EFIAPI +PeimInitializeVariableServices ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + UINTN Instance; + READ_ONLY_VARIABLE_PRE_MEMORY_DESCRIPTOR_PPI *PreMemoryDescriptorPpi; + EFI_PEI_PPI_DESCRIPTOR *PreMemoryDescriptorPpiDesc; + EFI_PEI_READ_ONLY_VARIABLE2_PPI *OldReadOnlyVariable2Ppi; + EFI_PEI_PPI_DESCRIPTOR *OldDescriptor; + BOOLEAN InPermanentMemory; + + InPermanentMemory = FALSE; + Status = PeiServicesRegisterForShadow (FileHandle); + if (EFI_ERROR (Status)) { + if (Status == EFI_ALREADY_STARTED) { + InPermanentMemory = TRUE; + } else { + ASSERT_EFI_ERROR (Status); + return Status; + } + } + + // + // The PI spec does not require that there only be one instance of + // EFI_PEI_READ_ONLY_VARIABLE2_PPI. Therefore one must assume that a + // scenario exists where there is a platform specific instance of + // EFI_PEI_READ_ONLY_VARIABLE2_PPI. Therefore, this driver must keep track + // of which instance of EFI_PEI_READ_ONLY_VARIABLE2_PPI it installs so that + // the correct instance is reinstalled after PEI is shadowed to permanent + // memory. + // + if (!InPermanentMemory) { + // + // If system is still in Pre-Memory, store the temporary address to the + // PPI descriptor in a separate unique PPI. Then when permanent memory is + // installed, the old PPI instance can be located and reinstalled with the + // new address + // + PreMemoryDescriptorPpi = (READ_ONLY_VARIABLE_PRE_MEMORY_DESCRIPTOR_PPI *) + AllocateZeroPool ( + sizeof ( + READ_ONLY_VARIABLE_PRE_MEMORY_DESCRIPTOR_PPI + ) + ); + PreMemoryDescriptorPpiDesc = (EFI_PEI_PPI_DESCRIPTOR *) + AllocateZeroPool ( + sizeof (EFI_PEI_PPI_DESCRIPTOR) + ); + if (PreMemoryDescriptorPpiDesc == NULL || PreMemoryDescriptorPpi == NULL) { + ASSERT (PreMemoryDescriptorPpiDesc != NULL); + ASSERT (PreMemoryDescriptorPpi != NULL); + return EFI_OUT_OF_RESOURCES; + } + PreMemoryDescriptorPpi->PreMemoryDescriptor = &mPpiListVariable; + PreMemoryDescriptorPpiDesc->Flags = EFI_PEI_PPI_DESCRIPTOR_PPI | + EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST; + PreMemoryDescriptorPpiDesc->Guid = &gReadOnlyVariablePreMemoryDescriptorPpiGuid; + PreMemoryDescriptorPpiDesc->Ppi = PreMemoryDescriptorPpi; + Status = PeiServicesInstallPpi (PreMemoryDescriptorPpiDesc); + if (EFI_ERROR (Status)) { + ASSERT_EFI_ERROR (Status); + return Status; + } + // + // Now that the descriptor address is saved, + // install EFI_PEI_READ_ONLY_VARIABLE2_PPI + // + Status = PeiServicesInstallPpi (&mPpiListVariable); + } else { + Status = PeiServicesLocatePpi ( + &gReadOnlyVariablePreMemoryDescriptorPpiGuid, + 0, + NULL, + (VOID **) &PreMemoryDescriptorPpi + ); + if (!EFI_ERROR (Status)) { + // + // The previous descriptor pointer has been found, locate the old PPI + // and reinstall it. + // + for (Instance = 0; Status != EFI_NOT_FOUND; Instance++) { + Status = PeiServicesLocatePpi ( + &gEfiPeiReadOnlyVariable2PpiGuid, + Instance, + &OldDescriptor, + (VOID **) &OldReadOnlyVariable2Ppi + ); + if (!EFI_ERROR (Status)) { + if (OldDescriptor == PreMemoryDescriptorPpi->PreMemoryDescriptor) { + Status = PeiServicesReInstallPpi (OldDescriptor, &mPpiListVariable); + ASSERT_EFI_ERROR (Status); + return Status; + } + } else if (Status != EFI_NOT_FOUND) { + ASSERT_EFI_ERROR (Status); + return Status; + } + } + // + // This implementation of EFI_PEI_READ_ONLY_VARIABLE2_PPI + // was never installed. Install it for the first time + // + Status = PeiServicesInstallPpi (&mPpiListVariable); + } else { + // + // This implementation of EFI_PEI_READ_ONLY_VARIABLE2_PPI + // was never installed. Install it for the first time + // + Status = PeiServicesInstallPpi (&mPpiListVariable); + } + } + + if (EFI_ERROR (Status)) { + ASSERT_EFI_ERROR (Status); + return Status; + } + + return EFI_SUCCESS; +} + + +/** + Gets the pointer to the first variable header in given variable store area. + + @param[in] VarStoreHeader Pointer to the Variable Store Header. + + @return Pointer to the first variable header + +**/ +VARIABLE_HEADER * +GetStartPointer ( + IN VARIABLE_STORE_HEADER *VarStoreHeader + ) +{ + // + // The end of variable store + // + return (VARIABLE_HEADER *) HEADER_ALIGN (VarStoreHeader + 1); +} + + +/** + This code gets the pointer to the last variable memory pointer byte. + + @param[in] VarStoreHeader Pointer to the Variable Store Header. + + @return VARIABLE_HEADER* pointer to last unavailable Variable Header. + +**/ +VARIABLE_HEADER * +GetEndPointer ( + IN VARIABLE_STORE_HEADER *VarStoreHeader + ) +{ + // + // The end of variable store + // + return (VARIABLE_HEADER *) HEADER_ALIGN ((UINTN) VarStoreHeader + VarStoreHeader->Size); +} + + +/** + This code checks if variable header is valid or not. + + @param[in] Variable Pointer to the Variable Header. + + @retval TRUE Variable header is valid. + @retval FALSE Variable header is not valid. + +**/ +BOOLEAN +IsValidVariableHeader ( + IN VARIABLE_HEADER *Variable + ) +{ + if (Variable == NULL || Variable->StartId != VARIABLE_DATA ) { + return FALSE; + } + + return TRUE; +} + + +/** + This code gets the size of variable header. + + @param[in] AuthFlag Authenticated variable flag. + + @return Size of variable header in bytes in type UINTN. + +**/ +UINTN +GetVariableHeaderSize ( + IN BOOLEAN AuthFlag + ) +{ + UINTN Value; + + if (AuthFlag) { + Value = sizeof (AUTHENTICATED_VARIABLE_HEADER); + } else { + Value = sizeof (VARIABLE_HEADER); + } + + return Value; +} + + +/** + This code gets the size of name of variable. + + @param[in] Variable Pointer to the Variable Header. + @param[in] AuthFlag Authenticated variable flag. + + @return Size of variable in bytes in type UINTN. + +**/ +UINTN +NameSizeOfVariable ( + IN VARIABLE_HEADER *Variable, + IN BOOLEAN AuthFlag + ) +{ + AUTHENTICATED_VARIABLE_HEADER *AuthVariable; + + AuthVariable = (AUTHENTICATED_VARIABLE_HEADER *) Variable; + if (AuthFlag) { + if (AuthVariable->State == (UINT8) (-1) || + AuthVariable->DataSize == (UINT32) (-1) || + AuthVariable->NameSize == (UINT32) (-1) || + AuthVariable->Attributes == (UINT32) (-1)) { + return 0; + } + return (UINTN) AuthVariable->NameSize; + } else { + if (Variable->State == (UINT8) (-1) || + Variable->DataSize == (UINT32) (-1) || + Variable->NameSize == (UINT32) (-1) || + Variable->Attributes == (UINT32) (-1)) { + return 0; + } + return (UINTN) Variable->NameSize; + } +} + + +/** + This code gets the size of data of variable. + + @param[in] Variable Pointer to the Variable Header. + @param[in] AuthFlag Authenticated variable flag. + + @return Size of variable in bytes in type UINTN. + +**/ +UINTN +DataSizeOfVariable ( + IN VARIABLE_HEADER *Variable, + IN BOOLEAN AuthFlag + ) +{ + AUTHENTICATED_VARIABLE_HEADER *AuthVariable; + + AuthVariable = (AUTHENTICATED_VARIABLE_HEADER *) Variable; + if (AuthFlag) { + if (AuthVariable->State == (UINT8) (-1) || + AuthVariable->DataSize == (UINT32) (-1) || + AuthVariable->NameSize == (UINT32) (-1) || + AuthVariable->Attributes == (UINT32) (-1)) { + return 0; + } + return (UINTN) AuthVariable->DataSize; + } else { + if (Variable->State == (UINT8) (-1) || + Variable->DataSize == (UINT32) (-1) || + Variable->NameSize == (UINT32) (-1) || + Variable->Attributes == (UINT32) (-1)) { + return 0; + } + return (UINTN) Variable->DataSize; + } +} + + +/** + This code gets the pointer to the variable name. + + @param[in] Variable Pointer to the Variable Header. + @param[in] AuthFlag Authenticated variable flag. + + @return A CHAR16* pointer to Variable Name. + +**/ +CHAR16 * +GetVariableNamePtr ( + IN VARIABLE_HEADER *Variable, + IN BOOLEAN AuthFlag + ) +{ + return (CHAR16 *) ((UINTN) Variable + GetVariableHeaderSize (AuthFlag)); +} + + +/** + This code gets the pointer to the variable guid. + + @param[in] Variable Pointer to the Variable Header. + @param[in] AuthFlag Authenticated variable flag. + + @return A EFI_GUID* pointer to Vendor Guid. + +**/ +EFI_GUID * +GetVendorGuidPtr ( + IN VARIABLE_HEADER *Variable, + IN BOOLEAN AuthFlag + ) +{ + AUTHENTICATED_VARIABLE_HEADER *AuthVariable; + + AuthVariable = (AUTHENTICATED_VARIABLE_HEADER *) Variable; + if (AuthFlag) { + return &AuthVariable->VendorGuid; + } else { + return &Variable->VendorGuid; + } +} + + +/** + This code gets the pointer to the variable data. + + @param[in] Variable Pointer to the Variable Header. + @param[in] VariableHeader Pointer to the Variable Header that has consecutive content. + @param[in] AuthFlag Authenticated variable flag. + + @return A UINT8* pointer to Variable Data. + +**/ +UINT8 * +GetVariableDataPtr ( + IN VARIABLE_HEADER *Variable, + IN VARIABLE_HEADER *VariableHeader, + IN BOOLEAN AuthFlag + ) +{ + UINTN Value; + + // + // Be careful about pad size for alignment + // + Value = (UINTN) GetVariableNamePtr (Variable, AuthFlag); + Value += NameSizeOfVariable (VariableHeader, AuthFlag); + Value += GET_PAD_SIZE (NameSizeOfVariable (VariableHeader, AuthFlag)); + + return (UINT8 *) Value; +} + + +/** + This code gets the pointer to the next variable header. + + @param[in] StoreInfo Pointer to variable store info structure. + @param[in] Variable Pointer to the Variable Header. + @param[in] VariableHeader Pointer to the Variable Header that has consecutive content. + + @return A VARIABLE_HEADER* pointer to next variable header. + +**/ +VARIABLE_HEADER * +GetNextVariablePtr ( + IN VARIABLE_STORE_INFO *StoreInfo, + IN VARIABLE_HEADER *Variable, + IN VARIABLE_HEADER *VariableHeader + ) +{ + UINTN Value; + + Value = (UINTN) GetVariableDataPtr (Variable, VariableHeader, StoreInfo->AuthFlag); + Value += DataSizeOfVariable (VariableHeader, StoreInfo->AuthFlag); + Value += GET_PAD_SIZE (DataSizeOfVariable (VariableHeader, StoreInfo->AuthFlag)); + // + // Be careful about pad size for alignment + // + Value = HEADER_ALIGN (Value); + + return (VARIABLE_HEADER *) Value; +} + + +/** + Get variable store status. + + @param[in] VarStoreHeader Pointer to the Variable Store Header. + + @retval EfiRaw Variable store is raw + @retval EfiValid Variable store is valid + @retval EfiInvalid Variable store is invalid + +**/ +VARIABLE_STORE_STATUS +GetVariableStoreStatus ( + IN VARIABLE_STORE_HEADER *VarStoreHeader + ) +{ + if ((CompareGuid (&VarStoreHeader->Signature, &gPeiVariableCacheHobGuid) || + CompareGuid (&VarStoreHeader->Signature, &gEfiAuthenticatedVariableGuid) || + CompareGuid (&VarStoreHeader->Signature, &gEfiVariableGuid)) && + VarStoreHeader->Format == VARIABLE_STORE_FORMATTED && + VarStoreHeader->State == VARIABLE_STORE_HEALTHY + ) { + + return EfiValid; + } + + if (((UINT32 *) (&VarStoreHeader->Signature))[0] == 0xffffffff && + ((UINT32 *) (&VarStoreHeader->Signature))[1] == 0xffffffff && + ((UINT32 *) (&VarStoreHeader->Signature))[2] == 0xffffffff && + ((UINT32 *) (&VarStoreHeader->Signature))[3] == 0xffffffff && + VarStoreHeader->Size == 0xffffffff && + VarStoreHeader->Format == 0xff && + VarStoreHeader->State == 0xff + ) { + return EfiRaw; + } else { + return EfiInvalid; + } +} + + +/** + Compare two variable names, one of them may be inconsecutive. + + @param[in] StoreInfo Pointer to variable store info structure. + @param[in] Name1 Pointer to one variable name. + @param[in] Name2 Pointer to another variable name. + @param[in] NameSize Variable name size. + + @retval TRUE Name1 and Name2 are identical. + @retval FALSE Name1 and Name2 are not identical. + +**/ +BOOLEAN +CompareVariableName ( + IN VARIABLE_STORE_INFO *StoreInfo, + IN CONST CHAR16 *Name1, + IN CONST CHAR16 *Name2, + IN UINTN NameSize + ) +{ + + // + // Both Name1 and Name2 are consecutive. + // + if (CompareMem ((UINT8 *) Name1, (UINT8 *) Name2, NameSize) == 0) { + return TRUE; + } + return FALSE; +} + + +/** + This function compares a variable with variable entries in database. + + @param[in] StoreInfo Pointer to variable store info structure. + @param[in] Variable Pointer to the variable in our database + @param[in] VariableHeader Pointer to the Variable Header that has consecutive content. + @param[in] VariableName Name of the variable to compare to 'Variable' + @param[in] VendorGuid GUID of the variable to compare to 'Variable' + @param[out] PtrTrack Variable Track Pointer structure that contains Variable Information. + + @retval EFI_SUCCESS Found match variable + @retval EFI_NOT_FOUND Variable not found + +**/ +EFI_STATUS +CompareWithValidVariable ( + IN VARIABLE_STORE_INFO *StoreInfo, + IN VARIABLE_HEADER *Variable, + IN VARIABLE_HEADER *VariableHeader, + IN CONST CHAR16 *VariableName, + IN CONST EFI_GUID *VendorGuid, + OUT VARIABLE_POINTER_TRACK *PtrTrack + ) +{ + VOID *Point; + EFI_GUID *TempVendorGuid; + + TempVendorGuid = GetVendorGuidPtr (VariableHeader, StoreInfo->AuthFlag); + + if (VariableName[0] == 0) { + PtrTrack->CurrPtr = Variable; + return EFI_SUCCESS; + } else { + // + // Don't use CompareGuid function here for performance reasons. + // Instead we compare the GUID a UINT32 at a time and branch + // on the first failed comparison. + // + if ((((INT32 *) VendorGuid)[0] == ((INT32 *) TempVendorGuid)[0]) && + (((INT32 *) VendorGuid)[1] == ((INT32 *) TempVendorGuid)[1]) && + (((INT32 *) VendorGuid)[2] == ((INT32 *) TempVendorGuid)[2]) && + (((INT32 *) VendorGuid)[3] == ((INT32 *) TempVendorGuid)[3]) + ) { + ASSERT (NameSizeOfVariable (VariableHeader, StoreInfo->AuthFlag) != 0); + Point = (VOID *) GetVariableNamePtr (Variable, StoreInfo->AuthFlag); + if (CompareVariableName (StoreInfo, VariableName, Point, NameSizeOfVariable (VariableHeader, StoreInfo->AuthFlag))) { + PtrTrack->CurrPtr = Variable; + return EFI_SUCCESS; + } + } + } + + return EFI_NOT_FOUND; +} + + +/** + Return the variable HOB header and the store info for the given HOB type + + @param[in] Type The type of the variable HOB. + @param[out] StoreInfo Return the store info. + + @return Pointer to the variable HOB header. + +**/ +VARIABLE_STORE_HEADER * +GetHobVariableStore ( + IN VARIABLE_HOB_TYPE Type, + OUT VARIABLE_STORE_INFO *StoreInfo + ) +{ + EFI_HOB_GUID_TYPE *GuidHob; + + StoreInfo->IndexTable = NULL; + StoreInfo->AuthFlag = FALSE; + + switch (Type) { + case VariableHobTypeCache: + GuidHob = GetFirstGuidHob (&gPeiVariableCacheHobGuid); + if (GuidHob != NULL) { + StoreInfo->AuthFlag = FALSE; + StoreInfo->VariableStoreHeader = (VARIABLE_STORE_HEADER *) GET_GUID_HOB_DATA (GuidHob); + return StoreInfo->VariableStoreHeader; + } + break; + case VariableHobTypeDefault: + GuidHob = GetFirstGuidHob (&gEfiAuthenticatedVariableGuid); + if (GuidHob != NULL) { + StoreInfo->AuthFlag = TRUE; + StoreInfo->VariableStoreHeader = (VARIABLE_STORE_HEADER *) GET_GUID_HOB_DATA (GuidHob); + return StoreInfo->VariableStoreHeader; + } + GuidHob = GetFirstGuidHob (&gEfiVariableGuid); + if (GuidHob != NULL) { + StoreInfo->AuthFlag = FALSE; + StoreInfo->VariableStoreHeader = (VARIABLE_STORE_HEADER *) GET_GUID_HOB_DATA (GuidHob); + return StoreInfo->VariableStoreHeader; + } + break; + } + return NULL; +} + + +/** + Get variable header that has consecutive content. + + @param[in] StoreInfo Pointer to variable store info structure. + @param[in] Variable Pointer to the Variable Header. + @param[out] VariableHeader Pointer to Pointer to the Variable Header that has consecutive content. + + @retval TRUE Variable header is valid. + @retval FALSE Variable header is not valid. + +**/ +BOOLEAN +GetVariableHeader ( + IN VARIABLE_STORE_INFO *StoreInfo, + IN VARIABLE_HEADER *Variable, + OUT VARIABLE_HEADER **VariableHeader + ) +{ + + if (Variable == NULL) { + return FALSE; + } + + // + // First assume variable header pointed by Variable is consecutive. + // + *VariableHeader = Variable; + + if (Variable >= GetEndPointer (StoreInfo->VariableStoreHeader)) { + // + // Reach the end of variable store. + // + return FALSE; + } + + return IsValidVariableHeader (*VariableHeader); +} + + +/** + Get variable name or data to output buffer. + + @param[in] StoreInfo Pointer to variable store info structure. + @param[in] NameOrData Pointer to the variable name/data that may be inconsecutive. + @param[in] Size Variable name/data size. + @param[out] Buffer Pointer to output buffer to hold the variable name/data. + +**/ +VOID +GetVariableNameOrData ( + IN VARIABLE_STORE_INFO *StoreInfo, + IN UINT8 *NameOrData, + IN UINTN Size, + OUT UINT8 *Buffer + ) +{ + + // + // Variable name/data is consecutive. + // + CopyMem (Buffer, NameOrData, Size); +} + + +/** + Find the variable in the specified variable store. + + @param[in] StoreInfo Pointer to the store info structure. + @param[in] VariableName Name of the variable to be found + @param[in] VendorGuid Vendor GUID to be found. + @param[out] PtrTrack Variable Track Pointer structure that contains Variable Information. + + @retval EFI_SUCCESS Variable found successfully + @retval EFI_NOT_FOUND Variable not found + @retval EFI_INVALID_PARAMETER Invalid variable name + +**/ +EFI_STATUS +FindVariableInHobsInternal ( + IN VARIABLE_STORE_INFO *StoreInfo, + IN CONST CHAR16 *VariableName, + IN CONST EFI_GUID *VendorGuid, + OUT VARIABLE_POINTER_TRACK *PtrTrack + ) +{ + VARIABLE_HEADER *Variable; + VARIABLE_HEADER *InDeletedVariable; + VARIABLE_STORE_HEADER *VariableStoreHeader; + VARIABLE_HEADER *VariableHeader; + + VariableStoreHeader = StoreInfo->VariableStoreHeader; + + if (VariableStoreHeader == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (GetVariableStoreStatus (VariableStoreHeader) != EfiValid) { + return EFI_UNSUPPORTED; + } + + if (~VariableStoreHeader->Size == 0) { + return EFI_NOT_FOUND; + } + + PtrTrack->StartPtr = GetStartPointer (VariableStoreHeader); + PtrTrack->EndPtr = GetEndPointer (VariableStoreHeader); + + InDeletedVariable = NULL; + + // + // No Variable Address equals zero, so 0 as initial value is safe. + // + VariableHeader = NULL; + + // + // Start at the beginning of the HOB + // + Variable = PtrTrack->StartPtr; + + // + // Find the variable by walking through the variable store + // + while (GetVariableHeader (StoreInfo, Variable, &VariableHeader)) { + if (VariableHeader->State == VAR_ADDED || VariableHeader->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED)) { + if (CompareWithValidVariable (StoreInfo, Variable, VariableHeader, VariableName, VendorGuid, PtrTrack) == EFI_SUCCESS) { + if (VariableHeader->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED)) { + InDeletedVariable = PtrTrack->CurrPtr; + } else { + return EFI_SUCCESS; + } + } + } + Variable = GetNextVariablePtr (StoreInfo, Variable, VariableHeader); + } + PtrTrack->CurrPtr = InDeletedVariable; + + return (PtrTrack->CurrPtr == NULL) ? EFI_NOT_FOUND : EFI_SUCCESS; +} + + +/** + Find the variable in HOB variable storages. + + @param[in] VariableName Name of the variable to be found + @param[in] VendorGuid Vendor GUID to be found. + @param[out] PtrTrack Variable Track Pointer structure that contains Variable Information. + @param[out] StoreInfo Return the store info. + + @retval EFI_SUCCESS Variable found successfully + @retval EFI_NOT_FOUND Variable not found + @retval EFI_INVALID_PARAMETER Invalid variable name + +**/ +EFI_STATUS +FindVariableInHobs ( + IN CONST CHAR16 *VariableName, + IN CONST EFI_GUID *VendorGuid, + OUT VARIABLE_POINTER_TRACK *PtrTrack, + OUT VARIABLE_STORE_INFO *StoreInfo + ) +{ + EFI_STATUS Status; + VARIABLE_HOB_TYPE Type; + + if (VariableName[0] != 0 && VendorGuid == NULL) { + return EFI_INVALID_PARAMETER; + } + + for (Type = (VARIABLE_HOB_TYPE) 0; Type < VariableHobTypeMax; Type++) { + if (GetHobVariableStore (Type, StoreInfo) != NULL) { + Status = FindVariableInHobsInternal ( + StoreInfo, + VariableName, + VendorGuid, + PtrTrack + ); + if (!EFI_ERROR (Status)) { + return Status; + } + } + } + + return EFI_NOT_FOUND; +} + + +/** + Retrieves a variable value from HOB resources. + + Read the specified variable from the UEFI variable store in the HOB. If the Data + buffer is too small to hold the contents of the variable, the error + EFI_BUFFER_TOO_SMALL is returned and DataSize is set to the required buffer + size to obtain the data. + + @param[in] VariableName A pointer to a null-terminated string that is the variable's name. + @param[in] VariableGuid A pointer to an EFI_GUID that is the variable's GUID. The combination of + VariableGuid and VariableName must be unique. + @param[out] Attributes If non-NULL, on return, points to the variable's attributes. + @param[in, out] DataSize On entry, points to the size in bytes of the Data buffer. + On return, points to the size of the data returned in Data. + @param[out] Data Points to the buffer which will hold the returned variable value. + + @retval EFI_SUCCESS The variable was read successfully. + @retval EFI_NOT_FOUND The variable could not be found. + @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the resulting data. + DataSize is updated with the size required for + the specified variable. + @retval EFI_INVALID_PARAMETER VariableName, VariableGuid, DataSize or Data is NULL. + +**/ +EFI_STATUS +EFIAPI +GetHobVariable ( + IN CONST CHAR16 *VariableName, + IN CONST EFI_GUID *VariableGuid, + OUT UINT32 *Attributes, + IN OUT UINTN *DataSize, + OUT VOID *Data + ) +{ + EFI_STATUS Status; + UINTN HobVariableDataSize; + VARIABLE_HEADER *HobVariableHeader; + VARIABLE_HOB_TYPE Type; + VARIABLE_POINTER_TRACK HobVariable; + VARIABLE_STORE_INFO HobStoreInfo; + + // + // Check the HOB variable stores if they exist + // + for (Type = (VARIABLE_HOB_TYPE) 0; Type < VariableHobTypeMax; Type++) { + if (GetHobVariableStore (Type, &HobStoreInfo) != NULL) { + DEBUG ((EFI_D_INFO, "Temp Debug: Found a HOB variable store in PeiGetVariable()\n")); + Status = FindVariableInHobsInternal ( + &HobStoreInfo, + VariableName, + VariableGuid, + &HobVariable + ); + if (HobVariable.CurrPtr == NULL || EFI_ERROR (Status)) { + if (Status == EFI_NOT_FOUND) { + // + // Check the next HOB + // + continue; + } + DEBUG ((EFI_D_ERROR, "Temp Debug: HobVariable found was null or had an error\n")); + return Status; + } + + GetVariableHeader (&HobStoreInfo, HobVariable.CurrPtr, &HobVariableHeader); + + // + // Get data size + // + HobVariableDataSize = DataSizeOfVariable (HobVariableHeader, HobStoreInfo.AuthFlag); + if (*DataSize >= HobVariableDataSize) { + DEBUG ((EFI_D_INFO, "Temp Debug: Data buffer passed for variable data is large enough\n")); + if (Data == NULL) { + DEBUG ((EFI_D_ERROR, "Temp Debug: Data in the variable is NULL (invalid)\n")); + return EFI_INVALID_PARAMETER; + } + + GetVariableNameOrData (&HobStoreInfo, GetVariableDataPtr (HobVariable.CurrPtr, HobVariableHeader, HobStoreInfo.AuthFlag), HobVariableDataSize, Data); + + if (Attributes != NULL) { + *Attributes = HobVariableHeader->Attributes; + } + + *DataSize = HobVariableDataSize; + + DEBUG ((EFI_D_INFO, "Temp Debug: Variable was found in the HOB. No need to check storage PPIs. Returning success.\n\n")); + + // + // Variable was found in the HOB + // + return EFI_SUCCESS; + } else { + DEBUG ((EFI_D_ERROR, "Temp Debug: Data buffer provided for variable data is too small!\n")); + *DataSize = HobVariableDataSize; + return EFI_BUFFER_TOO_SMALL; + } + } + } + + return EFI_NOT_FOUND; +} + + +/** + This service retrieves a variable's value using its name and GUID. + + Read the specified variable from the UEFI variable store. If the Data + buffer is too small to hold the contents of the variable, the error + EFI_BUFFER_TOO_SMALL is returned and DataSize is set to the required buffer + size to obtain the data. + + @param[in] This A pointer to this instance of the EFI_PEI_READ_ONLY_VARIABLE2_PPI. + @param[in] VariableName A pointer to a null-terminated string that is the variable's name. + @param[in] VariableGuid A pointer to an EFI_GUID that is the variable's GUID. The combination of + VariableGuid and VariableName must be unique. + @param[out] Attributes If non-NULL, on return, points to the variable's attributes. + @param[in, out] DataSize On entry, points to the size in bytes of the Data buffer. + On return, points to the size of the data returned in Data. + @param[out] Data Points to the buffer which will hold the returned variable value. + + @retval EFI_SUCCESS The variable was read successfully. + @retval EFI_NOT_FOUND The variable could not be found. + @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the resulting data. + DataSize is updated with the size required for + the specified variable. + @retval EFI_INVALID_PARAMETER VariableName, VariableGuid, DataSize or Data is NULL. + @retval EFI_DEVICE_ERROR The variable could not be retrieved because of a device error. + +**/ +EFI_STATUS +EFIAPI +PeiGetVariable ( + IN CONST EFI_PEI_READ_ONLY_VARIABLE2_PPI *This, + IN CONST CHAR16 *VariableName, + IN CONST EFI_GUID *VariableGuid, + OUT UINT32 *Attributes, + IN OUT UINTN *DataSize, + OUT VOID *Data + ) +{ + EFI_GUID DiscoveredInstanceGuid; + EFI_GUID VariableStorageInstanceId; + EFI_PEI_PPI_DESCRIPTOR *VariableStoragePpiDescriptor; + EFI_STATUS Status; + UINTN Instance; + VARIABLE_STORAGE_PPI *VariableStoragePpi; + + if (VariableName == NULL || VariableGuid == NULL || DataSize == NULL) { + DEBUG ((EFI_D_ERROR, "Temp Debug: Invalid parameter passed to PeiGetVariable()\n")); + return EFI_INVALID_PARAMETER; + } + + // + // Check if the variable can be found in a HOB store + // + Status = GetHobVariable (VariableName, VariableGuid, Attributes, DataSize, Data); + + if (Status == EFI_NOT_FOUND) { + DEBUG ((EFI_D_INFO, "Temp Debug: Could not find the variable in the HOBs. Checking storage PPIs...\n")); + } else if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Temp Debug: Error occurred checking the HOBs for the variable.\n")); + } else { + return Status; + } + + // + // Determine which PPI instance should be used for this variable + // + Status = GetVariableStorageId (VariableName, VariableGuid, FALSE, &VariableStorageInstanceId); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Temp Debug: Could not find the Variable Storage PPI ID for this variable!\n")); + ASSERT_EFI_ERROR (Status); + return Status; + } + + for (Instance = 0; Status != EFI_NOT_FOUND; Instance++) { + Status = PeiServicesLocatePpi ( + &gVariableStoragePpiGuid, + Instance, + &VariableStoragePpiDescriptor, + (VOID **) &VariableStoragePpi + ); + + if (!EFI_ERROR (Status) && !EFI_ERROR (VariableStoragePpi->GetId (&DiscoveredInstanceGuid))) { + DEBUG ((EFI_D_INFO, "Correct VariableStorageId GUID = %g\nDiscoveredGuid = %g\n", &VariableStorageInstanceId, &DiscoveredInstanceGuid)); + + if (CompareGuid (&VariableStorageInstanceId, &DiscoveredInstanceGuid)) { + // + // Found the appropriate Variable Storage PPI. Now use it to get the variable. + // + DEBUG ((EFI_D_INFO, "Temp Debug: Found the appropriate Variable Storage PPI. Calling VariableStoragePpi->GetVariable()...\n")); + + return VariableStoragePpi->GetVariable (VariableStoragePpi, VariableName, VariableGuid, Attributes, DataSize, Data); + } + } else if (Status != EFI_NOT_FOUND) { + ASSERT_EFI_ERROR (Status); + return Status; + } + } + + return EFI_NOT_FOUND; +} + + +/** + Return the next variable name and GUID in the HOB data store. + + @param[in] VariableNameSize On entry, points to the size of the buffer pointed to by VariableName. + On return, the size of the variable name buffer. + @param[in] VariableName On entry, a pointer to a null-terminated string that is the variable's name. + On return, points to the next variable's null-terminated name string. + @param[in] VariableGuid On entry, a pointer to an EFI_GUID that is the variable's GUID. + On return, a pointer to the next variable's GUID. + + @retval EFI_SUCCESS The variable was read successfully. + @retval EFI_NOT_FOUND The variable could not be found. + @retval EFI_BUFFER_TOO_SMALL The VariableNameSize is too small for the resulting + data. VariableNameSize is updated with the size + required for the specified variable. + @retval EFI_INVALID_PARAMETER VariableName, VariableGuid or + VariableNameSize is NULL. + @retval EFI_DEVICE_ERROR The variable could not be retrieved because of a device error. + +**/ +EFI_STATUS +EFIAPI +PeiGetHobNextVariableName ( + IN OUT UINTN *VariableNameSize, + IN OUT CHAR16 *VariableName, + IN OUT EFI_GUID *VariableGuid + ) +{ + VARIABLE_HOB_TYPE Type; + VARIABLE_POINTER_TRACK Variable; + VARIABLE_POINTER_TRACK VariableInDefaultHob; + VARIABLE_POINTER_TRACK VariablePtrTrack; + UINTN VarNameSize; + EFI_STATUS Status; + VARIABLE_STORE_HEADER *VariableStoreHeader[VariableHobTypeMax]; + VARIABLE_HEADER *VariableHeader; + VARIABLE_STORE_INFO StoreInfo; + VARIABLE_STORE_INFO StoreInfoForDefault; + VARIABLE_STORE_INFO StoreInfoForCache; + + if (VariableName == NULL || VariableGuid == NULL || VariableNameSize == NULL) { + return EFI_INVALID_PARAMETER; + } + + VariableHeader = NULL; + + Status = FindVariableInHobs (VariableName, VariableGuid, &Variable, &StoreInfo); + if (Variable.CurrPtr == NULL || Status != EFI_SUCCESS) { + return Status; + } + + if (VariableName[0] != 0) { + // + // If variable name is not NULL, get next variable + // + GetVariableHeader (&StoreInfo, Variable.CurrPtr, &VariableHeader); + Variable.CurrPtr = GetNextVariablePtr (&StoreInfo, Variable.CurrPtr, VariableHeader); + } + + VariableStoreHeader[VariableHobTypeDefault] = GetHobVariableStore (VariableHobTypeDefault, &StoreInfoForDefault); + VariableStoreHeader[VariableHobTypeCache] = GetHobVariableStore (VariableHobTypeCache, &StoreInfoForCache); + + while (TRUE) { + // + // Switch from HOB to Non-Volatile. + // + while (!GetVariableHeader (&StoreInfo, Variable.CurrPtr, &VariableHeader)) { + // + // Find current storage index + // + for (Type = (VARIABLE_HOB_TYPE) 0; Type < VariableHobTypeMax; Type++) { + if ((VariableStoreHeader[Type] != NULL) && (Variable.StartPtr == GetStartPointer (VariableStoreHeader[Type]))) { + break; + } + } + ASSERT (Type < VariableHobTypeMax); + // + // Switch to next storage + // + for (Type++; Type < VariableHobTypeMax; Type++) { + if (VariableStoreHeader[Type] != NULL) { + break; + } + } + // + // Capture the case that + // 1. current storage is the last one, or + // 2. no further storage + // + if (Type == VariableHobTypeMax) { + return EFI_NOT_FOUND; + } + Variable.StartPtr = GetStartPointer (VariableStoreHeader[Type]); + Variable.EndPtr = GetEndPointer (VariableStoreHeader[Type]); + Variable.CurrPtr = Variable.StartPtr; + GetHobVariableStore (Type, &StoreInfo); + } + + if (VariableHeader->State == VAR_ADDED || VariableHeader->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED)) { + if (VariableHeader->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED)) { + // + // If it is a IN_DELETED_TRANSITION variable, + // and there is also a same ADDED one at the same time, + // don't return it. + // + Status = FindVariableInHobsInternal ( + &StoreInfo, + GetVariableNamePtr (Variable.CurrPtr, StoreInfo.AuthFlag), + GetVendorGuidPtr (VariableHeader, StoreInfo.AuthFlag), + &VariablePtrTrack + ); + if (!EFI_ERROR (Status) && VariablePtrTrack.CurrPtr != Variable.CurrPtr) { + Variable.CurrPtr = GetNextVariablePtr (&StoreInfo, Variable.CurrPtr, VariableHeader); + continue; + } + } + + // + // Don't return cache HOB variable when default HOB overrides it + // + if ((VariableStoreHeader[VariableHobTypeDefault] != NULL) && (VariableStoreHeader[VariableHobTypeCache] != NULL) && + (Variable.StartPtr == GetStartPointer (VariableStoreHeader[VariableHobTypeCache])) + ) { + Status = FindVariableInHobsInternal ( + &StoreInfoForDefault, + GetVariableNamePtr (Variable.CurrPtr, StoreInfo.AuthFlag), + GetVendorGuidPtr (VariableHeader, StoreInfo.AuthFlag), + &VariableInDefaultHob + ); + if (!EFI_ERROR (Status)) { + Variable.CurrPtr = GetNextVariablePtr (&StoreInfo, Variable.CurrPtr, VariableHeader); + continue; + } + } + + VarNameSize = NameSizeOfVariable (VariableHeader, StoreInfo.AuthFlag); + ASSERT (VarNameSize != 0); + + if (VarNameSize <= *VariableNameSize) { + GetVariableNameOrData (&StoreInfo, (UINT8 *) GetVariableNamePtr (Variable.CurrPtr, StoreInfo.AuthFlag), VarNameSize, (UINT8 *) VariableName); + + CopyMem (VariableGuid, GetVendorGuidPtr (VariableHeader, StoreInfo.AuthFlag), sizeof (EFI_GUID)); + + Status = EFI_SUCCESS; + } else { + Status = EFI_BUFFER_TOO_SMALL; + } + + *VariableNameSize = VarNameSize; + // + // Variable is found + // + return Status; + } else { + Variable.CurrPtr = GetNextVariablePtr (&StoreInfo, Variable.CurrPtr, VariableHeader); + } + } +} + + +/** + Determines if a variable exists in the variable store HOBs + + @param[in] VariableName A pointer to a null-terminated string that is the variable's name. + @param[in] VariableGuid A pointer to an EFI_GUID that is the variable's GUID. The combination of + VariableGuid and VariableName must be unique. + + @retval TRUE The variable exists in the HOBs + @retval FALSE The variable does not exist in the HOBs + +**/ +BOOLEAN +EFIAPI +VariableExistsInHob ( + IN CONST CHAR16 *VariableName, + IN CONST EFI_GUID *VariableGuid + ) +{ + EFI_STATUS Status; + VARIABLE_POINTER_TRACK HobVariable; + VARIABLE_STORE_INFO HobStoreInfo; + + Status = FindVariableInHobs (VariableName, VariableGuid, &HobVariable, &HobStoreInfo); + if ((HobVariable.CurrPtr != NULL) && (!EFI_ERROR (Status))) { + return TRUE; + } else { + if (Status == EFI_NOT_FOUND) { + return FALSE; + } + ASSERT_EFI_ERROR (Status); + return FALSE; + } +} + + +/** + Return the next variable name and GUID. + + This function is called multiple times to retrieve the VariableName + and VariableGuid of all variables currently available in the system. + On each call, the previous results are passed into the interface, + and, on return, the interface returns the data for the next + interface. When the entire variable list has been returned, + EFI_NOT_FOUND is returned. + + @param[in] This A pointer to this instance of the EFI_PEI_READ_ONLY_VARIABLE2_PPI. + + @param[in] VariableNameSize On entry, points to the size of the buffer pointed to by VariableName. + On return, the size of the variable name buffer. + @param[in] VariableName On entry, a pointer to a null-terminated string that is the variable's name. + On return, points to the next variable's null-terminated name string. + @param[in] VariableGuid On entry, a pointer to an EFI_GUID that is the variable's GUID. + On return, a pointer to the next variable's GUID. + + @retval EFI_SUCCESS The variable was read successfully. + @retval EFI_NOT_FOUND The variable could not be found. + @retval EFI_BUFFER_TOO_SMALL The VariableNameSize is too small for the resulting + data. VariableNameSize is updated with the size + required for the specified variable. + @retval EFI_INVALID_PARAMETER VariableName, VariableGuid or + VariableNameSize is NULL. + @retval EFI_DEVICE_ERROR The variable could not be retrieved because of a device error. + +**/ +EFI_STATUS +EFIAPI +PeiGetNextVariableName ( + IN CONST EFI_PEI_READ_ONLY_VARIABLE2_PPI *This, + IN OUT UINTN *VariableNameSize, + IN OUT CHAR16 *VariableName, + IN OUT EFI_GUID *VariableGuid + ) +{ + EFI_GUID DiscoveredInstanceGuid; + EFI_GUID VariableStorageInstanceId; + EFI_PEI_PPI_DESCRIPTOR *VariableStoragePpiDescriptor; + EFI_STATUS Status; + VARIABLE_STORAGE_PPI *VariableStoragePpi; + BOOLEAN SearchComplete; + UINTN Instance; + UINT32 VarAttributes; + + if (VariableName == NULL || VariableGuid == NULL || VariableNameSize == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Check the HOBs first + // + Status = PeiGetHobNextVariableName (VariableNameSize, VariableName, VariableGuid); + if (!EFI_ERROR (Status)) { + return Status; + } else if (Status != EFI_NOT_FOUND) { + return Status; + } + // + // If VariableName is an empty string or we reached the end of the HOBs, + // get the first variable from the first Variable Storage PPI + // + if (VariableName[0] == 0 || (Status == EFI_NOT_FOUND && VariableExistsInHob (VariableName, VariableGuid))) { + ZeroMem ((VOID *) VariableName, *VariableNameSize); + ZeroMem ((VOID *) VariableGuid, sizeof (VariableGuid)); + Status = PeiServicesLocatePpi ( + &gVariableStoragePpiGuid, + 0, + &VariableStoragePpiDescriptor, + (VOID **) &VariableStoragePpi + ); + if (!EFI_ERROR (Status)) { + Status = VariableStoragePpi->GetNextVariableName ( + VariableStoragePpi, + VariableNameSize, + VariableName, + VariableGuid, + &VarAttributes + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "VariableStoragePpi->GetNextVariableName status %r\n", Status)); + } + } + return Status; + } + + // + // All of the HOB variables have been already enumerated, enumerate the Variable Storage PPIs + // + SearchComplete = FALSE; + while (!SearchComplete) { + Status = GetVariableStorageId (VariableName, VariableGuid, FALSE, &VariableStorageInstanceId); + if (EFI_ERROR (Status)) { + ASSERT_EFI_ERROR (Status); + return Status; + } + + for (Instance = 0; Status != EFI_NOT_FOUND; Instance++) { + Status = PeiServicesLocatePpi ( + &gVariableStoragePpiGuid, + Instance, + &VariableStoragePpiDescriptor, + (VOID **) &VariableStoragePpi + ); + if (!EFI_ERROR (Status)) { + Status = VariableStoragePpi->GetId (&DiscoveredInstanceGuid); + if (!EFI_ERROR (Status)) { + if (CompareGuid (&VariableStorageInstanceId, &DiscoveredInstanceGuid)) { + Status = VariableStoragePpi->GetNextVariableName ( + VariableStoragePpi, + VariableNameSize, + VariableName, + VariableGuid, + &VarAttributes + ); + if (!EFI_ERROR (Status)) { + if (VariableExistsInHob (VariableName, VariableGuid)) { + // + // Don't return this variable if there is a HOB variable that overrides it + // advance to the next variable + // + break; + } + return Status; + } else if (Status == EFI_NOT_FOUND) { + // + // If we reached the end of the variables in the current variable + // storage PPI, get the first variable in the next variable storage PPI + // + ZeroMem ((VOID *) VariableName, *VariableNameSize); + ZeroMem ((VOID *) VariableGuid, sizeof (VariableGuid)); + Status = PeiServicesLocatePpi ( + &gVariableStoragePpiGuid, + Instance + 1, + &VariableStoragePpiDescriptor, + (VOID **) &VariableStoragePpi + ); + if (!EFI_ERROR (Status)) { + Status = VariableStoragePpi->GetNextVariableName ( + VariableStoragePpi, + VariableNameSize, + VariableName, + VariableGuid, + &VarAttributes + ); + if (!EFI_ERROR (Status)) { + if (VariableExistsInHob (VariableName, VariableGuid)) { + // + // Don't return this variable if there is a HOB variable that overrides it + // advance to the next variable + // + break; + } + } + return Status; + } else if (Status == EFI_NOT_FOUND) { + // This is the last variable + SearchComplete = TRUE; + break; + } else { + ASSERT_EFI_ERROR (Status); + return Status; + } + } else { + return Status; + } + } + } else { + ASSERT_EFI_ERROR (Status); + return Status; + } + } else if (Status == EFI_NOT_FOUND) { + SearchComplete = TRUE; + } else { + ASSERT_EFI_ERROR (Status); + return Status; + } + } + } + return EFI_NOT_FOUND; +} + diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/Pei/Variable.h b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/Pei/Variable.h new file mode 100644 index 0000000000..916664e67f --- /dev/null +++ b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/Pei/Variable.h @@ -0,0 +1,152 @@ +/** @file + The internal header file includes the common header files, defines + internal structure and functions used by PeiVariable module. + + Copyright (c) 2006 - 2016, 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 _PEI_VARIABLE_H_ +#define _PEI_VARIABLE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef enum { + VariableHobTypeDefault, + VariableHobTypeCache, + VariableHobTypeMax +} VARIABLE_HOB_TYPE; + +typedef struct { + VARIABLE_STORE_HEADER *VariableStoreHeader; + VARIABLE_INDEX_TABLE *IndexTable; + // + // If it is not NULL, it means there may be an inconsecutive variable whose + // partial content is still in NV storage, but another partial content is backed up + // in spare block. + // + BOOLEAN AuthFlag; +} VARIABLE_STORE_INFO; + +// +// Functions +// +/** + Provide the functionality of the variable services. + + @param[in] FileHandle Handle of the file being invoked. + Type EFI_PEI_FILE_HANDLE is defined in FfsFindNextFile(). + @param[in] PeiServices General purpose services available to every PEIM. + + @retval EFI_SUCCESS If the interface could be successfully installed + @retval Others Returned from PeiServicesInstallPpi() + +**/ +EFI_STATUS +EFIAPI +PeimInitializeVariableServices ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ); + +/** + This service retrieves a variable's value using its name and GUID. + + Read the specified variable from the UEFI variable store. If the Data + buffer is too small to hold the contents of the variable, the error + EFI_BUFFER_TOO_SMALL is returned and DataSize is set to the required buffer + size to obtain the data. + + @param[in] This A pointer to this instance of the EFI_PEI_READ_ONLY_VARIABLE2_PPI. + @param[in] VariableName A pointer to a null-terminated string that is the variable's name. + @param[in] VariableGuid A pointer to an EFI_GUID that is the variable's GUID. The combination of + VariableGuid and VariableName must be unique. + @param[in] Attributes If non-NULL, on return, points to the variable's attributes. + @param[in] DataSize On entry, points to the size in bytes of the Data buffer. + On return, points to the size of the data returned in Data. + @param[in] Data Points to the buffer which will hold the returned variable value. + + @retval EFI_SUCCESS The variable was read successfully. + @retval EFI_NOT_FOUND The variable could not be found. + @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the resulting data. + DataSize is updated with the size required for + the specified variable. + @retval EFI_INVALID_PARAMETER VariableName, VariableGuid, DataSize or Data is NULL. + @retval EFI_DEVICE_ERROR The variable could not be retrieved because of a device error. + +**/ +EFI_STATUS +EFIAPI +PeiGetVariable ( + IN CONST EFI_PEI_READ_ONLY_VARIABLE2_PPI *This, + IN CONST CHAR16 *VariableName, + IN CONST EFI_GUID *VariableGuid, + OUT UINT32 *Attributes, + IN OUT UINTN *DataSize, + OUT VOID *Data + ); + +/** + Return the next variable name and GUID. + + This function is called multiple times to retrieve the VariableName + and VariableGuid of all variables currently available in the system. + On each call, the previous results are passed into the interface, + and, on return, the interface returns the data for the next + interface. When the entire variable list has been returned, + EFI_NOT_FOUND is returned. + + @param[in] This A pointer to this instance of the EFI_PEI_READ_ONLY_VARIABLE2_PPI. + + @param[in] VariableNameSize On entry, points to the size of the buffer pointed to by VariableName. + @param[in] VariableName On entry, a pointer to a null-terminated string that is the variable's name. + On return, points to the next variable's null-terminated name string. + + @param[in] VariableGuid On entry, a pointer to an UEFI _GUID that is the variable's GUID. + On return, a pointer to the next variable's GUID. + + @retval EFI_SUCCESS The variable was read successfully. + @retval EFI_NOT_FOUND The variable could not be found. + @retval EFI_BUFFER_TOO_SMALL The VariableNameSize is too small for the resulting + data. VariableNameSize is updated with the size + required for the specified variable. + @retval EFI_INVALID_PARAMETER VariableName, VariableGuid or + VariableNameSize is NULL. + @retval EFI_DEVICE_ERROR The variable could not be retrieved because of a device error. + +**/ +EFI_STATUS +EFIAPI +PeiGetNextVariableName ( + IN CONST EFI_PEI_READ_ONLY_VARIABLE2_PPI *This, + IN OUT UINTN *VariableNameSize, + IN OUT CHAR16 *VariableName, + IN OUT EFI_GUID *VariableGuid + ); + +#endif + diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/Pei/VariablePei.inf b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/Pei/VariablePei.inf new file mode 100644 index 0000000000..24f64c21b9 --- /dev/null +++ b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/Pei/VariablePei.inf @@ -0,0 +1,75 @@ +## @file +# Implements ReadOnly Variable Services required by PEIM and installs PEI ReadOnly Varaiable2 PPI. +# +# Copyright (c) 2006 - 2016, 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 = PeiVariable + MODULE_UNI_FILE = PeiVariable.uni + FILE_GUID = 34C8C28F-B61C-45a2-8F2E-89E46BECC63B + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + ENTRY_POINT = PeimInitializeVariableServices + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + Variable.c + Variable.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + BroxtonPlatformPkg/PlatformPkg.dec + BroxtonSiPkg/BroxtonSiPkg.dec + +[LibraryClasses] + BaseMemoryLib + MemoryAllocationLib + PcdLib + HobLib + PeimEntryPoint + DebugLib + PeiServicesTablePointerLib + PeiServicesLib + PeiVariableCacheLib + VariableStorageSelectorLib + +[Guids] + ## CONSUMES ## GUID # Variable store header + ## SOMETIMES_CONSUMES ## HOB + gEfiAuthenticatedVariableGuid + ## SOMETIMES_CONSUMES ## GUID # Variable store header + ## SOMETIMES_CONSUMES ## HOB + gEfiVariableGuid + ## SOMETIMES_PRODUCES ## HOB + ## SOMETIMES_CONSUMES ## HOB + gEfiVariableIndexTableGuid + +[Ppis] + gEfiPeiReadOnlyVariable2PpiGuid ## PRODUCES + gReadOnlyVariablePreMemoryDescriptorPpiGuid ## PRODUCES + gVariableStoragePpiGuid ## CONSUMES + +[Pcd] + +[Depex] + TRUE + +[UserExtensions.TianoCore."ExtraFiles"] + PeiVariableExtra.uni diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/Measurement.c b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/Measurement.c new file mode 100644 index 0000000000..a0ad4c26dc --- /dev/null +++ b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/Measurement.c @@ -0,0 +1,260 @@ +/** @file + Measure TrEE required variable. + + Copyright (c) 2013 - 2016, 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 + +typedef struct { + CHAR16 *VariableName; + EFI_GUID *VendorGuid; +} VARIABLE_TYPE; + +VARIABLE_TYPE mVariableType[] = { + {EFI_SECURE_BOOT_MODE_NAME, &gEfiGlobalVariableGuid}, + {EFI_PLATFORM_KEY_NAME, &gEfiGlobalVariableGuid}, + {EFI_KEY_EXCHANGE_KEY_NAME, &gEfiGlobalVariableGuid}, + {EFI_IMAGE_SECURITY_DATABASE, &gEfiImageSecurityDatabaseGuid}, + {EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid}, +}; + +/** + This function will return if this variable is SecureBootPolicy Variable. + + @param[in] VariableName A Null-terminated string that is the name of the vendor's variable. + @param[in] VendorGuid A unique identifier for the vendor. + + @retval TRUE This is SecureBootPolicy Variable. + @retval FALSE This is not SecureBootPolicy Variable. + +**/ +BOOLEAN +IsSecureBootPolicyVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid + ) +{ + UINTN Index; + + for (Index = 0; Index < sizeof (mVariableType) / sizeof (mVariableType[0]); Index++) { + if ((StrCmp (VariableName, mVariableType[Index].VariableName) == 0) && + (CompareGuid (VendorGuid, mVariableType[Index].VendorGuid))) { + return TRUE; + } + } + return FALSE; +} + + +/** + Measure and log an EFI variable, and extend the measurement result into a specific PCR. + + @param[in] VarName A Null-terminated string that is the name of the vendor's variable. + @param[in] VendorGuid A unique identifier for the vendor. + @param[in] VarData The content of the variable data. + @param[in] VarSize The size of the variable data. + + @retval EFI_SUCCESS Operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Out of memory. + @retval EFI_DEVICE_ERROR The operation was unsuccessful. + +**/ +EFI_STATUS +EFIAPI +MeasureVariable ( + IN CHAR16 *VarName, + IN EFI_GUID *VendorGuid, + IN VOID *VarData, + IN UINTN VarSize + ) +{ + EFI_STATUS Status; + UINTN VarNameLength; + EFI_VARIABLE_DATA_TREE *VarLog; + UINT32 VarLogSize; + + ASSERT ((VarSize == 0 && VarData == NULL) || (VarSize != 0 && VarData != NULL)); + + VarNameLength = StrLen (VarName); + VarLogSize = (UINT32) (sizeof (*VarLog) + VarNameLength * sizeof (*VarName) + VarSize + - sizeof (VarLog->UnicodeName) - sizeof (VarLog->VariableData)); + + VarLog = (EFI_VARIABLE_DATA_TREE *) AllocateZeroPool (VarLogSize); + if (VarLog == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (&VarLog->VariableName, VendorGuid, sizeof (VarLog->VariableName)); + VarLog->UnicodeNameLength = VarNameLength; + VarLog->VariableDataLength = VarSize; + CopyMem ( + VarLog->UnicodeName, + VarName, + VarNameLength * sizeof (*VarName) + ); + if (VarSize != 0) { + CopyMem ( + (CHAR16 *) VarLog->UnicodeName + VarNameLength, + VarData, + VarSize + ); + } + + DEBUG ((EFI_D_INFO, "AuthVariableDxe: MeasureVariable (Pcr - %x, EventType - %x, ", (UINTN) 7, (UINTN) EV_EFI_VARIABLE_AUTHORITY)); + DEBUG ((EFI_D_INFO, "VariableName - %s, VendorGuid - %g)\n", VarName, VendorGuid)); + + Status = TpmMeasureAndLogData ( + 7, + EV_EFI_VARIABLE_DRIVER_CONFIG, + VarLog, + VarLogSize, + VarLog, + VarLogSize + ); + FreePool (VarLog); + return Status; +} + + +/** + Returns the status whether get the variable success. The function retrieves + variable through the UEFI Runtime Service GetVariable(). The + returned buffer is allocated using AllocatePool(). The caller is responsible + for freeing this buffer with FreePool(). + + This API is only invoked in boot time. It may NOT be invoked at runtime. + + @param[in] Name The pointer to a Null-terminated Unicode string. + @param[in] Guid The pointer to an EFI_GUID structure + @param[out] Value The buffer point saved the variable info. + @param[out] Size The buffer size of the variable. + + @return EFI_OUT_OF_RESOURCES Allocate buffer failed. + @return EFI_SUCCESS Find the specified variable. + @return Others Errors Return errors from call to gRT->GetVariable. + +**/ +EFI_STATUS +InternalGetVariable ( + IN CONST CHAR16 *Name, + IN CONST EFI_GUID *Guid, + OUT VOID **Value, + OUT UINTN *Size + ) +{ + EFI_STATUS Status; + UINTN BufferSize; + + // + // Try to get the variable size. + // + BufferSize = 0; + *Value = NULL; + if (Size != NULL) { + *Size = 0; + } + + Status = gRT->GetVariable ((CHAR16 *) Name, (EFI_GUID *) Guid, NULL, &BufferSize, *Value); + if (Status != EFI_BUFFER_TOO_SMALL) { + return Status; + } + + // + // Allocate buffer to get the variable. + // + *Value = AllocatePool (BufferSize); + ASSERT (*Value != NULL); + if (*Value == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Get the variable data. + // + Status = gRT->GetVariable ((CHAR16 *) Name, (EFI_GUID *) Guid, NULL, &BufferSize, *Value); + if (EFI_ERROR (Status)) { + FreePool(*Value); + *Value = NULL; + } + + if (Size != NULL) { + *Size = BufferSize; + } + + return Status; +} + + +/** + SecureBoot Hook for SetVariable. + + @param[in] VariableName Name of Variable to be found. + @param[in] VendorGuid Variable vendor GUID. + +**/ +VOID +EFIAPI +SecureBootHook ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid + ) +{ + EFI_STATUS Status; + UINTN VariableDataSize; + VOID *VariableData; + + if (!IsSecureBootPolicyVariable (VariableName, VendorGuid)) { + return ; + } + + // + // We should NOT use Data and DataSize here,because it may include signature, + // or is just partial with append attributes, or is deleted. + // We should GetVariable again, to get full variable content. + // + Status = InternalGetVariable ( + VariableName, + VendorGuid, + &VariableData, + &VariableDataSize + ); + if (EFI_ERROR (Status)) { + VariableData = NULL; + VariableDataSize = 0; + } + + Status = MeasureVariable ( + VariableName, + VendorGuid, + VariableData, + VariableDataSize + ); + DEBUG ((EFI_D_INFO, "MeasureBootPolicyVariable - %r\n", Status)); + + if (VariableData != NULL) { + FreePool (VariableData); + } + + return ; +} + diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/TcgMorLockDxe.c b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/TcgMorLockDxe.c new file mode 100644 index 0000000000..26de9bf9c1 --- /dev/null +++ b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/TcgMorLockDxe.c @@ -0,0 +1,93 @@ +/** @file + TCG MOR (Memory Overwrite Request) Lock Control support (DXE version). + + This module clears MemoryOverwriteRequestControlLock variable to indicate + MOR lock control unsupported. + + Copyright (c) 2016, 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 "Variable.h" + +extern EDKII_VARIABLE_LOCK_PROTOCOL mVariableLock; + + +/** + This service is an MOR/MorLock checker handler for the SetVariable(). + + @param[in] VariableName The name of the vendor's variable, as a + Null-Terminated Unicode String + @param[in] VendorGuid Unify identifier for vendor. + @param[in] Attributes Point to memory location to return the attributes of variable. If the point + is NULL, the parameter would be ignored. + @param[in] DataSize The size in bytes of Data-Buffer. + @param[in] Data Point to the content of the variable. + + @retval EFI_SUCCESS The MOR/MorLock check pass, and Variable driver can store the variable data. + @retval EFI_INVALID_PARAMETER The MOR/MorLock data or data size or attributes is not allowed for MOR variable. + @retval EFI_ACCESS_DENIED The MOR/MorLock is locked. + @retval EFI_ALREADY_STARTED The MorLock variable is handled inside this function. + Variable driver can just return EFI_SUCCESS. +**/ +EFI_STATUS +SetVariableCheckHandlerMor ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN UINT32 Attributes, + IN UINTN DataSize, + IN VOID *Data + ) +{ + // + // Just let it pass. No need provide protection for DXE version. + // + return EFI_SUCCESS; +} + +/** + Initialization for MOR Lock Control. + + @retval EFI_SUCEESS MorLock initialization success. + @retval Others Some error occurs. + +**/ +EFI_STATUS +MorLockInit ( + VOID + ) +{ + // + // Always clear variable to report unsupported to OS. + // The reason is that the DXE version is not proper to provide *protection*. + // BIOS should use SMM version variable driver to provide such capability. + // + VariableServiceSetVariable ( + MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME, + &gEfiMemoryOverwriteRequestControlLockGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + 0, + NULL + ); + + // + // Need set this variable to be read-only to prevent other module set it. + // + VariableLockRequestToLock (&mVariableLock, MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME, &gEfiMemoryOverwriteRequestControlLockGuid); + return EFI_SUCCESS; +} + diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/TcgMorLockSmm.c b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/TcgMorLockSmm.c new file mode 100644 index 0000000000..2a500401d3 --- /dev/null +++ b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/TcgMorLockSmm.c @@ -0,0 +1,407 @@ +/** @file + TCG MOR (Memory Overwrite Request) Lock Control support (SMM version). + + This module initilizes MemoryOverwriteRequestControlLock variable. + This module adds Variable Hook and check MemoryOverwriteRequestControlLock. + + Copyright (c) 2016, 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 "Variable.h" + +typedef struct { + CHAR16 *VariableName; + EFI_GUID *VendorGuid; +} VARIABLE_TYPE; + +VARIABLE_TYPE mMorVariableType[] = { + {MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME, &gEfiMemoryOverwriteControlDataGuid}, + {MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME, &gEfiMemoryOverwriteRequestControlLockGuid}, +}; + +#define MOR_LOCK_DATA_UNLOCKED 0x0 +#define MOR_LOCK_DATA_LOCKED_WITHOUT_KEY 0x1 +#define MOR_LOCK_DATA_LOCKED_WITH_KEY 0x2 + +#define MOR_LOCK_V1_SIZE 1 +#define MOR_LOCK_V2_KEY_SIZE 8 + +typedef enum { + MorLockStateUnlocked = 0, + MorLockStateLocked = 1, +} MOR_LOCK_STATE; + +UINT8 mMorLockKey[MOR_LOCK_V2_KEY_SIZE]; +BOOLEAN mMorLockKeyEmpty = TRUE; +BOOLEAN mMorLockPassThru = FALSE; +MOR_LOCK_STATE mMorLockState = MorLockStateUnlocked; + + +/** + Returns if this is MOR related variable. + + @param[in] VariableName The name of the vendor's variable, it's a Null-Terminated Unicode String + @param[in] VendorGuid Unify identifier for vendor. + + @retval TRUE The variable is MOR related. + @retval FALSE The variable is NOT MOR related. + +**/ +BOOLEAN +IsAnyMorVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid + ) +{ + UINTN Index; + + for (Index = 0; Index < sizeof (mMorVariableType) / sizeof (mMorVariableType[0]); Index++) { + if ((StrCmp (VariableName, mMorVariableType[Index].VariableName) == 0) && + (CompareGuid (VendorGuid, mMorVariableType[Index].VendorGuid))) { + return TRUE; + } + } + return FALSE; +} + + +/** + Returns if this is MOR lock variable. + + @param[in] VariableName The name of the vendor's variable, it's a Null-Terminated Unicode String + @param[in] VendorGuid Unify identifier for vendor. + + @retval TRUE The variable is MOR lock variable. + @retval FALSE The variable is NOT MOR lock variable. + +**/ +BOOLEAN +IsMorLockVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid + ) +{ + if ((StrCmp (VariableName, MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME) == 0) && + (CompareGuid (VendorGuid, &gEfiMemoryOverwriteRequestControlLockGuid))) { + return TRUE; + } + return FALSE; +} + + +/** + Set MOR lock variable. + + @param[in] Data MOR Lock variable data. + + @retval EFI_SUCCESS The firmware has successfully stored the variable and its data as + defined by the Attributes. + @retval EFI_INVALID_PARAMETER An invalid combination of attribute bits was supplied, or the + DataSize exceeds the maximum allowed. + @retval EFI_INVALID_PARAMETER VariableName is an empty Unicode string. + @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the variable and its data. + @retval EFI_DEVICE_ERROR The variable could not be saved due to a hardware failure. + @retval EFI_WRITE_PROTECTED The variable in question is read-only. + @retval EFI_WRITE_PROTECTED The variable in question cannot be deleted. + @retval EFI_SECURITY_VIOLATION The variable could not be written due to EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS + set but the AuthInfo does NOT pass the validation check carried + out by the firmware. + @retval EFI_NOT_FOUND The variable trying to be updated or deleted was not found. + +**/ +EFI_STATUS +SetMorLockVariable ( + IN UINT8 Data + ) +{ + EFI_STATUS Status; + + mMorLockPassThru = TRUE; + Status = VariableServiceSetVariable ( + MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME, + &gEfiMemoryOverwriteRequestControlLockGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + sizeof (Data), + &Data + ); + mMorLockPassThru = FALSE; + + return Status; +} + + +/** + This service is an MorLock checker handler for the SetVariable(). + + @param[in] VariableName The name of the vendor's variable, as a + Null-Terminated Unicode String + @param[in] VendorGuid Unify identifier for vendor. + @param[in] Attributes Point to memory location to return the attributes of variable. If the point + is NULL, the parameter would be ignored. + @param[in] DataSize The size in bytes of Data-Buffer. + @param[in] Data Point to the content of the variable. + + @retval EFI_SUCCESS The MorLock check pass, and Variable driver can store the variable data. + @retval EFI_INVALID_PARAMETER The MorLock data or data size or attributes is not allowed. + @retval EFI_ACCESS_DENIED The MorLock is locked. + @retval EFI_WRITE_PROTECTED The MorLock deletion is not allowed. + @retval EFI_ALREADY_STARTED The MorLock variable is handled inside this function. + Variable driver can just return EFI_SUCCESS. +**/ +EFI_STATUS +SetVariableCheckHandlerMorLock ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN UINT32 Attributes, + IN UINTN DataSize, + IN VOID *Data + ) +{ + EFI_STATUS Status; + + // + // Basic Check + // + if (Attributes == 0 || DataSize == 0 || Data == NULL) { + return EFI_WRITE_PROTECTED; + } + + if ((Attributes != (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS)) || + ((DataSize != MOR_LOCK_V1_SIZE) && (DataSize != MOR_LOCK_V2_KEY_SIZE))) { + return EFI_INVALID_PARAMETER; + } + + // + // Do not check if the request is passthru. + // + if (mMorLockPassThru) { + return EFI_SUCCESS; + } + + if (mMorLockState == MorLockStateUnlocked) { + // + // In Unlocked State + // + if (DataSize == MOR_LOCK_V1_SIZE) { + // + // V1 - lock permenantly + // + if (*(UINT8 *) Data == MOR_LOCK_DATA_UNLOCKED) { + // + // Unlock + // + Status = SetMorLockVariable (MOR_LOCK_DATA_UNLOCKED); + if (!EFI_ERROR (Status)) { + // + // return EFI_ALREADY_STARTED to skip variable set. + // + return EFI_ALREADY_STARTED; + } else { + // + // SetVar fail + // + return Status; + } + } else if (*(UINT8 *) Data == MOR_LOCK_DATA_LOCKED_WITHOUT_KEY) { + // + // Lock without key + // + Status = SetMorLockVariable (MOR_LOCK_DATA_LOCKED_WITHOUT_KEY); + if (!EFI_ERROR (Status)) { + // + // Lock success + // + mMorLockState = MorLockStateLocked; + // + // return EFI_ALREADY_STARTED to skip variable set. + // + return EFI_ALREADY_STARTED; + } else { + // + // SetVar fail + // + return Status; + } + } else { + return EFI_INVALID_PARAMETER; + } + } else if (DataSize == MOR_LOCK_V2_KEY_SIZE) { + // + // V2 lock and provision the key + // + + // + // Need set here because the data value on flash is different + // + Status = SetMorLockVariable (MOR_LOCK_DATA_LOCKED_WITH_KEY); + if (EFI_ERROR(Status)) { + // + // SetVar fail, do not provision the key + // + return Status; + } else { + // + // Lock success, provision the key + // + mMorLockKeyEmpty = FALSE; + CopyMem (mMorLockKey, Data, MOR_LOCK_V2_KEY_SIZE); + mMorLockState = MorLockStateLocked; + // + // return EFI_ALREADY_STARTED to skip variable set. + // + return EFI_ALREADY_STARTED; + } + } else { + ASSERT (FALSE); + return EFI_OUT_OF_RESOURCES; + } + } else { + // + // In Locked State + // + if (mMorLockKeyEmpty || (DataSize != MOR_LOCK_V2_KEY_SIZE)) { + return EFI_ACCESS_DENIED; + } + if ((CompareMem (Data, mMorLockKey, MOR_LOCK_V2_KEY_SIZE) == 0)) { + // + // Key match - unlock + // + + // + // Need set here because the data value on flash is different + // + Status = SetMorLockVariable (MOR_LOCK_DATA_UNLOCKED); + if (EFI_ERROR (Status)) { + // + // SetVar fail + // + return Status; + } else { + // + // Unlock Success + // + mMorLockState = MorLockStateUnlocked; + mMorLockKeyEmpty = TRUE; + ZeroMem (mMorLockKey, sizeof (mMorLockKey)); + // + // return EFI_ALREADY_STARTED to skip variable set. + // + return EFI_ALREADY_STARTED; + } + } else { + // + // Key mismatch - Prevent Dictionary Attack + // + mMorLockState = MorLockStateLocked; + mMorLockKeyEmpty = TRUE; + ZeroMem (mMorLockKey, sizeof (mMorLockKey)); + return EFI_ACCESS_DENIED; + } + } +} + + +/** + This service is an MOR/MorLock checker handler for the SetVariable(). + + @param[in] VariableName The name of the vendor's variable, as a + Null-Terminated Unicode String + @param[in] VendorGuid Unify identifier for vendor. + @param[in] Attributes Point to memory location to return the attributes of variable. If the point + is NULL, the parameter would be ignored. + @param[in] DataSize The size in bytes of Data-Buffer. + @param[in] Data Point to the content of the variable. + + @retval EFI_SUCCESS The MOR/MorLock check pass, and Variable driver can store the variable data. + @retval EFI_INVALID_PARAMETER The MOR/MorLock data or data size or attributes is not allowed for MOR variable. + @retval EFI_ACCESS_DENIED The MOR/MorLock is locked. + @retval EFI_ALREADY_STARTED The MorLock variable is handled inside this function. + Variable driver can just return EFI_SUCCESS. + +**/ +EFI_STATUS +SetVariableCheckHandlerMor ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN UINT32 Attributes, + IN UINTN DataSize, + IN VOID *Data + ) +{ + // + // do not handle non-MOR variable + // + if (!IsAnyMorVariable (VariableName, VendorGuid)) { + return EFI_SUCCESS; + } + + // + // MorLock variable + // + if (IsMorLockVariable (VariableName, VendorGuid)) { + return SetVariableCheckHandlerMorLock ( + VariableName, + VendorGuid, + Attributes, + DataSize, + Data + ); + } + + // + // Mor Variable + // + // + // Basic Check + // + if ((Attributes != (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS)) || + (DataSize != sizeof (UINT8)) || + (Data == NULL)) { + return EFI_INVALID_PARAMETER; + } + if (mMorLockState == MorLockStateLocked) { + // + // If lock, deny access + // + return EFI_ACCESS_DENIED; + } + // + // grant access + // + return EFI_SUCCESS; +} + + +/** + Initialization for MOR Lock Control. + + @retval EFI_SUCEESS MorLock initialization success. + @retval Others Some error occurs. + +**/ +EFI_STATUS +MorLockInit ( + VOID + ) +{ + // + // Set variable to report capability to OS + // + return SetMorLockVariable (0); +} + diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VarCheck.c b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VarCheck.c new file mode 100644 index 0000000000..bd0dc0ca42 --- /dev/null +++ b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VarCheck.c @@ -0,0 +1,161 @@ +/** @file + Implementation functions and structures for var check protocol + and variable lock protocol based on VarCheckLib. + + Copyright (c) 2015 - 2016, 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" + + +/** + Mark a variable that will become read-only after leaving the DXE phase of execution. + Write request coming from SMM environment through EFI_SMM_VARIABLE_PROTOCOL is allowed. + + @param[in] This The VARIABLE_LOCK_PROTOCOL instance. + @param[in] VariableName A pointer to the variable name that will be made read-only subsequently. + @param[in] VendorGuid A pointer to the vendor GUID that will be made read-only subsequently. + + @retval EFI_SUCCESS The variable specified by the VariableName and the VendorGuid was marked + as pending to be read-only. + @retval EFI_INVALID_PARAMETER VariableName or VendorGuid is NULL. + Or VariableName is an empty string. + @retval EFI_ACCESS_DENIED EFI_END_OF_DXE_EVENT_GROUP_GUID or EFI_EVENT_GROUP_READY_TO_BOOT has + already been signaled. + @retval EFI_OUT_OF_RESOURCES There is not enough resource to hold the lock request. + +**/ +EFI_STATUS +EFIAPI +VariableLockRequestToLock ( + IN CONST EDKII_VARIABLE_LOCK_PROTOCOL *This, + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid + ) +{ + EFI_STATUS Status; + VAR_CHECK_VARIABLE_PROPERTY Property; + + AcquireLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock); + + Status = VarCheckLibVariablePropertyGet (VariableName, VendorGuid, &Property); + if (!EFI_ERROR (Status)) { + Property.Property |= VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY; + } else { + Property.Revision = VAR_CHECK_VARIABLE_PROPERTY_REVISION; + Property.Property = VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY; + Property.Attributes = 0; + Property.MinSize = 1; + Property.MaxSize = MAX_UINTN; + } + Status = VarCheckLibVariablePropertySet (VariableName, VendorGuid, &Property); + + DEBUG ((EFI_D_INFO, "[Variable] Lock: %g:%s %r\n", VendorGuid, VariableName, Status)); + + ReleaseLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock); + + return Status; +} + + +/** + Register SetVariable check handler. + + @param[in] Handler Pointer to check handler. + + @retval EFI_SUCCESS The SetVariable check handler was registered successfully. + @retval EFI_INVALID_PARAMETER Handler is NULL. + @retval EFI_ACCESS_DENIED EFI_END_OF_DXE_EVENT_GROUP_GUID or EFI_EVENT_GROUP_READY_TO_BOOT has + already been signaled. + @retval EFI_OUT_OF_RESOURCES There is not enough resource for the SetVariable check handler register request. + @retval EFI_UNSUPPORTED This interface is not implemented. + For example, it is unsupported in VarCheck protocol if both VarCheck and SmmVarCheck protocols are present. + +**/ +EFI_STATUS +EFIAPI +VarCheckRegisterSetVariableCheckHandler ( + IN VAR_CHECK_SET_VARIABLE_CHECK_HANDLER Handler + ) +{ + EFI_STATUS Status; + + AcquireLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock); + Status = VarCheckLibRegisterSetVariableCheckHandler (Handler); + ReleaseLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock); + + return Status; +} + + +/** + Variable property set. + + @param[in] Name Pointer to the variable name. + @param[in] Guid Pointer to the vendor GUID. + @param[in] VariableProperty Pointer to the input variable property. + + @retval EFI_SUCCESS The property of variable specified by the Name and Guid was set successfully. + @retval EFI_INVALID_PARAMETER Name, Guid or VariableProperty is NULL, or Name is an empty string, + or the fields of VariableProperty are not valid. + @retval EFI_ACCESS_DENIED EFI_END_OF_DXE_EVENT_GROUP_GUID or EFI_EVENT_GROUP_READY_TO_BOOT has + already been signaled. + @retval EFI_OUT_OF_RESOURCES There is not enough resource for the variable property set request. + +**/ +EFI_STATUS +EFIAPI +VarCheckVariablePropertySet ( + IN CHAR16 *Name, + IN EFI_GUID *Guid, + IN VAR_CHECK_VARIABLE_PROPERTY *VariableProperty + ) +{ + EFI_STATUS Status; + + AcquireLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock); + Status = VarCheckLibVariablePropertySet (Name, Guid, VariableProperty); + ReleaseLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock); + + return Status; +} + + +/** + Variable property get. + + @param[in] Name Pointer to the variable name. + @param[in] Guid Pointer to the vendor GUID. + @param[out] VariableProperty Pointer to the output variable property. + + @retval EFI_SUCCESS The property of variable specified by the Name and Guid was got successfully. + @retval EFI_INVALID_PARAMETER Name, Guid or VariableProperty is NULL, or Name is an empty string. + @retval EFI_NOT_FOUND The property of variable specified by the Name and Guid was not found. + +**/ +EFI_STATUS +EFIAPI +VarCheckVariablePropertyGet ( + IN CHAR16 *Name, + IN EFI_GUID *Guid, + OUT VAR_CHECK_VARIABLE_PROPERTY *VariableProperty + ) +{ + EFI_STATUS Status; + + AcquireLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock); + Status = VarCheckLibVariablePropertyGet (Name, Guid, VariableProperty); + ReleaseLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock); + + return Status; +} + diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c new file mode 100644 index 0000000000..43687dafe8 --- /dev/null +++ b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.c @@ -0,0 +1,4818 @@ +/** @file + The common variable operation routines shared by DXE_RUNTIME variable + module and DXE_SMM variable module. + + Caution: This module requires additional review when modified. + This driver will have external input - variable data. They may be input in SMM mode. + This external input must be validated carefully to avoid security issue like + buffer overflow, integer overflow. + + VariableServiceGetNextVariableName () and VariableServiceQueryVariableInfo() are external API. + They need check input parameter. + + VariableServiceGetVariable() and VariableServiceSetVariable() are external API + to receive datasize and data buffer. The size should be checked carefully. + + VariableServiceSetVariable() should also check authenticate data to avoid buffer overflow, + integer overflow. It should also check attribute to avoid authentication bypass. + + Copyright (c) 2006 - 2016, 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" + +VARIABLE_MODULE_GLOBAL *mVariableModuleGlobal; + +/// +/// A flag which indicates whether all variables should be treated as volatile +/// +BOOLEAN mForceVolatileVariable; + +/// +/// Define a memory cache that improves the search performance for a variable. +/// +VARIABLE_STORE_HEADER *mNvVariableCache = NULL; + +/// +/// The memory entry used for variable statistics data. +/// +VARIABLE_INFO_ENTRY *gVariableInfo = NULL; + +/// +/// The flag to indicate whether the platform has left the DXE phase of execution. +/// +BOOLEAN mEndOfDxe = FALSE; + +/// +/// Buffer for temporary storage of variable contents when copying to NV cache +/// +VOID *mVariableDataBuffer = NULL; + +/// +/// It indicates the var check request source. +/// In the implementation, DXE is regarded as untrusted, and SMM is trusted. +/// +VAR_CHECK_REQUEST_SOURCE mRequestSource = VarCheckFromUntrusted; +CHAR16 mVariableNameBuffer[MAX_VARIABLE_NAME_SIZE]; + +// +// It will record the current boot error flag before EndOfDxe. +// +VAR_ERROR_FLAG mCurrentBootVarErrFlag = VAR_ERROR_FLAG_NO_ERROR; + +VARIABLE_ENTRY_PROPERTY mVariableEntryProperty[] = { + { + &gEdkiiVarErrorFlagGuid, + VAR_ERROR_FLAG_NAME, + { + VAR_CHECK_VARIABLE_PROPERTY_REVISION, + VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY, + VARIABLE_ATTRIBUTE_NV_BS_RT, + sizeof (VAR_ERROR_FLAG), + sizeof (VAR_ERROR_FLAG) + } + }, +}; + +AUTH_VAR_LIB_CONTEXT_IN mAuthContextIn = { + AUTH_VAR_LIB_CONTEXT_IN_STRUCT_VERSION, + // + // StructSize, TO BE FILLED + // + 0, + // + // MaxAuthVariableSize, TO BE FILLED + // + 0, + VariableExLibFindVariable, + VariableExLibFindNextVariable, + VariableExLibUpdateVariable, + VariableExLibGetScratchBuffer, + VariableExLibCheckRemainingSpaceForConsistency, + VariableExLibAtRuntime, +}; + +AUTH_VAR_LIB_CONTEXT_OUT mAuthContextOut; + + +/** + SecureBoot Hook for auth variable update. + + @param[in] VariableName Name of Variable to be found. + @param[in] VendorGuid Variable vendor GUID. + +**/ +VOID +EFIAPI +SecureBootHook ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid + ); + + +/** + Initialization for MOR Lock Control. + + @retval EFI_SUCEESS MorLock initialization success. + @retval Others Some error occurs. + +**/ +EFI_STATUS +MorLockInit ( + VOID + ); + + +/** + This service is an MOR/MorLock checker handler for the SetVariable(). + + @param[in] VariableName The name of the vendor's variable, as a + Null-Terminated Unicode String + @param[in] VendorGuid Unify identifier for vendor. + @param[in] Attributes Point to memory location to return the attributes of variable. If the point + is NULL, the parameter would be ignored. + @param[in] DataSize The size in bytes of Data-Buffer. + @param[in] Data Point to the content of the variable. + + @retval EFI_SUCCESS The MOR/MorLock check pass, and Variable driver can store the variable data. + @retval EFI_INVALID_PARAMETER The MOR/MorLock data or data size or attributes is not allowed for MOR variable. + @retval EFI_ACCESS_DENIED The MOR/MorLock is locked. + @retval EFI_ALREADY_STARTED The MorLock variable is handled inside this function. + Variable driver can just return EFI_SUCCESS. + +**/ +EFI_STATUS +SetVariableCheckHandlerMor ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN UINT32 Attributes, + IN UINTN DataSize, + IN VOID *Data + ); + + +/** + Routine used to track statistical information about variable usage. + The data is stored in the EFI system table so it can be accessed later. + VariableInfo.efi can dump out the table. Only Boot Services variable + accesses are tracked by this code. The PcdVariableCollectStatistics + build flag controls if this feature is enabled. + + A read that hits in the cache will have Read and Cache true for + 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] Cache TRUE for a cache hit. + +**/ +VOID +UpdateVariableInfo ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN BOOLEAN Volatile, + IN BOOLEAN Read, + IN BOOLEAN Write, + IN BOOLEAN Delete, + IN BOOLEAN Cache + ) +{ + VARIABLE_INFO_ENTRY *Entry; + + if (FeaturePcdGet (PcdVariableCollectStatistics)) { + + 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. + // + gVariableInfo = AllocateZeroPool (sizeof (VARIABLE_INFO_ENTRY)); + + if (gVariableInfo == NULL) { + ASSERT (gVariableInfo != NULL); + return; + } + + CopyGuid (&gVariableInfo->VendorGuid, VendorGuid); + gVariableInfo->Name = AllocateZeroPool (StrSize (VariableName)); + + if (gVariableInfo->Name == NULL) { + ASSERT (gVariableInfo->Name != NULL); + return; + } + + StrCpyS (gVariableInfo->Name, StrSize (VariableName) / sizeof (CHAR16), VariableName); + gVariableInfo->Volatile = Volatile; + } + + + for (Entry = gVariableInfo; Entry != NULL; Entry = Entry->Next) { + if (CompareGuid (VendorGuid, &Entry->VendorGuid)) { + if (StrCmp (VariableName, Entry->Name) == 0) { + if (Read) { + Entry->ReadCount++; + } + if (Write) { + Entry->WriteCount++; + } + if (Delete) { + Entry->DeleteCount++; + } + if (Cache) { + Entry->CacheCount++; + } + return; + } + } + + if (Entry->Next == NULL) { + // + // If the entry is not in the table add it. + // Next iteration of the loop will fill in the data. + // + Entry->Next = AllocateZeroPool (sizeof (VARIABLE_INFO_ENTRY)); + + if (Entry->Next == NULL) { + ASSERT (Entry->Next != NULL); + return; + } + + CopyGuid (&Entry->Next->VendorGuid, VendorGuid); + Entry->Next->Name = AllocateZeroPool (StrSize (VariableName)); + ASSERT (Entry->Next->Name != NULL); + StrCpyS (Entry->Next->Name, StrSize (VariableName) /sizeof (CHAR16), VariableName); + Entry->Next->Volatile = Volatile; + } + + } + } +} + + +/** + This code checks if variable header is valid or not. + + @param[in] Variable Pointer to the Variable Header. + @param[in] VariableStoreEnd Pointer to the Variable Store End. + + @retval TRUE Variable header is valid. + @retval FALSE Variable header is not valid. + +**/ +BOOLEAN +IsValidVariableHeader ( + IN VARIABLE_HEADER *Variable, + IN VARIABLE_HEADER *VariableStoreEnd + ) +{ + if ((Variable == NULL) || (Variable >= VariableStoreEnd) || (Variable->StartId != VARIABLE_DATA)) { + // + // Variable is NULL or has reached the end of variable store, + // or the StartId is not correct. + // + return FALSE; + } + + return TRUE; +} + + +/** + This function writes data to the FWH at the correct LBA even if the LBAs + are fragmented. + + @param[in] Global Pointer to VARAIBLE_GLOBAL structure. + @param[in] Volatile Point out the Variable is Volatile or Non-Volatile. + @param[in] SetByIndex TRUE if target pointer is given as index. + FALSE if target pointer is absolute. + @param[in] DataPtrIndex Pointer to the Data from the end of VARIABLE_STORE_HEADER + structure. + @param[in] DataSize Size of data to be written. + @param[in] Buffer Pointer to the buffer from which data is written. + + @retval EFI_INVALID_PARAMETER Parameters not valid. + @retval EFI_SUCCESS Variable store successfully updated. + +**/ +EFI_STATUS +UpdateVariableStore ( + IN VARIABLE_GLOBAL *Global, + IN BOOLEAN Volatile, + IN BOOLEAN SetByIndex, + IN UINTN DataPtrIndex, + IN UINT32 DataSize, + IN UINT8 *Buffer + ) +{ + VARIABLE_STORE_HEADER *VolatileBase; + EFI_PHYSICAL_ADDRESS DataPtr; + + DataPtr = DataPtrIndex; + // + // Check if the Data is Volatile. + // + if (!Volatile) { + // + // Data Pointer should point to the actual Address where data is to be + // written. + // + if (SetByIndex) { + DataPtr += (UINTN) mNvVariableCache; + } + + if ((DataPtr + DataSize) >= ((UINTN) ((UINT8 *) mNvVariableCache + mNvVariableCache->Size))) { + return EFI_INVALID_PARAMETER; + } + } else { + // + // Data Pointer should point to the actual Address where data is to be + // written. + // + VolatileBase = (VARIABLE_STORE_HEADER *) ((UINTN) mVariableModuleGlobal->VariableGlobal.VolatileVariableBase); + if (SetByIndex) { + DataPtr += mVariableModuleGlobal->VariableGlobal.VolatileVariableBase; + } + + if ((DataPtr + DataSize) >= ((UINTN) ((UINT8 *) VolatileBase + VolatileBase->Size))) { + return EFI_INVALID_PARAMETER; + } + } + CopyMem ((UINT8 *) (UINTN) DataPtr, Buffer, DataSize); + + return EFI_SUCCESS; +} + + +/** + This code gets the current status of Variable Store. + + @param[in] 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. + +**/ +VARIABLE_STORE_STATUS +GetVariableStoreStatus ( + IN VARIABLE_STORE_HEADER *VarStoreHeader + ) +{ + if ((CompareGuid (&VarStoreHeader->Signature, &gEfiAuthenticatedVariableGuid) || + CompareGuid (&VarStoreHeader->Signature, &gEfiVariableGuid)) && + VarStoreHeader->Format == VARIABLE_STORE_FORMATTED && + VarStoreHeader->State == VARIABLE_STORE_HEALTHY + ) { + + return EfiValid; + } else if (((UINT32 *) (&VarStoreHeader->Signature))[0] == 0xffffffff && + ((UINT32 *) (&VarStoreHeader->Signature))[1] == 0xffffffff && + ((UINT32 *) (&VarStoreHeader->Signature))[2] == 0xffffffff && + ((UINT32 *) (&VarStoreHeader->Signature))[3] == 0xffffffff && + VarStoreHeader->Size == 0xffffffff && + VarStoreHeader->Format == 0xff && + VarStoreHeader->State == 0xff + ) { + return EfiRaw; + } else { + return EfiInvalid; + } +} + + +/** + This code gets the size of variable header. + + @return Size of variable header in bytes in type UINTN. + +**/ +UINTN +GetVariableHeaderSize ( + VOID + ) +{ + UINTN Value; + + if (mVariableModuleGlobal->VariableGlobal.AuthFormat) { + Value = sizeof (AUTHENTICATED_VARIABLE_HEADER); + } else { + Value = sizeof (VARIABLE_HEADER); + } + + return Value; +} + + +/** + This code gets the size of name of variable. + + @param[in] Variable Pointer to the Variable Header. + + @return UINTN Size of variable in bytes. + +**/ +UINTN +NameSizeOfVariable ( + IN VARIABLE_HEADER *Variable + ) +{ + AUTHENTICATED_VARIABLE_HEADER *AuthVariable; + + AuthVariable = (AUTHENTICATED_VARIABLE_HEADER *) Variable; + if (mVariableModuleGlobal->VariableGlobal.AuthFormat) { + if (AuthVariable->State == (UINT8) (-1) || + AuthVariable->DataSize == (UINT32) (-1) || + AuthVariable->NameSize == (UINT32) (-1) || + AuthVariable->Attributes == (UINT32) (-1)) { + return 0; + } + return (UINTN) AuthVariable->NameSize; + } else { + if (Variable->State == (UINT8) (-1) || + Variable->DataSize == (UINT32) (-1) || + Variable->NameSize == (UINT32) (-1) || + Variable->Attributes == (UINT32) (-1)) { + return 0; + } + return (UINTN) Variable->NameSize; + } +} + + +/** + This code sets the size of name of variable. + + @param[in] Variable Pointer to the Variable Header. + @param[in] NameSize Name size to set. + +**/ +VOID +SetNameSizeOfVariable ( + IN VARIABLE_HEADER *Variable, + IN UINTN NameSize + ) +{ + AUTHENTICATED_VARIABLE_HEADER *AuthVariable; + + AuthVariable = (AUTHENTICATED_VARIABLE_HEADER *) Variable; + if (mVariableModuleGlobal->VariableGlobal.AuthFormat) { + AuthVariable->NameSize = (UINT32) NameSize; + } else { + Variable->NameSize = (UINT32) NameSize; + } +} + + +/** + This code gets the size of variable data. + + @param[in] Variable Pointer to the Variable Header. + + @return Size of variable in bytes. + +**/ +UINTN +DataSizeOfVariable ( + IN VARIABLE_HEADER *Variable + ) +{ + AUTHENTICATED_VARIABLE_HEADER *AuthVariable; + + AuthVariable = (AUTHENTICATED_VARIABLE_HEADER *) Variable; + if (mVariableModuleGlobal->VariableGlobal.AuthFormat) { + if (AuthVariable->State == (UINT8) (-1) || + AuthVariable->DataSize == (UINT32) (-1) || + AuthVariable->NameSize == (UINT32) (-1) || + AuthVariable->Attributes == (UINT32) (-1)) { + return 0; + } + return (UINTN) AuthVariable->DataSize; + } else { + if (Variable->State == (UINT8) (-1) || + Variable->DataSize == (UINT32) (-1) || + Variable->NameSize == (UINT32) (-1) || + Variable->Attributes == (UINT32) (-1)) { + return 0; + } + return (UINTN) Variable->DataSize; + } +} + + +/** + This code sets the size of variable data. + + @param[in] Variable Pointer to the Variable Header. + @param[in] DataSize Data size to set. + +**/ +VOID +SetDataSizeOfVariable ( + IN VARIABLE_HEADER *Variable, + IN UINTN DataSize + ) +{ + AUTHENTICATED_VARIABLE_HEADER *AuthVariable; + + AuthVariable = (AUTHENTICATED_VARIABLE_HEADER *) Variable; + if (mVariableModuleGlobal->VariableGlobal.AuthFormat) { + AuthVariable->DataSize = (UINT32) DataSize; + } else { + Variable->DataSize = (UINT32) DataSize; + } +} + + +/** + This code gets the pointer to the variable name. + + @param Variable Pointer to the Variable Header. + + @return Pointer to Variable Name which is Unicode encoding. + +**/ +CHAR16 * +GetVariableNamePtr ( + IN VARIABLE_HEADER *Variable + ) +{ + return (CHAR16 *) ((UINTN) Variable + GetVariableHeaderSize ()); +} + + +/** + This code gets the pointer to the variable guid. + + @param Variable Pointer to the Variable Header. + + @return A EFI_GUID* pointer to Vendor Guid. + +**/ +EFI_GUID * +GetVendorGuidPtr ( + IN VARIABLE_HEADER *Variable + ) +{ + AUTHENTICATED_VARIABLE_HEADER *AuthVariable; + + AuthVariable = (AUTHENTICATED_VARIABLE_HEADER *) Variable; + if (mVariableModuleGlobal->VariableGlobal.AuthFormat) { + return &AuthVariable->VendorGuid; + } else { + return &Variable->VendorGuid; + } +} + + +/** + This code gets the pointer to the variable data. + + @param Variable Pointer to the Variable Header. + + @return Pointer to Variable Data. + +**/ +UINT8 * +GetVariableDataPtr ( + IN VARIABLE_HEADER *Variable + ) +{ + UINTN Value; + + // + // Be careful about pad size for alignment. + // + Value = (UINTN) GetVariableNamePtr (Variable); + Value += NameSizeOfVariable (Variable); + Value += GET_PAD_SIZE (NameSizeOfVariable (Variable)); + + return (UINT8 *) Value; +} + + +/** + This code gets the variable data offset related to variable header. + + @param[in] Variable Pointer to the Variable Header. + + @return Variable Data offset. + +**/ +UINTN +GetVariableDataOffset ( + IN VARIABLE_HEADER *Variable + ) +{ + UINTN Value; + + // + // Be careful about pad size for alignment + // + Value = GetVariableHeaderSize (); + Value += NameSizeOfVariable (Variable); + Value += GET_PAD_SIZE (NameSizeOfVariable (Variable)); + + return Value; +} + + +/** + This code gets the pointer to the next variable header. + + @param[in] Variable Pointer to the Variable Header. + + @return Pointer to next variable header. + +**/ +VARIABLE_HEADER * +GetNextVariablePtr ( + IN VARIABLE_HEADER *Variable + ) +{ + UINTN Value; + + Value = (UINTN) GetVariableDataPtr (Variable); + Value += DataSizeOfVariable (Variable); + Value += GET_PAD_SIZE (DataSizeOfVariable (Variable)); + + // + // Be careful about pad size for alignment. + // + return (VARIABLE_HEADER *) HEADER_ALIGN (Value); +} + + +/** + Gets the pointer to the first variable header in given variable store area. + + @param[in] VarStoreHeader Pointer to the Variable Store Header. + + @return Pointer to the first variable header. + +**/ +VARIABLE_HEADER * +GetStartPointer ( + IN VARIABLE_STORE_HEADER *VarStoreHeader + ) +{ + // + // The end of variable store. + // + return (VARIABLE_HEADER *) HEADER_ALIGN (VarStoreHeader + 1); +} + + +/** + Gets the pointer to the end of the variable storage area. + + This function gets pointer to the end of the variable storage + area, according to the input variable store header. + + @param[in] VarStoreHeader Pointer to the Variable Store Header. + + @return Pointer to the end of the variable storage area. + +**/ +VARIABLE_HEADER * +GetEndPointer ( + IN VARIABLE_STORE_HEADER *VarStoreHeader + ) +{ + // + // The end of variable store + // + return (VARIABLE_HEADER *) HEADER_ALIGN ((UINTN) VarStoreHeader + VarStoreHeader->Size); +} + + +/** + Gets the VARIABLE_STORAGE_PROTOCOL for the NV Storage that should store + a variable with the given name and GUID + + @param[in] VariableName Name of the variable to be found. + @param[in] VendorGuid Vendor GUID to be found. + @param[out] VariableStorageProtocol The found VARIABLE_STORAGE_PROTOCOL + + @retval EFI_INVALID_PARAMETER If VariableName is an empty string, or + VendorGuid is NULL. + @retval EFI_SUCCESS VARIABLE_STORAGE_PROTOCOL successfully found. + @retval EFI_NOT_FOUND VARIABLE_STORAGE_PROTOCOL not found + +**/ +EFI_STATUS +GetVariableStorageProtocol ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + OUT VARIABLE_STORAGE_PROTOCOL **VariableStorageProtocol + ) +{ + EFI_GUID VariableStorageId; + EFI_GUID InstanceGuid; + VARIABLE_STORAGE_PROTOCOL *CurrentInstance; + UINTN Index; + EFI_STATUS Status; + + if (VariableName[0] == 0 || VendorGuid == NULL) { + return EFI_INVALID_PARAMETER; + } + *VariableStorageProtocol = NULL; + ZeroMem ((VOID *) &VariableStorageId, sizeof (EFI_GUID)); + Status = GetVariableStorageId (VariableName, VendorGuid, TRUE, &VariableStorageId); + if (EFI_ERROR (Status)) { + return Status; + } + for ( Index = 0; + Index < mVariableModuleGlobal->VariableGlobal.VariableStoresCount; + Index++) { + CurrentInstance = mVariableModuleGlobal->VariableGlobal.VariableStores[Index]; + ZeroMem ((VOID *) &InstanceGuid, sizeof (EFI_GUID)); + Status = CurrentInstance->GetId (&InstanceGuid); + if (EFI_ERROR (Status)) { + return Status; + } + if (CompareGuid (&VariableStorageId, &InstanceGuid)) { + *VariableStorageProtocol = CurrentInstance; + return EFI_SUCCESS; + } + } + return EFI_NOT_FOUND; +} + + +/** + Record variable error flag. + + @param[in] Flag Variable error flag to record. + @param[in] VariableName Name of variable. + @param[in] VendorGuid Guid of variable. + @param[in] Attributes Attributes of the variable. + @param[in] VariableSize Size of the variable. + +**/ +VOID +RecordVarErrorFlag ( + IN VAR_ERROR_FLAG Flag, + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN UINT32 Attributes, + IN UINTN VariableSize + ) +{ + EFI_STATUS Status; + VARIABLE_POINTER_TRACK Variable; + VAR_ERROR_FLAG *VarErrFlag; + VAR_ERROR_FLAG TempFlag; + VARIABLE_STORAGE_PROTOCOL *VariableStorageProtocol; + + DEBUG_CODE ( + DEBUG ((EFI_D_ERROR, " Variable Driver: RecordVarErrorFlag (0x%02x) %s:%g - 0x%08x - 0x%x\n", Flag, VariableName, VendorGuid, Attributes, VariableSize)); + if (Flag == VAR_ERROR_FLAG_SYSTEM_ERROR) { + if (AtRuntime ()) { + DEBUG ((EFI_D_ERROR, " Variable Driver: CommonRuntimeVariableSpace = 0x%x - CommonVariableTotalSize = 0x%x\n", mVariableModuleGlobal->CommonRuntimeVariableSpace, mVariableModuleGlobal->CommonVariableTotalSize)); + } else { + DEBUG ((EFI_D_ERROR, " Variable Driver: CommonVariableSpace = 0x%x - CommonVariableTotalSize = 0x%x\n", mVariableModuleGlobal->CommonVariableSpace, mVariableModuleGlobal->CommonVariableTotalSize)); + } + } else { + DEBUG ((EFI_D_ERROR, " Variable Driver: CommonMaxUserVariableSpace = 0x%x - CommonUserVariableTotalSize = 0x%x\n", mVariableModuleGlobal->CommonMaxUserVariableSpace, mVariableModuleGlobal->CommonUserVariableTotalSize)); + } + ); + + if (!mEndOfDxe) { + // + // Before EndOfDxe, just record the current boot variable error flag to local variable, + // and leave the variable error flag in NV flash as the last boot variable error flag. + // After EndOfDxe in InitializeVarErrorFlag (), the variable error flag in NV flash + // will be initialized to this local current boot variable error flag. + // + mCurrentBootVarErrFlag &= Flag; + return; + } + + // + // Record error flag (it should have be initialized). + // + Status = FindVariable ( + VAR_ERROR_FLAG_NAME, + &gEdkiiVarErrorFlagGuid, + &Variable, + &mVariableModuleGlobal->VariableGlobal, + FALSE + ); + if (!EFI_ERROR (Status)) { + VarErrFlag = (VAR_ERROR_FLAG *) GetVariableDataPtr (Variable.CurrPtr); + TempFlag = *VarErrFlag; + TempFlag &= Flag; + if (TempFlag == *VarErrFlag) { + return; + } + VariableStorageProtocol = NULL; + Status = GetVariableStorageProtocol ( + VAR_ERROR_FLAG_NAME, + &gEdkiiVarErrorFlagGuid, + &VariableStorageProtocol + ); + if (!EFI_ERROR (Status) && VariableStorageProtocol != NULL) { + // + // Update the data in NV + // + if (VariableStorageProtocol->WriteServiceIsReady ( + VariableStorageProtocol)) { + Status = VariableStorageProtocol->SetVariable ( + VariableStorageProtocol, + VAR_ERROR_FLAG_NAME, + &gEdkiiVarErrorFlagGuid, + VARIABLE_ATTRIBUTE_NV_BS_RT, + sizeof (TempFlag), + &TempFlag, + AtRuntime (), + 0, + 0, + NULL + ); + if (!EFI_ERROR (Status)) { + // + // Update the data in NV cache. + // + *VarErrFlag = TempFlag; + } + } + } + } +} + + +/** + Initialize variable error flag. + + Before EndOfDxe, the variable indicates the last boot variable error flag, + then it means the last boot variable error flag must be got before EndOfDxe. + After EndOfDxe, the variable indicates the current boot variable error flag, + then it means the current boot variable error flag must be got after EndOfDxe. + +**/ +VOID +InitializeVarErrorFlag ( + VOID + ) +{ + EFI_STATUS Status; + VARIABLE_POINTER_TRACK Variable; + VAR_ERROR_FLAG Flag; + VAR_ERROR_FLAG VarErrFlag; + + if (!mEndOfDxe) { + return; + } + + Flag = mCurrentBootVarErrFlag; + DEBUG ((EFI_D_INFO, " Variable Driver: Initialize variable error flag (%02x)\n", Flag)); + + Status = FindVariable ( + VAR_ERROR_FLAG_NAME, + &gEdkiiVarErrorFlagGuid, + &Variable, + &mVariableModuleGlobal->VariableGlobal, + FALSE + ); + if (!EFI_ERROR (Status)) { + VarErrFlag = *((VAR_ERROR_FLAG *) GetVariableDataPtr (Variable.CurrPtr)); + if (VarErrFlag == Flag) { + return; + } + } + + UpdateVariable ( + VAR_ERROR_FLAG_NAME, + &gEdkiiVarErrorFlagGuid, + &Flag, + sizeof (Flag), + VARIABLE_ATTRIBUTE_NV_BS_RT, + 0, + 0, + &Variable, + NULL + ); +} + + +/** + Is user variable? + + @param[in] Variable Pointer to variable header. + + @retval TRUE User variable. + @retval FALSE System variable. + +**/ +BOOLEAN +IsUserVariable ( + IN VARIABLE_HEADER *Variable + ) +{ + VAR_CHECK_VARIABLE_PROPERTY Property; + + // + // Only after End Of Dxe, the variables belong to system variable are fixed. + // If PcdMaxUserNvStorageVariableSize is 0, it means user variable share the same NV storage with system variable, + // then no need to check if the variable is user variable or not specially. + // + if (mEndOfDxe && (mVariableModuleGlobal->CommonMaxUserVariableSpace != mVariableModuleGlobal->CommonVariableSpace)) { + if (VarCheckLibVariablePropertyGet (GetVariableNamePtr (Variable), GetVendorGuidPtr (Variable), &Property) == EFI_NOT_FOUND) { + return TRUE; + } + } + return FALSE; +} + + +/** + Calculate common user variable total size. + +**/ +VOID +CalculateCommonUserVariableTotalSize ( + VOID + ) +{ + VARIABLE_HEADER *Variable; + VARIABLE_HEADER *NextVariable; + UINTN VariableSize; + VAR_CHECK_VARIABLE_PROPERTY Property; + + // + // Only after End Of Dxe, the variables belong to system variable are fixed. + // If PcdMaxUserNvStorageVariableSize is 0, it means user variable share the same NV storage with system variable, + // then no need to calculate the common user variable total size specially. + // + if (mEndOfDxe && (mVariableModuleGlobal->CommonMaxUserVariableSpace != mVariableModuleGlobal->CommonVariableSpace)) { + Variable = GetStartPointer (mNvVariableCache); + while (IsValidVariableHeader (Variable, GetEndPointer (mNvVariableCache))) { + NextVariable = GetNextVariablePtr (Variable); + VariableSize = (UINTN) NextVariable - (UINTN) Variable; + if ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != EFI_VARIABLE_HARDWARE_ERROR_RECORD) { + if (VarCheckLibVariablePropertyGet (GetVariableNamePtr (Variable), GetVendorGuidPtr (Variable), &Property) == EFI_NOT_FOUND) { + // + // No property, it is user variable. + // + mVariableModuleGlobal->CommonUserVariableTotalSize += VariableSize; + } + } + + Variable = NextVariable; + } + } +} + + +/** + Initialize variable quota. + +**/ +VOID +InitializeVariableQuota ( + VOID + ) +{ + if (!mEndOfDxe) { + return; + } + + InitializeVarErrorFlag (); + CalculateCommonUserVariableTotalSize (); +} + + +/** + Find the variable in the specified variable store. + + @param[in] VariableName Name of the variable to be found + @param[in] VendorGuid Vendor GUID to be found. + @param[in] IgnoreRtCheck Ignore EFI_VARIABLE_RUNTIME_ACCESS attribute + check at runtime when searching variable. + @param[in, out] PtrTrack Variable Track Pointer structure that contains Variable Information. + + @retval EFI_SUCCESS Variable found successfully + @retval EFI_NOT_FOUND Variable not found +**/ +EFI_STATUS +FindVariableEx ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN BOOLEAN IgnoreRtCheck, + IN OUT VARIABLE_POINTER_TRACK *PtrTrack + ) +{ + VARIABLE_HEADER *InDeletedVariable; + VOID *Point; + + PtrTrack->InDeletedTransitionPtr = NULL; + + // + // Find the variable by walk through HOB, volatile and non-volatile variable store. + // + InDeletedVariable = NULL; + + for ( PtrTrack->CurrPtr = PtrTrack->StartPtr + ; IsValidVariableHeader (PtrTrack->CurrPtr, PtrTrack->EndPtr) + ; PtrTrack->CurrPtr = GetNextVariablePtr (PtrTrack->CurrPtr) + ) { + if (PtrTrack->CurrPtr->State == VAR_ADDED || + PtrTrack->CurrPtr->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED) + ) { + if (IgnoreRtCheck || !AtRuntime () || ((PtrTrack->CurrPtr->Attributes & EFI_VARIABLE_RUNTIME_ACCESS) != 0)) { + if (VariableName[0] == 0) { + if (PtrTrack->CurrPtr->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED)) { + InDeletedVariable = PtrTrack->CurrPtr; + } else { + PtrTrack->InDeletedTransitionPtr = InDeletedVariable; + return EFI_SUCCESS; + } + } else { + if (CompareGuid (VendorGuid, GetVendorGuidPtr (PtrTrack->CurrPtr))) { + Point = (VOID *) GetVariableNamePtr (PtrTrack->CurrPtr); + + ASSERT (NameSizeOfVariable (PtrTrack->CurrPtr) != 0); + if (CompareMem (VariableName, Point, NameSizeOfVariable (PtrTrack->CurrPtr)) == 0) { + if (PtrTrack->CurrPtr->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED)) { + InDeletedVariable = PtrTrack->CurrPtr; + } else { + PtrTrack->InDeletedTransitionPtr = InDeletedVariable; + return EFI_SUCCESS; + } + } + } + } + } + } + } + + PtrTrack->CurrPtr = InDeletedVariable; + return (PtrTrack->CurrPtr == NULL) ? EFI_NOT_FOUND : EFI_SUCCESS; +} + + +/** + Variable store garbage collection and reclaim operation. + + @param[in] VariableBase Base address of variable store. + @param[out] LastVariableOffset Offset of last variable. + @param[in] IsVolatile The variable store is volatile or not; + if it is non-volatile, need FTW. + @param[in, out] UpdatingPtrTrack Pointer to updating variable pointer track structure. + @param[in] NewVariable Pointer to new variable. + @param[in] NewVariableSize New variable size. + + @return EFI_SUCCESS Reclaim operation has finished successfully. + @return EFI_OUT_OF_RESOURCES No enough memory resources or variable space. + @return Others Unexpect error happened during reclaim operation. + +**/ +EFI_STATUS +Reclaim ( + IN EFI_PHYSICAL_ADDRESS VariableBase, + OUT UINTN *LastVariableOffset, + IN BOOLEAN IsVolatile, + IN OUT VARIABLE_POINTER_TRACK *UpdatingPtrTrack, + IN VARIABLE_HEADER *NewVariable, + IN UINTN NewVariableSize + ) +{ + VARIABLE_HEADER *Variable; + VARIABLE_HEADER *AddedVariable; + VARIABLE_HEADER *NextVariable; + VARIABLE_HEADER *NextAddedVariable; + VARIABLE_STORE_HEADER *VariableStoreHeader; + UINT8 *ValidBuffer; + UINTN MaximumBufferSize; + UINTN VariableSize; + UINTN NameSize; + UINT8 *CurrPtr; + VOID *Point0; + VOID *Point1; + BOOLEAN FoundAdded; + EFI_STATUS Status; + UINTN CommonVariableTotalSize; + UINTN CommonUserVariableTotalSize; + UINTN HwErrVariableTotalSize; + VARIABLE_HEADER *UpdatingVariable; + VARIABLE_HEADER *UpdatingInDeletedTransition; + AUTHENTICATED_VARIABLE_HEADER *AuthVariable; + VARIABLE_STORAGE_PROTOCOL *VariableStorageProtocol; + VARIABLE_POINTER_TRACK VarErrFlagPtr; + + Status = EFI_SUCCESS; + + UpdatingVariable = NULL; + UpdatingInDeletedTransition = NULL; + + if (UpdatingPtrTrack != NULL) { + UpdatingVariable = UpdatingPtrTrack->CurrPtr; + UpdatingInDeletedTransition = UpdatingPtrTrack->InDeletedTransitionPtr; + } + + VariableStoreHeader = (VARIABLE_STORE_HEADER *) ((UINTN) VariableBase); + + CommonVariableTotalSize = 0; + CommonUserVariableTotalSize = 0; + HwErrVariableTotalSize = 0; + + if (IsVolatile || mForceVolatileVariable) { + // + // Start Pointers for the variable. + // + Variable = GetStartPointer (VariableStoreHeader); + MaximumBufferSize = sizeof (VARIABLE_STORE_HEADER); + + while (IsValidVariableHeader (Variable, GetEndPointer (VariableStoreHeader))) { + NextVariable = GetNextVariablePtr (Variable); + if ((Variable->State == VAR_ADDED || Variable->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED)) && + Variable != UpdatingVariable && + Variable != UpdatingInDeletedTransition + ) { + VariableSize = (UINTN) NextVariable - (UINTN) Variable; + MaximumBufferSize += VariableSize; + } + + Variable = NextVariable; + } + + if (NewVariable != NULL) { + // + // Add the new variable size. + // + MaximumBufferSize += NewVariableSize; + } + + // + // Reserve the 1 Bytes with Oxff to identify the + // end of the variable buffer. + // + MaximumBufferSize += 1; + ValidBuffer = AllocatePool (MaximumBufferSize); + if (ValidBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + SetMem (ValidBuffer, MaximumBufferSize, 0xff); + + // + // 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. + // + Variable = GetStartPointer (VariableStoreHeader); + while (IsValidVariableHeader (Variable, GetEndPointer (VariableStoreHeader))) { + NextVariable = GetNextVariablePtr (Variable); + if (Variable != UpdatingVariable && Variable->State == VAR_ADDED) { + VariableSize = (UINTN) NextVariable - (UINTN) Variable; + CopyMem (CurrPtr, (UINT8 *) Variable, VariableSize); + CurrPtr += VariableSize; + if ((!IsVolatile) && ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD)) { + HwErrVariableTotalSize += VariableSize; + } else if ((!IsVolatile) && ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != EFI_VARIABLE_HARDWARE_ERROR_RECORD)) { + CommonVariableTotalSize += VariableSize; + if (IsUserVariable (Variable)) { + CommonUserVariableTotalSize += VariableSize; + } + } + } + Variable = NextVariable; + } + + // + // Reinstall all in delete transition variables. + // + Variable = GetStartPointer (VariableStoreHeader); + while (IsValidVariableHeader (Variable, GetEndPointer (VariableStoreHeader))) { + NextVariable = GetNextVariablePtr (Variable); + if (Variable != UpdatingVariable && Variable != UpdatingInDeletedTransition && Variable->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED)) { + // + // Buffer has cached all ADDED variable. + // Per IN_DELETED variable, we have to guarantee that + // no ADDED one in previous buffer. + // + FoundAdded = FALSE; + AddedVariable = GetStartPointer ((VARIABLE_STORE_HEADER *) ValidBuffer); + while (IsValidVariableHeader (AddedVariable, GetEndPointer ((VARIABLE_STORE_HEADER *) ValidBuffer))) { + NextAddedVariable = GetNextVariablePtr (AddedVariable); + NameSize = NameSizeOfVariable (AddedVariable); + if (CompareGuid (GetVendorGuidPtr (AddedVariable), GetVendorGuidPtr (Variable)) && + NameSize == NameSizeOfVariable (Variable) + ) { + Point0 = (VOID *) GetVariableNamePtr (AddedVariable); + Point1 = (VOID *) GetVariableNamePtr (Variable); + if (CompareMem (Point0, Point1, NameSize) == 0) { + FoundAdded = TRUE; + break; + } + } + AddedVariable = NextAddedVariable; + } + if (!FoundAdded) { + // + // Promote VAR_IN_DELETED_TRANSITION to VAR_ADDED. + // + VariableSize = (UINTN) NextVariable - (UINTN) Variable; + CopyMem (CurrPtr, (UINT8 *) Variable, VariableSize); + ((VARIABLE_HEADER *) CurrPtr)->State = VAR_ADDED; + CurrPtr += VariableSize; + if ((!IsVolatile) && ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD)) { + HwErrVariableTotalSize += VariableSize; + } else if ((!IsVolatile) && ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != EFI_VARIABLE_HARDWARE_ERROR_RECORD)) { + CommonVariableTotalSize += VariableSize; + if (IsUserVariable (Variable)) { + CommonUserVariableTotalSize += VariableSize; + } + } + } + } + + Variable = NextVariable; + } + } else { + // + // If we are reclaiming the NV cache, just clear it out. The cache will be rebuilt from NV as needed + // + ValidBuffer = (UINT8 *) mNvVariableCache; + VarErrFlagPtr.CurrPtr = NULL; + + // + // Compute the changes to NV storage usage + // + HwErrVariableTotalSize = mVariableModuleGlobal->HwErrVariableTotalSize; + CommonVariableTotalSize = mVariableModuleGlobal->CommonVariableTotalSize; + CommonUserVariableTotalSize = mVariableModuleGlobal->CommonUserVariableTotalSize; + if (NewVariable != NULL && UpdatingVariable != NULL) { + NextVariable = GetNextVariablePtr (UpdatingPtrTrack->CurrPtr); + VariableSize = (UINTN) NextVariable - (UINTN) UpdatingPtrTrack->CurrPtr; + if ((UpdatingPtrTrack->CurrPtr->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) { + HwErrVariableTotalSize -= VariableSize; + } else if ((UpdatingPtrTrack->CurrPtr->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != EFI_VARIABLE_HARDWARE_ERROR_RECORD) { + CommonVariableTotalSize -= VariableSize; + if (IsUserVariable (UpdatingPtrTrack->CurrPtr)) { + CommonUserVariableTotalSize -= VariableSize; + } + } + } + + // + // Retain VarErrorFlag in the NV cache + // + if (mEndOfDxe) { + VarErrFlagPtr.StartPtr = GetStartPointer (VariableStoreHeader); + VarErrFlagPtr.EndPtr = GetEndPointer (VariableStoreHeader); + VarErrFlagPtr.Volatile = FALSE; + if (IsValidVariableHeader (VarErrFlagPtr.StartPtr, VarErrFlagPtr.EndPtr)) { + Status = FindVariableEx ( + VAR_ERROR_FLAG_NAME, + &gEdkiiVarErrorFlagGuid, + TRUE, + &VarErrFlagPtr + ); + if (!EFI_ERROR (Status) && VarErrFlagPtr.CurrPtr != NULL) { + NextVariable = GetNextVariablePtr (VarErrFlagPtr.CurrPtr); + VariableSize = (UINTN) NextVariable - (UINTN) VarErrFlagPtr.CurrPtr; + } else { + VarErrFlagPtr.CurrPtr = NULL; + } + } + } + + // + // Delete all existing data + // + Point0 = (VOID *) GetStartPointer (VariableStoreHeader); + Point1 = (VOID *) GetEndPointer (VariableStoreHeader); + CurrPtr = (UINT8 *) Point0; + if (VarErrFlagPtr.CurrPtr != NULL && IsValidVariableHeader (Point0, Point1)) { + CopyMem (Point0, VarErrFlagPtr.CurrPtr, VariableSize); + Point0 = (UINT8 *) GetNextVariablePtr ((VARIABLE_HEADER *) Point0); + } + + if (Point1 > Point0) { + MaximumBufferSize = (UINTN) Point1 - (UINTN) Point0; + SetMem (Point0, MaximumBufferSize, 0xff); + } + } + + // + // Install the new variable if it is not NULL. + // + if (NewVariable != NULL) { + if ((UINTN) (CurrPtr - ValidBuffer) + NewVariableSize > VariableStoreHeader->Size) { + // + // No enough space to store the new variable. + // + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + if (!IsVolatile) { + if ((NewVariable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) { + HwErrVariableTotalSize += NewVariableSize; + } else if ((NewVariable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != EFI_VARIABLE_HARDWARE_ERROR_RECORD) { + CommonVariableTotalSize += NewVariableSize; + if (IsUserVariable (NewVariable)) { + CommonUserVariableTotalSize += NewVariableSize; + } + } + if ((HwErrVariableTotalSize > PcdGet32 (PcdHwErrStorageSize)) || + (CommonVariableTotalSize > mVariableModuleGlobal->CommonVariableSpace) || + (CommonUserVariableTotalSize > mVariableModuleGlobal->CommonMaxUserVariableSpace)) { + // + // No enough space to store the new variable by NV or NV+HR attribute. + // + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + // + // Update the data in NV + // + if (!mForceVolatileVariable) { + VariableStorageProtocol = NULL; + Status = GetVariableStorageProtocol ( + GetVariableNamePtr (NewVariable), + GetVendorGuidPtr (NewVariable), + &VariableStorageProtocol + ); + if (!EFI_ERROR (Status) && VariableStorageProtocol != NULL) { + if (VariableStorageProtocol->WriteServiceIsReady (VariableStorageProtocol)) { + if (mVariableModuleGlobal->VariableGlobal.AuthFormat) { + AuthVariable = (AUTHENTICATED_VARIABLE_HEADER *) NewVariable; + Status = VariableStorageProtocol->SetVariable ( + VariableStorageProtocol, + GetVariableNamePtr (NewVariable), + GetVendorGuidPtr (NewVariable), + NewVariable->Attributes, + DataSizeOfVariable (NewVariable), + GetVariableDataPtr (NewVariable), + AtRuntime (), + AuthVariable->PubKeyIndex, + ReadUnaligned64 (&(AuthVariable->MonotonicCount)), + &AuthVariable->TimeStamp + ); + } else { + Status = VariableStorageProtocol->SetVariable ( + VariableStorageProtocol, + GetVariableNamePtr (NewVariable), + GetVendorGuidPtr (NewVariable), + NewVariable->Attributes, + DataSizeOfVariable (NewVariable), + GetVariableDataPtr (NewVariable), + AtRuntime (), + 0, + 0, + NULL + ); + } + if (EFI_ERROR (Status)) { + goto Done; + } + } else { + Status = EFI_NOT_AVAILABLE_YET; + goto Done; + } + } else { + if (!EFI_ERROR (Status)) { + Status = EFI_NOT_FOUND; + } + goto Done; + } + } + } + + CopyMem (CurrPtr, (UINT8 *) NewVariable, NewVariableSize); + ((VARIABLE_HEADER *) CurrPtr)->State = VAR_ADDED; + if (UpdatingVariable != NULL) { + UpdatingPtrTrack->CurrPtr = (VARIABLE_HEADER *)((UINTN)UpdatingPtrTrack->StartPtr + ((UINTN)CurrPtr - (UINTN)GetStartPointer ((VARIABLE_STORE_HEADER *) ValidBuffer))); + UpdatingPtrTrack->InDeletedTransitionPtr = NULL; + } + CurrPtr += NewVariableSize; + } + + *LastVariableOffset = (UINTN) (CurrPtr - ValidBuffer); + if (IsVolatile) { + // + // If volatile variable store, just copy valid buffer. + // + SetMem ((UINT8 *) (UINTN) VariableBase, VariableStoreHeader->Size, 0xff); + CopyMem ((UINT8 *) (UINTN) VariableBase, ValidBuffer, (UINTN) (CurrPtr - ValidBuffer)); + Status = EFI_SUCCESS; + } else { + // + // If non-volatile variable store, update NV storage usage + // + mVariableModuleGlobal->HwErrVariableTotalSize = HwErrVariableTotalSize; + mVariableModuleGlobal->CommonVariableTotalSize = CommonVariableTotalSize; + mVariableModuleGlobal->CommonUserVariableTotalSize = CommonUserVariableTotalSize; + } + +Done: + if (IsVolatile || mForceVolatileVariable) { + FreePool (ValidBuffer); + } + + return Status; +} + + +/** + Get index from supported language codes according to language string. + + This code is used to get corresponding index in supported language codes. It can handle + RFC4646 and ISO639 language tags. + In ISO639 language tags, take 3-characters as a delimitation to find matched string and calculate the index. + In RFC4646 language tags, take semicolon as a delimitation to find matched string and calculate the index. + + For example: + SupportedLang = "engfraengfra" + Lang = "eng" + Iso639Language = TRUE + The return value is "0". + Another example: + SupportedLang = "en;fr;en-US;fr-FR" + Lang = "fr-FR" + Iso639Language = FALSE + The return value is "3". + + @param[in] SupportedLang Platform supported language codes. + @param[in] Lang Configured language. + @param[in] Iso639Language A bool value to signify if the handler is operated on ISO639 or RFC4646. + + @retval The index of language in the language codes. + +**/ +UINTN +GetIndexFromSupportedLangCodes ( + IN CHAR8 *SupportedLang, + IN CHAR8 *Lang, + IN BOOLEAN Iso639Language + ) +{ + UINTN Index; + UINTN CompareLength; + UINTN LanguageLength; + + if (Iso639Language) { + CompareLength = ISO_639_2_ENTRY_SIZE; + for (Index = 0; Index < AsciiStrLen (SupportedLang); Index += CompareLength) { + if (AsciiStrnCmp (Lang, SupportedLang + Index, CompareLength) == 0) { + // + // Successfully find the index of Lang string in SupportedLang string. + // + Index = Index / CompareLength; + return Index; + } + } + ASSERT (FALSE); + return 0; + } else { + // + // Compare RFC4646 language code + // + Index = 0; + for (LanguageLength = 0; Lang[LanguageLength] != '\0'; LanguageLength++); + + for (Index = 0; *SupportedLang != '\0'; Index++, SupportedLang += CompareLength) { + // + // Skip ';' characters in SupportedLang + // + for (; *SupportedLang != '\0' && *SupportedLang == ';'; SupportedLang++); + // + // Determine the length of the next language code in SupportedLang + // + for (CompareLength = 0; SupportedLang[CompareLength] != '\0' && SupportedLang[CompareLength] != ';'; CompareLength++); + + if ((CompareLength == LanguageLength) && + (AsciiStrnCmp (Lang, SupportedLang, CompareLength) == 0)) { + // + // Successfully find the index of Lang string in SupportedLang string. + // + return Index; + } + } + ASSERT (FALSE); + return 0; + } +} + + +/** + Get language string from supported language codes according to index. + + 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. + + For example: + SupportedLang = "engfraengfra" + Index = "1" + Iso639Language = TRUE + The return value is "fra". + Another example: + SupportedLang = "en;fr;en-US;fr-FR" + Index = "1" + Iso639Language = FALSE + The return value is "fr". + + @param[in] SupportedLang Platform supported language codes. + @param[in] Index The index in supported language codes. + @param[in] Iso639Language A bool value to signify if the handler is operated on ISO639 or RFC4646. + + @retval The language string in the language codes. + +**/ +CHAR8 * +GetLangFromSupportedLangCodes ( + IN CHAR8 *SupportedLang, + IN UINTN Index, + IN BOOLEAN Iso639Language + ) +{ + UINTN SubIndex; + UINTN CompareLength; + CHAR8 *Supported; + + SubIndex = 0; + Supported = SupportedLang; + if (Iso639Language) { + // + // 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. + // + for (CompareLength = 0; *Supported != ';' && *Supported != '\0'; CompareLength++) { + Supported++; + } + if ((*Supported == '\0') && (SubIndex != Index)) { + // + // Have completed the traverse, but not find corrsponding string. + // This case is not allowed to happen. + // + ASSERT(FALSE); + return NULL; + } + if (SubIndex == Index) { + // + // 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. + // + mVariableModuleGlobal->PlatformLang[CompareLength] = '\0'; + return CopyMem (mVariableModuleGlobal->PlatformLang, Supported - CompareLength, CompareLength); + } + SubIndex++; + + // + // Skip ';' characters in Supported + // + for (; *Supported != '\0' && *Supported == ';'; Supported++); + } + } +} + + +/** + Returns a pointer to an allocated buffer that contains the best matching language + from a set of supported languages. + + This function supports both ISO 639-2 and RFC 4646 language codes, but language + code types may not be mixed in a single call to this function. This function + supports a variable argument list that allows the caller to pass in a prioritized + list of language codes to test against all the language codes in SupportedLanguages. + + If SupportedLanguages is NULL, then ASSERT(). + + @param[in] SupportedLanguages A pointer to a Null-terminated ASCII string that + contains a set of language codes in the format + specified by Iso639Language. + @param[in] Iso639Language If TRUE, then all language codes are assumed to be + in ISO 639-2 format. If FALSE, then all language + codes are assumed to be in RFC 4646 language format + @param[in] ... A variable argument list that contains pointers to + Null-terminated ASCII strings that contain one or more + language codes in the format specified by Iso639Language. + The first language code from each of these language + code lists is used to determine if it is an exact or + close match to any of the language codes in + SupportedLanguages. Close matches only apply to RFC 4646 + language codes, and the matching algorithm from RFC 4647 + is used to determine if a close match is present. If + an exact or close match is found, then the matching + language code from SupportedLanguages is returned. If + no matches are found, then the next variable argument + parameter is evaluated. The variable argument list + is terminated by a NULL. + + @retval NULL The best matching language could not be found in SupportedLanguages. + @retval NULL There are not enough resources available to return the best matching + language. + @retval Other A pointer to a Null-terminated ASCII string that is the best matching + language in SupportedLanguages. + +**/ +CHAR8 * +EFIAPI +VariableGetBestLanguage ( + IN CONST CHAR8 *SupportedLanguages, + IN BOOLEAN Iso639Language, + ... + ) +{ + VA_LIST Args; + CHAR8 *Language; + UINTN CompareLength; + UINTN LanguageLength; + CONST CHAR8 *Supported; + CHAR8 *Buffer; + + if (SupportedLanguages == NULL) { + return NULL; + } + + VA_START (Args, Iso639Language); + while ((Language = VA_ARG (Args, CHAR8 *)) != NULL) { + // + // Default to ISO 639-2 mode + // + CompareLength = 3; + LanguageLength = MIN (3, AsciiStrLen (Language)); + + // + // If in RFC 4646 mode, then determine the length of the first RFC 4646 language code in Language + // + if (!Iso639Language) { + for (LanguageLength = 0; Language[LanguageLength] != 0 && Language[LanguageLength] != ';'; LanguageLength++); + } + + // + // Trim back the length of Language used until it is empty + // + while (LanguageLength > 0) { + // + // Loop through all language codes in SupportedLanguages + // + for (Supported = SupportedLanguages; *Supported != '\0'; Supported += CompareLength) { + // + // In RFC 4646 mode, then Loop through all language codes in SupportedLanguages + // + if (!Iso639Language) { + // + // Skip ';' characters in Supported + // + for (; *Supported != '\0' && *Supported == ';'; Supported++); + // + // Determine the length of the next language code in Supported + // + for (CompareLength = 0; Supported[CompareLength] != 0 && Supported[CompareLength] != ';'; CompareLength++); + // + // If Language is longer than the Supported, then skip to the next language + // + if (LanguageLength > CompareLength) { + continue; + } + } + // + // See if the first LanguageLength characters in Supported match Language + // + if (AsciiStrnCmp (Supported, Language, LanguageLength) == 0) { + VA_END (Args); + + Buffer = Iso639Language ? mVariableModuleGlobal->Lang : mVariableModuleGlobal->PlatformLang; + Buffer[CompareLength] = '\0'; + return CopyMem (Buffer, Supported, CompareLength); + } + } + + if (Iso639Language) { + // + // If ISO 639 mode, then each language can only be tested once + // + LanguageLength = 0; + } else { + // + // If RFC 4646 mode, then trim Language from the right to the next '-' character + // + for (LanguageLength--; LanguageLength > 0 && Language[LanguageLength] != '-'; LanguageLength--); + } + } + } + VA_END (Args); + + // + // No matches were found + // + return NULL; +} + + +/** + This function is to check if the remaining variable space is enough to set + all Variables from argument list successfully. The purpose of the check + is to keep the consistency of the Variables to be in variable storage. + + Note: Variables are assumed to be in same storage. + The set sequence of Variables will be same with the sequence of VariableEntry from argument list, + so follow the argument sequence to check the Variables. + + @param[in] Attributes Variable attributes for Variable entries. + @param[in] Marker VA_LIST style variable argument list. + The variable argument list with type VARIABLE_ENTRY_CONSISTENCY *. + A NULL terminates the list. The VariableSize of + VARIABLE_ENTRY_CONSISTENCY is the variable data size as input. + It will be changed to variable total size as output. + + @retval TRUE Have enough variable space to set the Variables successfully. + @retval FALSE No enough variable space to set the Variables successfully. + +**/ +BOOLEAN +EFIAPI +CheckRemainingSpaceForConsistencyInternal ( + IN UINT32 Attributes, + IN VA_LIST Marker + ) +{ + EFI_STATUS Status; + VA_LIST Args; + VARIABLE_ENTRY_CONSISTENCY *VariableEntry; + UINT64 MaximumVariableStorageSize; + UINT64 RemainingVariableStorageSize; + UINT64 MaximumVariableSize; + UINTN TotalNeededSize; + UINTN OriginalVarSize; + VARIABLE_STORE_HEADER *VariableStoreHeader; + VARIABLE_POINTER_TRACK VariablePtrTrack; + VARIABLE_HEADER *NextVariable; + UINTN VarNameSize; + UINTN VarDataSize; + + // + // Non-Volatile related. + // + VariableStoreHeader = mNvVariableCache; + + Status = VariableServiceQueryVariableInfoInternal ( + Attributes, + &MaximumVariableStorageSize, + &RemainingVariableStorageSize, + &MaximumVariableSize + ); + ASSERT_EFI_ERROR (Status); + + TotalNeededSize = 0; + Args = Marker; + VariableEntry = VA_ARG (Args, VARIABLE_ENTRY_CONSISTENCY *); + while (VariableEntry != NULL) { + // + // Calculate variable total size. + // + VarNameSize = StrSize (VariableEntry->Name); + VarNameSize += GET_PAD_SIZE (VarNameSize); + VarDataSize = VariableEntry->VariableSize; + VarDataSize += GET_PAD_SIZE (VarDataSize); + VariableEntry->VariableSize = HEADER_ALIGN (GetVariableHeaderSize () + VarNameSize + VarDataSize); + + TotalNeededSize += VariableEntry->VariableSize; + VariableEntry = VA_ARG (Args, VARIABLE_ENTRY_CONSISTENCY *); + } + + if (RemainingVariableStorageSize >= TotalNeededSize) { + // + // Already have enough space. + // + return TRUE; + } else if (AtRuntime ()) { + // + // At runtime, no reclaim. + // The original variable space of Variables can't be reused. + // + return FALSE; + } + + Args = Marker; + VariableEntry = VA_ARG (Args, VARIABLE_ENTRY_CONSISTENCY *); + while (VariableEntry != NULL) { + // + // Check if Variable[Index] has been present and get its size. + // + OriginalVarSize = 0; + VariablePtrTrack.StartPtr = GetStartPointer (VariableStoreHeader); + VariablePtrTrack.EndPtr = GetEndPointer (VariableStoreHeader); + Status = FindVariableEx ( + VariableEntry->Name, + VariableEntry->Guid, + FALSE, + &VariablePtrTrack + ); + if (!EFI_ERROR (Status)) { + // + // Get size of Variable[Index]. + // + NextVariable = GetNextVariablePtr (VariablePtrTrack.CurrPtr); + OriginalVarSize = (UINTN) NextVariable - (UINTN) VariablePtrTrack.CurrPtr; + // + // Add the original size of Variable[Index] to remaining variable storage size. + // + RemainingVariableStorageSize += OriginalVarSize; + } + if (VariableEntry->VariableSize > RemainingVariableStorageSize) { + // + // No enough space for Variable[Index]. + // + return FALSE; + } + // + // Sub the (new) size of Variable[Index] from remaining variable storage size. + // + RemainingVariableStorageSize -= VariableEntry->VariableSize; + VariableEntry = VA_ARG (Args, VARIABLE_ENTRY_CONSISTENCY *); + } + + return TRUE; +} + + +/** + This function is to check if the remaining variable space is enough to set + all Variables from argument list successfully. The purpose of the check + is to keep the consistency of the Variables to be in variable storage. + + Note: Variables are assumed to be in same storage. + The set sequence of Variables will be same with the sequence of VariableEntry from argument list, + so follow the argument sequence to check the Variables. + + @param[in] Attributes Variable attributes for Variable entries. + @param ... The variable argument list with type VARIABLE_ENTRY_CONSISTENCY *. + A NULL terminates the list. The VariableSize of + VARIABLE_ENTRY_CONSISTENCY is the variable data size as input. + It will be changed to variable total size as output. + + @retval TRUE Have enough variable space to set the Variables successfully. + @retval FALSE No enough variable space to set the Variables successfully. + +**/ +BOOLEAN +EFIAPI +CheckRemainingSpaceForConsistency ( + IN UINT32 Attributes, + ... + ) +{ + VA_LIST Marker; + BOOLEAN Return; + + VA_START (Marker, Attributes); + + Return = CheckRemainingSpaceForConsistencyInternal (Attributes, Marker); + + VA_END (Marker); + + return Return; +} + + +/** + Hook the operations in PlatformLangCodes, LangCodes, PlatformLang and Lang. + + When setting Lang/LangCodes, simultaneously update PlatformLang/PlatformLangCodes. + + 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] Data Variable data. + @param[in] DataSize Size of data. 0 means delete. + + @retval EFI_SUCCESS The update operation is successful or ignored. + @retval EFI_WRITE_PROTECTED Update PlatformLangCodes/LangCodes at runtime. + @retval EFI_OUT_OF_RESOURCES No enough variable space to do the update operation. + @retval Others Other errors happened during the update operation. + +**/ +EFI_STATUS +AutoUpdateLangVariable ( + IN CHAR16 *VariableName, + IN VOID *Data, + IN UINTN DataSize + ) +{ + EFI_STATUS Status; + UINTN Index; + UINT32 Attributes; + VARIABLE_POINTER_TRACK Variable; + BOOLEAN SetLanguageCodes; + VARIABLE_ENTRY_CONSISTENCY VariableEntry[2]; + CHAR8 *BestLang; + CHAR8 *BestPlatformLang; + + BestLang = NULL; + BestPlatformLang = NULL; + + // + // Don't do updates for delete operation + // + if (DataSize == 0) { + return EFI_SUCCESS; + } + + SetLanguageCodes = FALSE; + + if (StrCmp (VariableName, EFI_PLATFORM_LANG_CODES_VARIABLE_NAME) == 0) { + // + // PlatformLangCodes is a volatile variable, so it can not be updated at runtime. + // + if (AtRuntime ()) { + return EFI_WRITE_PROTECTED; + } + + SetLanguageCodes = TRUE; + + // + // According to UEFI spec, PlatformLangCodes is only set once in firmware initialization, and is read-only + // Therefore, in variable driver, only store the original value for other use. + // + if (mVariableModuleGlobal->PlatformLangCodes != NULL) { + FreePool (mVariableModuleGlobal->PlatformLangCodes); + } + mVariableModuleGlobal->PlatformLangCodes = AllocateRuntimeCopyPool (DataSize, Data); + ASSERT (mVariableModuleGlobal->PlatformLangCodes != NULL); + + // + // PlatformLang holds a single language from PlatformLangCodes, + // so the size of PlatformLangCodes is enough for the PlatformLang. + // + if (mVariableModuleGlobal->PlatformLang != NULL) { + FreePool (mVariableModuleGlobal->PlatformLang); + } + mVariableModuleGlobal->PlatformLang = AllocateRuntimePool (DataSize); + ASSERT (mVariableModuleGlobal->PlatformLang != NULL); + + } else if (StrCmp (VariableName, EFI_LANG_CODES_VARIABLE_NAME) == 0) { + // + // LangCodes is a volatile variable, so it can not be updated at runtime. + // + if (AtRuntime ()) { + return EFI_WRITE_PROTECTED; + } + + SetLanguageCodes = TRUE; + + // + // According to UEFI spec, LangCodes is only set once in firmware initialization, and is read-only + // Therefore, in variable driver, only store the original value for other use. + // + if (mVariableModuleGlobal->LangCodes != NULL) { + FreePool (mVariableModuleGlobal->LangCodes); + } + mVariableModuleGlobal->LangCodes = AllocateRuntimeCopyPool (DataSize, Data); + ASSERT (mVariableModuleGlobal->LangCodes != NULL); + } + + if (SetLanguageCodes + && (mVariableModuleGlobal->PlatformLangCodes != NULL) + && (mVariableModuleGlobal->LangCodes != NULL)) { + // + // Update Lang if PlatformLang is already set + // Update PlatformLang if Lang is already set + // + Status = FindVariable (EFI_PLATFORM_LANG_VARIABLE_NAME, &gEfiGlobalVariableGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, FALSE); + if (!EFI_ERROR (Status)) { + // + // Update Lang + // + VariableName = EFI_PLATFORM_LANG_VARIABLE_NAME; + Data = GetVariableDataPtr (Variable.CurrPtr); + DataSize = DataSizeOfVariable (Variable.CurrPtr); + } else { + Status = FindVariable (EFI_LANG_VARIABLE_NAME, &gEfiGlobalVariableGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, FALSE); + if (!EFI_ERROR (Status)) { + // + // Update PlatformLang + // + VariableName = EFI_LANG_VARIABLE_NAME; + Data = GetVariableDataPtr (Variable.CurrPtr); + DataSize = DataSizeOfVariable (Variable.CurrPtr); + } else { + // + // Neither PlatformLang nor Lang is set, directly return + // + return EFI_SUCCESS; + } + } + } + + Status = EFI_SUCCESS; + + // + // According to UEFI spec, "Lang" and "PlatformLang" is NV|BS|RT attributions. + // + Attributes = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS; + + if (StrCmp (VariableName, EFI_PLATFORM_LANG_VARIABLE_NAME) == 0) { + // + // Update Lang when PlatformLangCodes/LangCodes were set. + // + if ((mVariableModuleGlobal->PlatformLangCodes != NULL) && (mVariableModuleGlobal->LangCodes != NULL)) { + // + // When setting PlatformLang, firstly get most matched language string from supported language codes. + // + BestPlatformLang = VariableGetBestLanguage (mVariableModuleGlobal->PlatformLangCodes, FALSE, Data, NULL); + if (BestPlatformLang != NULL) { + // + // Get the corresponding index in language codes. + // + Index = GetIndexFromSupportedLangCodes (mVariableModuleGlobal->PlatformLangCodes, BestPlatformLang, FALSE); + + // + // Get the corresponding ISO639 language tag according to RFC4646 language tag. + // + BestLang = GetLangFromSupportedLangCodes (mVariableModuleGlobal->LangCodes, Index, TRUE); + + // + // Check the variable space for both Lang and PlatformLang variable. + // + VariableEntry[0].VariableSize = ISO_639_2_ENTRY_SIZE + 1; + VariableEntry[0].Guid = &gEfiGlobalVariableGuid; + VariableEntry[0].Name = EFI_LANG_VARIABLE_NAME; + + VariableEntry[1].VariableSize = AsciiStrSize (BestPlatformLang); + VariableEntry[1].Guid = &gEfiGlobalVariableGuid; + VariableEntry[1].Name = EFI_PLATFORM_LANG_VARIABLE_NAME; + if (!CheckRemainingSpaceForConsistency (VARIABLE_ATTRIBUTE_NV_BS_RT, &VariableEntry[0], &VariableEntry[1], NULL)) { + // + // No enough variable space to set both Lang and PlatformLang successfully. + // + Status = EFI_OUT_OF_RESOURCES; + } else { + // + // Successfully convert PlatformLang to Lang, and set the BestLang value into Lang variable simultaneously. + // + FindVariable (EFI_LANG_VARIABLE_NAME, &gEfiGlobalVariableGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, FALSE); + + Status = UpdateVariable (EFI_LANG_VARIABLE_NAME, &gEfiGlobalVariableGuid, BestLang, + ISO_639_2_ENTRY_SIZE + 1, Attributes, 0, 0, &Variable, NULL); + } + + DEBUG ((EFI_D_INFO, " Variable Driver: Auto Update PlatformLang, PlatformLang:%a, Lang:%a Status: %r\n", BestPlatformLang, BestLang, Status)); + } + } + + } else if (StrCmp (VariableName, EFI_LANG_VARIABLE_NAME) == 0) { + // + // Update PlatformLang when PlatformLangCodes/LangCodes were set. + // + if ((mVariableModuleGlobal->PlatformLangCodes != NULL) && (mVariableModuleGlobal->LangCodes != NULL)) { + // + // When setting Lang, firstly get most matched language string from supported language codes. + // + BestLang = VariableGetBestLanguage (mVariableModuleGlobal->LangCodes, TRUE, Data, NULL); + if (BestLang != NULL) { + // + // Get the corresponding index in language codes. + // + Index = GetIndexFromSupportedLangCodes (mVariableModuleGlobal->LangCodes, BestLang, TRUE); + + // + // Get the corresponding RFC4646 language tag according to ISO639 language tag. + // + BestPlatformLang = GetLangFromSupportedLangCodes (mVariableModuleGlobal->PlatformLangCodes, Index, FALSE); + + if (BestPlatformLang == NULL) { + ASSERT (BestPlatformLang != NULL); + return EFI_OUT_OF_RESOURCES; + } + + // + // Check the variable space for both PlatformLang and Lang variable. + // + VariableEntry[0].VariableSize = AsciiStrSize (BestPlatformLang); + VariableEntry[0].Guid = &gEfiGlobalVariableGuid; + VariableEntry[0].Name = EFI_PLATFORM_LANG_VARIABLE_NAME; + + VariableEntry[1].VariableSize = ISO_639_2_ENTRY_SIZE + 1; + VariableEntry[1].Guid = &gEfiGlobalVariableGuid; + VariableEntry[1].Name = EFI_LANG_VARIABLE_NAME; + if (!CheckRemainingSpaceForConsistency (VARIABLE_ATTRIBUTE_NV_BS_RT, &VariableEntry[0], &VariableEntry[1], NULL)) { + // + // No enough variable space to set both PlatformLang and Lang successfully. + // + Status = EFI_OUT_OF_RESOURCES; + } else { + // + // Successfully convert Lang to PlatformLang, and set the BestPlatformLang value into PlatformLang variable simultaneously. + // + FindVariable (EFI_PLATFORM_LANG_VARIABLE_NAME, &gEfiGlobalVariableGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, FALSE); + + Status = UpdateVariable (EFI_PLATFORM_LANG_VARIABLE_NAME, &gEfiGlobalVariableGuid, BestPlatformLang, + AsciiStrSize (BestPlatformLang), Attributes, 0, 0, &Variable, NULL); + } + + DEBUG ((EFI_D_INFO, " Variable Driver: Auto Update Lang, Lang:%a, PlatformLang:%a Status: %r\n", BestLang, BestPlatformLang, Status)); + } + } + } + + if (SetLanguageCodes) { + // + // Continue to set PlatformLangCodes or LangCodes. + // + return EFI_SUCCESS; + } else { + return Status; + } +} + + +/** + Compare two EFI_TIME data. + + @param[in] FirstTime A pointer to the first EFI_TIME data. + @param[in] SecondTime A pointer to the second EFI_TIME data. + + @retval TRUE The FirstTime is not later than the SecondTime. + @retval FALSE The FirstTime is later than the SecondTime. + +**/ +BOOLEAN +VariableCompareTimeStampInternal ( + IN EFI_TIME *FirstTime, + IN EFI_TIME *SecondTime + ) +{ + if (FirstTime->Year != SecondTime->Year) { + return (BOOLEAN) (FirstTime->Year < SecondTime->Year); + } else if (FirstTime->Month != SecondTime->Month) { + return (BOOLEAN) (FirstTime->Month < SecondTime->Month); + } else if (FirstTime->Day != SecondTime->Day) { + return (BOOLEAN) (FirstTime->Day < SecondTime->Day); + } else if (FirstTime->Hour != SecondTime->Hour) { + return (BOOLEAN) (FirstTime->Hour < SecondTime->Hour); + } else if (FirstTime->Minute != SecondTime->Minute) { + return (BOOLEAN) (FirstTime->Minute < SecondTime->Minute); + } + + return (BOOLEAN) (FirstTime->Second <= SecondTime->Second); +} + + +/** + Update the variable region with Variable information. If EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS is set, + index of associated public key is needed. + + @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 Attributes of the variable. + @param[in] KeyIndex Index of associated public key. + @param[in] MonotonicCount Value of associated monotonic count. + @param[in, out] CacheVariable The variable information which is used to keep track of variable usage. + @param[in] TimeStamp Value of associated TimeStamp. + @param[in] OnlyUpdateNvCache TRUE if only the NV cache should be written to, not the VARIABLE_STORAGE_PROTOCOLs + + @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 +UpdateVariableInternal ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN VOID *Data, + IN UINTN DataSize, + IN UINT32 Attributes OPTIONAL, + IN UINT32 KeyIndex OPTIONAL, + IN UINT64 MonotonicCount OPTIONAL, + IN OUT VARIABLE_POINTER_TRACK *CacheVariable, + IN EFI_TIME *TimeStamp OPTIONAL, + IN BOOLEAN OnlyUpdateNvCache + ) +{ + EFI_STATUS Status; + VARIABLE_HEADER *NextVariable; + UINTN ScratchSize; + UINTN MaxDataSize; + UINTN VarNameOffset; + UINTN VarDataOffset; + UINTN VarNameSize; + UINTN VarSize; + BOOLEAN Volatile; + UINT8 State; + VARIABLE_POINTER_TRACK *Variable; + VARIABLE_HEADER *LastVariable; + VARIABLE_HEADER *OldVariable; + UINTN OldVariableSize; + UINTN CacheOffset; + UINT8 *BufferForMerge; + UINTN MergedBufSize; + BOOLEAN DataReady; + UINTN DataOffset; + BOOLEAN IsCommonVariable; + BOOLEAN IsCommonUserVariable; + AUTHENTICATED_VARIABLE_HEADER *AuthVariable; + EFI_PHYSICAL_ADDRESS HobVariableBase; + VARIABLE_STORAGE_PROTOCOL *VariableStorageProtocol; + + if (!mVariableModuleGlobal->WriteServiceReady) { + // + // NV Variable writes are not ready, so the EFI_VARIABLE_WRITE_ARCH_PROTOCOL is not installed. + // + if ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) { + // + // Trying to update NV variable prior to the installation of EFI_VARIABLE_WRITE_ARCH_PROTOCOL + // + DEBUG ((EFI_D_ERROR, " Variable Driver: Update NV variable before EFI_VARIABLE_WRITE_ARCH_PROTOCOL ready - %r\n", EFI_NOT_AVAILABLE_YET)); + return EFI_NOT_AVAILABLE_YET; + } else if ((Attributes & VARIABLE_ATTRIBUTE_AT_AW) != 0) { + // + // Trying to update volatile authenticated variable prior to the installation of EFI_VARIABLE_WRITE_ARCH_PROTOCOL + // The authenticated variable perhaps is not initialized, just return here. + // + DEBUG ((EFI_D_ERROR, " Variable Driver: Update AUTH variable before EFI_VARIABLE_WRITE_ARCH_PROTOCOL ready - %r\n", EFI_NOT_AVAILABLE_YET)); + return EFI_NOT_AVAILABLE_YET; + } + } + + DEBUG ((DEBUG_INFO, "+-+-> Variable Driver: UpdateVariable.\n Variable Name: %s.\n Guid: %g.\n", VariableName, VendorGuid)); + + // + // Check if CacheVariable points to the variable in variable HOB. + // If yes, let CacheVariable points to the variable in NV variable cache. + // + if ((CacheVariable->CurrPtr != NULL) && + (mVariableModuleGlobal->VariableGlobal.HobVariableBase != 0) && + (CacheVariable->StartPtr == GetStartPointer ((VARIABLE_STORE_HEADER *) (UINTN) mVariableModuleGlobal->VariableGlobal.HobVariableBase)) + ) { + HobVariableBase = mVariableModuleGlobal->VariableGlobal.HobVariableBase; + mVariableModuleGlobal->VariableGlobal.HobVariableBase = 0; + Status = FindVariable (VariableName, VendorGuid, CacheVariable, &mVariableModuleGlobal->VariableGlobal, FALSE); + mVariableModuleGlobal->VariableGlobal.HobVariableBase = HobVariableBase; + if (CacheVariable->CurrPtr == NULL || EFI_ERROR (Status)) { + // + // There is no matched variable in NV variable cache. + // + if ((((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0) && (DataSize == 0)) || (Attributes == 0)) { + // + // It is to delete variable, + // go to delete this variable in variable HOB and + // try to flush other variables from HOB to flash. + // + FlushHobVariableToFlash (VariableName, VendorGuid); + return EFI_SUCCESS; + } + } + } + + Variable = CacheVariable; + + // + // Tricky part: Use scratch data area at the end of volatile variable store + // as a temporary storage. + // + NextVariable = GetEndPointer ((VARIABLE_STORE_HEADER *) ((UINTN) mVariableModuleGlobal->VariableGlobal.VolatileVariableBase)); + ScratchSize = mVariableModuleGlobal->ScratchBufferSize; + SetMem (NextVariable, ScratchSize, 0xff); + DataReady = FALSE; + + if (Variable->CurrPtr != NULL) { + DEBUG ((DEBUG_INFO, " Variable Driver: Updating an existing variable (found in the cache).\n")); + // + // Update/Delete existing variable. + // + if (AtRuntime ()) { + // + // If AtRuntime and the variable is Volatile and Runtime Access, + // the volatile is ReadOnly, and SetVariable should be aborted and + // return EFI_WRITE_PROTECTED. + // + if (Variable->Volatile) { + Status = EFI_WRITE_PROTECTED; + goto Done; + } + // + // 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; + } + + // + // Only variable that have RT attributes can be updated/deleted in Runtime. + // + if ((Variable->CurrPtr->Attributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + } + + // + // Setting a data variable with no access, or zero DataSize attributes + // causes it to be deleted. + // When the EFI_VARIABLE_APPEND_WRITE attribute is set, DataSize of zero will + // not delete the variable. + // + if ((((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0) && (DataSize == 0)) || + ((Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == 0)) { + DEBUG ((DEBUG_INFO, " Variable Driver: Variable is being deleted.\n")); + if (Variable->InDeletedTransitionPtr != NULL) { + // + // Both ADDED and IN_DELETED_TRANSITION variable are present, + // set IN_DELETED_TRANSITION one to DELETED state first. + // + Variable->InDeletedTransitionPtr->State &= VAR_DELETED; + } + Variable->CurrPtr->State &= VAR_DELETED; + Status = EFI_SUCCESS; + if (!Variable->Volatile && !OnlyUpdateNvCache) { + DEBUG ((DEBUG_INFO, " Variable Driver: Variable is being deleted from NV storage.\n")); + // + // Delete the variable from the NV storage + // + Status = GetVariableStorageProtocol ( + VariableName, + VendorGuid, + &VariableStorageProtocol + ); + if (!EFI_ERROR (Status)) { + if (VariableStorageProtocol == NULL) { + ASSERT (VariableStorageProtocol != NULL); + return EFI_NOT_FOUND; + } + Status = VariableStorageProtocol->SetVariable ( + VariableStorageProtocol, + VariableName, + VendorGuid, + Attributes, + 0, + NULL, + AtRuntime (), + 0, + 0, + TimeStamp + ); + DEBUG ((DEBUG_INFO, " Variable Driver: Value returned from storage protocol = %r.\n", Status)); + } + OldVariable = GetNextVariablePtr (Variable->CurrPtr); + OldVariableSize = (UINTN) OldVariable - (UINTN) Variable->CurrPtr; + if ((Variable->CurrPtr->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) { + mVariableModuleGlobal->HwErrVariableTotalSize -= OldVariableSize; + } else if ((Variable->CurrPtr->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != EFI_VARIABLE_HARDWARE_ERROR_RECORD) { + mVariableModuleGlobal->CommonVariableTotalSize -= OldVariableSize; + if (IsUserVariable (Variable->CurrPtr)) { + mVariableModuleGlobal->CommonUserVariableTotalSize -= OldVariableSize; + } + } + } + if (!EFI_ERROR (Status)) { + UpdateVariableInfo (VariableName, VendorGuid, Variable->Volatile, FALSE, FALSE, TRUE, FALSE); + FlushHobVariableToFlash (VariableName, VendorGuid); + } + goto Done; + } + // + // 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) && + ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0) && + (TimeStamp == NULL)) { + // + // Variable content unchanged and no need to update timestamp, just return. + // + 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))) { + + // + // EFI_VARIABLE_APPEND_WRITE attribute only effects for existing variable. + // + if ((Attributes & EFI_VARIABLE_APPEND_WRITE) != 0) { + // + // NOTE: From 0 to DataOffset of NextVariable is reserved for Variable Header and Name. + // From DataOffset of NextVariable is to save the existing variable data. + // + DataOffset = GetVariableDataOffset (Variable->CurrPtr); + BufferForMerge = (UINT8 *) ((UINTN) NextVariable + DataOffset); + CopyMem (BufferForMerge, (UINT8 *) ((UINTN) Variable->CurrPtr + DataOffset), DataSizeOfVariable (Variable->CurrPtr)); + + // + // Set Max Common/Auth Variable Data Size as default MaxDataSize. + // Max Harware error record variable data size is different from common/auth variable. + // + if ((Attributes & VARIABLE_ATTRIBUTE_AT_AW) != 0) { + MaxDataSize = mVariableModuleGlobal->MaxAuthVariableSize - DataOffset; + } else { + MaxDataSize = mVariableModuleGlobal->MaxVariableSize - DataOffset; + } + if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) { + MaxDataSize = PcdGet32 (PcdMaxHardwareErrorVariableSize) - DataOffset; + } + + if (DataSizeOfVariable (Variable->CurrPtr) + DataSize > MaxDataSize) { + // + // Existing data size + new data size exceed maximum variable size limitation. + // + Status = EFI_INVALID_PARAMETER; + goto Done; + } + // + // Append the new data to the end of existing data. + // + CopyMem ((UINT8*) ((UINTN) BufferForMerge + DataSizeOfVariable (Variable->CurrPtr)), Data, DataSize); + MergedBufSize = DataSizeOfVariable (Variable->CurrPtr) + DataSize; + + // + // BufferForMerge(from DataOffset of NextVariable) has included the merged existing and new data. + // + Data = BufferForMerge; + DataSize = MergedBufSize; + DataReady = TRUE; + } + + // + // Mark the old variable as in delete transition. + // + Variable->CurrPtr->State &= VAR_IN_DELETED_TRANSITION; + } + } else { + DEBUG ((DEBUG_INFO, " Variable Driver: New variable being written.\n")); + // + // Not found existing variable. Create a new variable. + // + + if ((DataSize == 0) && ((Attributes & EFI_VARIABLE_APPEND_WRITE) != 0)) { + Status = EFI_SUCCESS; + goto Done; + } + + // + // Make sure we are trying to create a new variable. + // 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; + goto Done; + } + + // + // Only variable have NV|RT attribute can be created in Runtime. + // + if (AtRuntime () && + (((Attributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0) || ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0))) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + } + + // + // Function part - create a new variable and copy the data. + // Both update a variable and create a variable will come here. + // + NextVariable->StartId = VARIABLE_DATA; + // + // NextVariable->State = VAR_ADDED; + // + NextVariable->Reserved = 0; + if (mVariableModuleGlobal->VariableGlobal.AuthFormat) { + AuthVariable = (AUTHENTICATED_VARIABLE_HEADER *) NextVariable; + AuthVariable->PubKeyIndex = KeyIndex; + AuthVariable->MonotonicCount = MonotonicCount; + ZeroMem (&AuthVariable->TimeStamp, sizeof (EFI_TIME)); + + if (((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) && + (TimeStamp != NULL)) { + if ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0) { + CopyMem (&AuthVariable->TimeStamp, TimeStamp, sizeof (EFI_TIME)); + } else { + // + // In the case when the EFI_VARIABLE_APPEND_WRITE attribute is set, only + // when the new TimeStamp value is later than the current timestamp associated + // with the variable, we need associate the new timestamp with the updated value. + // + if (Variable->CurrPtr != NULL) { + if (VariableCompareTimeStampInternal (&(((AUTHENTICATED_VARIABLE_HEADER *) Variable->CurrPtr)->TimeStamp), TimeStamp)) { + CopyMem (&AuthVariable->TimeStamp, TimeStamp, sizeof (EFI_TIME)); + } + } + } + } + } + + // + // The EFI_VARIABLE_APPEND_WRITE attribute will never be set in the returned + // Attributes bitmask parameter of a GetVariable() call. + // + NextVariable->Attributes = Attributes & (~EFI_VARIABLE_APPEND_WRITE); + + VarNameOffset = GetVariableHeaderSize (); + VarNameSize = StrSize (VariableName); + CopyMem ( + (UINT8 *) ((UINTN) NextVariable + VarNameOffset), + VariableName, + VarNameSize + ); + VarDataOffset = VarNameOffset + VarNameSize + GET_PAD_SIZE (VarNameSize); + + // + // If DataReady is TRUE, it means the variable data has been saved into + // NextVariable during EFI_VARIABLE_APPEND_WRITE operation preparation. + // + if (!DataReady) { + CopyMem ( + (UINT8 *) ((UINTN) NextVariable + VarDataOffset), + Data, + DataSize + ); + } + + CopyMem (GetVendorGuidPtr (NextVariable), VendorGuid, sizeof (EFI_GUID)); + // + // 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. + // + SetNameSizeOfVariable (NextVariable, VarNameSize); + SetDataSizeOfVariable (NextVariable, DataSize); + + // + // The actual size of the variable that stores in storage should + // include pad size. + // + VarSize = VarDataOffset + DataSize + GET_PAD_SIZE (DataSize); + if ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) { + // + // Create a nonvolatile variable. + // + Volatile = FALSE; + + IsCommonVariable = FALSE; + IsCommonUserVariable = FALSE; + LastVariable = GetStartPointer (mNvVariableCache); + while (IsValidVariableHeader (LastVariable, GetEndPointer (mNvVariableCache))) { + LastVariable = GetNextVariablePtr (LastVariable); + } + CacheOffset = (UINTN) LastVariable - (UINTN) mNvVariableCache; + if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == 0) { + IsCommonVariable = TRUE; + IsCommonUserVariable = IsUserVariable (NextVariable); + } + if ((((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != 0) + && ((VarSize + mVariableModuleGlobal->HwErrVariableTotalSize) > PcdGet32 (PcdHwErrStorageSize))) + || (IsCommonVariable && ((VarSize + mVariableModuleGlobal->CommonVariableTotalSize) > mVariableModuleGlobal->CommonVariableSpace)) + || (IsCommonVariable && AtRuntime () && ((VarSize + mVariableModuleGlobal->CommonVariableTotalSize) > mVariableModuleGlobal->CommonRuntimeVariableSpace)) + || (IsCommonUserVariable && ((VarSize + mVariableModuleGlobal->CommonUserVariableTotalSize) > mVariableModuleGlobal->CommonMaxUserVariableSpace)) + || (UINT32) (VarSize + CacheOffset) > mNvVariableCache->Size) { + if (AtRuntime ()) { + if (IsCommonUserVariable && ((VarSize + mVariableModuleGlobal->CommonUserVariableTotalSize) > mVariableModuleGlobal->CommonMaxUserVariableSpace)) { + RecordVarErrorFlag (VAR_ERROR_FLAG_USER_ERROR, VariableName, VendorGuid, Attributes, VarSize); + } + if (IsCommonVariable && ((VarSize + mVariableModuleGlobal->CommonVariableTotalSize) > mVariableModuleGlobal->CommonRuntimeVariableSpace)) { + RecordVarErrorFlag (VAR_ERROR_FLAG_SYSTEM_ERROR, VariableName, VendorGuid, Attributes, VarSize); + } + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + // + // Perform garbage collection & reclaim operation, and integrate the new variable at the same time. + // + Status = Reclaim ( + (EFI_PHYSICAL_ADDRESS) (UINTN) mNvVariableCache, + &CacheOffset, + FALSE, + Variable, + NextVariable, + HEADER_ALIGN (VarSize) + ); + if (!EFI_ERROR (Status)) { + // + // The new variable has been integrated successfully during reclaiming. + // + if (Variable->CurrPtr != NULL) { + CacheVariable->CurrPtr = (VARIABLE_HEADER *)((UINTN) CacheVariable->StartPtr + ((UINTN) Variable->CurrPtr - (UINTN) Variable->StartPtr)); + CacheVariable->InDeletedTransitionPtr = NULL; + } + UpdateVariableInfo (VariableName, VendorGuid, FALSE, FALSE, TRUE, FALSE, FALSE); + FlushHobVariableToFlash (VariableName, VendorGuid); + } else { + if (IsCommonUserVariable && ((VarSize + mVariableModuleGlobal->CommonUserVariableTotalSize) > mVariableModuleGlobal->CommonMaxUserVariableSpace)) { + RecordVarErrorFlag (VAR_ERROR_FLAG_USER_ERROR, VariableName, VendorGuid, Attributes, VarSize); + } + if (IsCommonVariable && ((VarSize + mVariableModuleGlobal->CommonVariableTotalSize) > mVariableModuleGlobal->CommonVariableSpace)) { + RecordVarErrorFlag (VAR_ERROR_FLAG_SYSTEM_ERROR, VariableName, VendorGuid, Attributes, VarSize); + } + } + goto Done; + } + // + // Write the variable to NV + // + if (!OnlyUpdateNvCache) { + Status = GetVariableStorageProtocol ( + VariableName, + VendorGuid, + &VariableStorageProtocol + ); + if (EFI_ERROR (Status) || VariableStorageProtocol == NULL) { + goto Done; + } + Status = VariableStorageProtocol->SetVariable ( + VariableStorageProtocol, + VariableName, + VendorGuid, + Attributes, + DataSize, + Data, + AtRuntime (), + KeyIndex, + MonotonicCount, + TimeStamp + ); + DEBUG ((DEBUG_INFO, " Variable Driver: Variable was written to NV via the storage protocol. Status = %r.\n", Status)); + if (EFI_ERROR (Status)) { + goto Done; + } + if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != 0) { + mVariableModuleGlobal->HwErrVariableTotalSize += HEADER_ALIGN (VarSize); + } else { + mVariableModuleGlobal->CommonVariableTotalSize += HEADER_ALIGN (VarSize); + if (IsCommonUserVariable) { + mVariableModuleGlobal->CommonUserVariableTotalSize += HEADER_ALIGN (VarSize); + } + } + } + // + // Update the NV Cache + // + NextVariable->State = VAR_ADDED; + Status = UpdateVariableStore ( + &mVariableModuleGlobal->VariableGlobal, + FALSE, + TRUE, + CacheOffset, + (UINT32) VarSize, + (UINT8 *) NextVariable + ); + DEBUG ((DEBUG_INFO, " Variable Driver: Wrote the variable to the NV cache in UpdateVariable().\n")); + if (EFI_ERROR (Status)) { + goto Done; + } + } else { + // + // Create a volatile variable. + // + Volatile = TRUE; + + DEBUG ((DEBUG_INFO, " Variable Driver: Creating a volatile variable.\n")); + + if ((UINT32) (VarSize + mVariableModuleGlobal->VolatileLastVariableOffset) > + ((VARIABLE_STORE_HEADER *) ((UINTN) (mVariableModuleGlobal->VariableGlobal.VolatileVariableBase)))->Size) { + // + // Perform garbage collection & reclaim operation, and integrate the new variable at the same time. + // + Status = Reclaim ( + mVariableModuleGlobal->VariableGlobal.VolatileVariableBase, + &mVariableModuleGlobal->VolatileLastVariableOffset, + TRUE, + Variable, + NextVariable, + HEADER_ALIGN (VarSize) + ); + if (!EFI_ERROR (Status)) { + // + // The new variable has been integrated successfully during reclaiming. + // + if (Variable->CurrPtr != NULL) { + CacheVariable->CurrPtr = (VARIABLE_HEADER *) ((UINTN) CacheVariable->StartPtr + ((UINTN) Variable->CurrPtr - (UINTN) Variable->StartPtr)); + CacheVariable->InDeletedTransitionPtr = NULL; + } + UpdateVariableInfo (VariableName, VendorGuid, TRUE, FALSE, TRUE, FALSE, FALSE); + } + goto Done; + } + + NextVariable->State = VAR_ADDED; + Status = UpdateVariableStore ( + &mVariableModuleGlobal->VariableGlobal, + TRUE, + TRUE, + mVariableModuleGlobal->VolatileLastVariableOffset, + (UINT32) VarSize, + (UINT8 *) NextVariable + ); + + if (EFI_ERROR (Status)) { + goto Done; + } + + mVariableModuleGlobal->VolatileLastVariableOffset += HEADER_ALIGN (VarSize); + } + + // + // Mark the old variable as deleted. + // + if (!EFI_ERROR (Status) && Variable->CurrPtr != NULL) { + // + // Reduce the current NV storage usage counts by the old variable's size + // + if (!OnlyUpdateNvCache) { + OldVariable = GetNextVariablePtr (Variable->CurrPtr); + OldVariableSize = (UINTN) OldVariable - (UINTN) Variable->CurrPtr; + if ((Variable->CurrPtr->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) { + mVariableModuleGlobal->HwErrVariableTotalSize -= OldVariableSize; + } else if ((Variable->CurrPtr->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != EFI_VARIABLE_HARDWARE_ERROR_RECORD) { + mVariableModuleGlobal->CommonVariableTotalSize -= OldVariableSize; + if (IsUserVariable (Variable->CurrPtr)) { + mVariableModuleGlobal->CommonUserVariableTotalSize -= OldVariableSize; + } + } + } + if (Variable->InDeletedTransitionPtr != NULL) { + // + // Both ADDED and IN_DELETED_TRANSITION old variable are present, + // set IN_DELETED_TRANSITION one to DELETED state first. + // + State = Variable->InDeletedTransitionPtr->State; + State &= VAR_DELETED; + Status = UpdateVariableStore ( + &mVariableModuleGlobal->VariableGlobal, + Variable->Volatile, + FALSE, + (UINTN) &Variable->InDeletedTransitionPtr->State, + sizeof (UINT8), + &State + ); + if (EFI_ERROR (Status)) { + goto Done; + } + } + + State = Variable->CurrPtr->State; + State &= VAR_DELETED; + + Status = UpdateVariableStore ( + &mVariableModuleGlobal->VariableGlobal, + Variable->Volatile, + FALSE, + (UINTN) &Variable->CurrPtr->State, + sizeof (UINT8), + &State + ); + } + + if (!EFI_ERROR (Status)) { + UpdateVariableInfo (VariableName, VendorGuid, Volatile, FALSE, TRUE, FALSE, FALSE); + if (!Volatile) { + FlushHobVariableToFlash (VariableName, VendorGuid); + } + } + +Done: + return Status; +} + + +/** + Update the variable region with Variable information. If EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS is set, + index of associated public key is needed. + + @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 Attributes of the variable. + @param[in] KeyIndex Index of associated public key. + @param[in] MonotonicCount Value of associated monotonic count. + @param[in, out] CacheVariable The variable information which is used to keep track of variable usage. + @param[in] TimeStamp Value of associated TimeStamp. + + @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 +UpdateVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN VOID *Data, + IN UINTN DataSize, + IN UINT32 Attributes OPTIONAL, + IN UINT32 KeyIndex OPTIONAL, + IN UINT64 MonotonicCount OPTIONAL, + IN OUT VARIABLE_POINTER_TRACK *CacheVariable, + IN EFI_TIME *TimeStamp OPTIONAL + ) +{ + return UpdateVariableInternal ( + VariableName, + VendorGuid, + Data, + DataSize, + Attributes, + KeyIndex, + MonotonicCount, + CacheVariable, + TimeStamp, + mForceVolatileVariable + ); +} + +/** + Finds the first variable from the first NV storage. + + @param[out] PtrTrack VARIABLE_POINTER_TRACK structure for output, + including the range searched and the target position. + @param[in] Global Pointer to VARIABLE_GLOBAL structure, including + base of volatile variable storage area, base of + NV variable storage area, and a lock. + @param[in] IgnoreRtCheck Ignore EFI_VARIABLE_RUNTIME_ACCESS attribute + check at runtime when searching variable. + + @retval EFI_INVALID_PARAMETER If VariableName is not an empty string, while + VendorGuid is NULL. + @retval EFI_SUCCESS Variable successfully found. + @retval EFI_NOT_FOUND Variable not found. + +**/ +EFI_STATUS +FindFirstNvVariable ( + OUT VARIABLE_POINTER_TRACK *PtrTrack, + IN VARIABLE_GLOBAL *Global, + IN BOOLEAN IgnoreRtCheck + ) +{ + VARIABLE_STORAGE_PROTOCOL *VariableStorage; + UINTN VariableNameBufferSize; + UINT32 VariableAttributes; + EFI_GUID TempGuid; + EFI_STATUS Status; + + if (mVariableModuleGlobal->VariableGlobal.VariableStoresCount <= 0) { + return EFI_NOT_FOUND; + } + VariableNameBufferSize = sizeof (mVariableNameBuffer); + ZeroMem ((VOID *) &mVariableNameBuffer[0], VariableNameBufferSize); + ZeroMem ((VOID *) &TempGuid, sizeof (EFI_GUID)); + VariableStorage = mVariableModuleGlobal->VariableGlobal.VariableStores[0]; + Status = VariableStorage->GetNextVariableName ( + VariableStorage, + &VariableNameBufferSize, + &mVariableNameBuffer[0], + &TempGuid, + &VariableAttributes + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "VariableStorageProtocol->GetNextVariableName status %r\n", Status)); + ASSERT (Status != EFI_BUFFER_TOO_SMALL); + return Status; + } + return FindVariable (&mVariableNameBuffer[0], &TempGuid, PtrTrack, Global, IgnoreRtCheck); +} + + +/** + Finds variable in storage blocks of volatile and non-volatile storage areas. + + This code finds variable in storage blocks of volatile and non-volatile storage areas. + If VariableName is an empty string, then we just return the first + qualified variable without comparing VariableName and VendorGuid. + If IgnoreRtCheck is TRUE, then we ignore the EFI_VARIABLE_RUNTIME_ACCESS attribute check + at runtime when searching existing variable, only VariableName and VendorGuid are compared. + Otherwise, variables without EFI_VARIABLE_RUNTIME_ACCESS are not visible at runtime. + + @param[in] VariableName Name of the variable to be found. + @param[in] VendorGuid Vendor GUID to be found. + @param[out] PtrTrack VARIABLE_POINTER_TRACK structure for output, + including the range searched and the target position. + @param[in] Global Pointer to VARIABLE_GLOBAL structure, including + base of volatile variable storage area, base of + NV variable storage area, and a lock. + @param[in] IgnoreRtCheck Ignore EFI_VARIABLE_RUNTIME_ACCESS attribute + check at runtime when searching variable. + + @retval EFI_INVALID_PARAMETER If VariableName is not an empty string, while + VendorGuid is NULL. + @retval EFI_SUCCESS Variable successfully found. + @retval EFI_NOT_FOUND Variable not found. + +**/ +EFI_STATUS +FindVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + OUT VARIABLE_POINTER_TRACK *PtrTrack, + IN VARIABLE_GLOBAL *Global, + IN BOOLEAN IgnoreRtCheck + ) +{ + EFI_STATUS Status; + EFI_STATUS Status2; + VARIABLE_STORE_HEADER *VariableStoreHeader[VariableStoreTypeMax]; + EFI_GUID VariableStorageId; + EFI_GUID InstanceGuid; + VARIABLE_STORAGE_PROTOCOL *VariableStorageProtocol; + VARIABLE_STORAGE_PROTOCOL *CorrectVariableStorageProtocol; + UINTN Index; + VARIABLE_STORE_TYPE Type; + EFI_TIME TimeStamp; + UINT64 MonotonicCount; + UINTN DataSize; + UINT32 Attributes; + UINT32 KeyIndex; + BOOLEAN FailedRtCheck; + + FailedRtCheck = FALSE; + if (VariableName[0] != 0 && VendorGuid == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // 0: Volatile, 1: HOB, 2: Non-Volatile Cache. + // The index and attributes mapping must be kept in this order as RuntimeServiceGetNextVariableName + // make use of this mapping to implement search algorithm. + // + VariableStoreHeader[VariableStoreTypeVolatile] = (VARIABLE_STORE_HEADER *) (UINTN) Global->VolatileVariableBase; + VariableStoreHeader[VariableStoreTypeHob] = (VARIABLE_STORE_HEADER *) (UINTN) Global->HobVariableBase; + VariableStoreHeader[VariableStoreTypeNvCache] = mNvVariableCache; + + DEBUG ((DEBUG_INFO, "+-+-> Variable Driver: FindVariable.\n Variable Name: %s.\n Guid: %g.\n", VariableName, VendorGuid)); + + // + // Find the variable by walk through HOB, volatile and non-volatile variable store. + // + for (Type = (VARIABLE_STORE_TYPE) 0; Type < VariableStoreTypeMax; Type++) { + if (VariableStoreHeader[Type] == NULL) { + continue; + } + + PtrTrack->StartPtr = GetStartPointer (VariableStoreHeader[Type]); + PtrTrack->EndPtr = GetEndPointer (VariableStoreHeader[Type]); + PtrTrack->Volatile = (BOOLEAN) (Type == VariableStoreTypeVolatile); + + Status = FindVariableEx (VariableName, VendorGuid, TRUE, PtrTrack); + if (!EFI_ERROR (Status)) { + FailedRtCheck = FALSE; + if (!IgnoreRtCheck && + ((PtrTrack->CurrPtr->Attributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0) && + AtRuntime ()) { + FailedRtCheck = TRUE; + continue; + } + DEBUG ((DEBUG_INFO, "Variable Driver: Found the variable in store type %d before going to protocols.\n", Type)); + return Status; + } + } + if (FailedRtCheck) { + PtrTrack->CurrPtr = NULL; + PtrTrack->InDeletedTransitionPtr = NULL; + return EFI_NOT_FOUND; + } + if (mForceVolatileVariable) { + return EFI_NOT_FOUND; + } + + // + // If VariableName is an empty string get the first variable from the first NV storage + // + if (VariableName[0] == 0) { + return FindFirstNvVariable (PtrTrack, Global, IgnoreRtCheck); + } + // + // Search the VARIABLE_STORAGE_PROTOCOLs + // first, try the protocol instance which the VariableStorageSelectorLib suggests + // + ASSERT (mVariableDataBuffer != NULL); + if (mVariableDataBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + CorrectVariableStorageProtocol = NULL; + ZeroMem ((VOID *) &VariableStorageId, sizeof (EFI_GUID)); + Status = GetVariableStorageId (VariableName, VendorGuid, TRUE, &VariableStorageId); + if (EFI_ERROR (Status)) { + return Status; + } + for ( Index = 0; + Index < mVariableModuleGlobal->VariableGlobal.VariableStoresCount; + Index++) { + VariableStorageProtocol = mVariableModuleGlobal->VariableGlobal.VariableStores[Index]; + ZeroMem ((VOID *) &InstanceGuid, sizeof (EFI_GUID)); + Status = VariableStorageProtocol->GetId (&InstanceGuid); + if (EFI_ERROR (Status)) { + return Status; + } + if (CompareGuid (&VariableStorageId, &InstanceGuid)) { + CorrectVariableStorageProtocol = VariableStorageProtocol; + DataSize = mVariableModuleGlobal->ScratchBufferSize; + ZeroMem (mVariableDataBuffer, DataSize); + Status = VariableStorageProtocol->GetAuthenticatedVariable ( + VariableStorageProtocol, + VariableName, + VendorGuid, + &Attributes, + &DataSize, + mVariableDataBuffer, + &KeyIndex, + &MonotonicCount, + &TimeStamp + ); + DEBUG ((DEBUG_INFO, " Variable Driver: Variable storage protocol GetAuthenticatedVariable status = %r.\n", Status)); + + if (!EFI_ERROR (Status)) { + goto UpdateNvCache; + } + ASSERT (Status != EFI_BUFFER_TOO_SMALL); + if (Status != EFI_NOT_FOUND) { + return Status; + } + break; + } + } + // + // As a fallback, try searching the remaining VARIABLE_STORAGE_PROTOCOLs even + // though the variable shouldn't actually be stored in there + // + for ( Index = 0; + Index < mVariableModuleGlobal->VariableGlobal.VariableStoresCount; + Index++) { + VariableStorageProtocol = mVariableModuleGlobal->VariableGlobal.VariableStores[Index]; + ZeroMem ((VOID *) &InstanceGuid, sizeof (EFI_GUID)); + Status = VariableStorageProtocol->GetId (&InstanceGuid); + if (EFI_ERROR (Status)) { + return Status; + } + if (!CompareGuid (&VariableStorageId, &VariableStorageId)) { + DataSize = mVariableModuleGlobal->ScratchBufferSize; + ZeroMem (mVariableDataBuffer, DataSize); + Status = VariableStorageProtocol->GetAuthenticatedVariable ( + VariableStorageProtocol, + VariableName, + VendorGuid, + &Attributes, + &DataSize, + mVariableDataBuffer, + &KeyIndex, + &MonotonicCount, + &TimeStamp + ); + if (!EFI_ERROR (Status)) { + // + // The variable isn't being stored in the correct VariableStorage, + // attempt to move it to the correct VariableStorage + // + DEBUG (( + DEBUG_INFO, + "Variable %s is not being stored in the correct NV storage!\n", + VariableName + )); + DEBUG (( + DEBUG_INFO, + "Expected StorageId = %g, Actual StorageId = %g\n", + &VariableStorageId, + &VariableStorageId + )); + if (CorrectVariableStorageProtocol != NULL) { + if (CorrectVariableStorageProtocol->WriteServiceIsReady ( + CorrectVariableStorageProtocol) && + VariableStorageProtocol->WriteServiceIsReady ( + VariableStorageProtocol)) { + Status2 = CorrectVariableStorageProtocol->SetVariable ( + CorrectVariableStorageProtocol, + VariableName, + VendorGuid, + Attributes, + DataSize, + mVariableDataBuffer, + AtRuntime (), + KeyIndex, + MonotonicCount, + &TimeStamp + ); + if (EFI_ERROR (Status2)) { + DEBUG ((DEBUG_INFO, "Failed to copy variable to correct VariableStorage!\n")); + goto UpdateNvCache; + } + // + // Delete the redundant copy that is incorrectly stored + // + Status2 = VariableStorageProtocol->SetVariable ( + VariableStorageProtocol, + VariableName, + VendorGuid, + Attributes, + 0, + NULL, + AtRuntime (), + 0, + 0, + &TimeStamp + ); + if (EFI_ERROR (Status2)) { + DEBUG ((DEBUG_INFO, " Variable Driver: Failed to delete redundant copy of variable in the incorrect VariableStorage!\n")); + } + DEBUG ((DEBUG_INFO, " Variable Driver: Variable has been moved to the correct VariableStorage.\n")); + } else { + DEBUG ((DEBUG_INFO, " Variable Driver: VariableStorage is not ready to write, unable to move variable.\n")); + } + } else { + DEBUG ((DEBUG_INFO, " Variable Driver: Expected VariableStorage does not exist!\n")); + } + goto UpdateNvCache; + } + ASSERT (Status != EFI_BUFFER_TOO_SMALL); + if (Status != EFI_NOT_FOUND) { + return Status; + } + } + } + return EFI_NOT_FOUND; +UpdateNvCache: + DEBUG ((DEBUG_INFO, " Variable Driver: Updating the cache for this variable.\n")); + + PtrTrack->CurrPtr = NULL; + PtrTrack->InDeletedTransitionPtr = NULL; + PtrTrack->StartPtr = GetStartPointer (mNvVariableCache); + PtrTrack->EndPtr = GetEndPointer (mNvVariableCache); + PtrTrack->Volatile = FALSE; + Status = UpdateVariableInternal ( + VariableName, + VendorGuid, + mVariableDataBuffer, + DataSize, + Attributes, + KeyIndex, + MonotonicCount, + PtrTrack, + &TimeStamp, + TRUE + ); + DEBUG ((DEBUG_INFO, " Variable Driver: UpdateVariable status = %r.\n", Status)); + if (!EFI_ERROR (Status)) { + PtrTrack->StartPtr = GetStartPointer (mNvVariableCache); + PtrTrack->EndPtr = GetEndPointer (mNvVariableCache); + PtrTrack->Volatile = FALSE; + Status = FindVariableEx (VariableName, VendorGuid, IgnoreRtCheck, PtrTrack); + } + return Status; +} + + +/** + This code finds variable in storage blocks (Volatile or Non-Volatile). + + Caution: This function may receive untrusted input. + This function may be invoked in SMM mode, and datasize is external input. + This function will do basic validation, before parse the data. + + @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. + + @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 + ) +{ + EFI_STATUS Status; + VARIABLE_POINTER_TRACK Variable; + UINTN VarDataSize; + + if (VariableName == NULL || VendorGuid == NULL || DataSize == NULL) { + return EFI_INVALID_PARAMETER; + } + + AcquireLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock); + + Status = FindVariable (VariableName, VendorGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, FALSE); + if (Variable.CurrPtr == NULL || EFI_ERROR (Status)) { + goto Done; + } + + // + // Get data size + // + VarDataSize = DataSizeOfVariable (Variable.CurrPtr); + ASSERT (VarDataSize != 0); + + if (*DataSize >= VarDataSize) { + if (Data == NULL) { + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + CopyMem (Data, GetVariableDataPtr (Variable.CurrPtr), VarDataSize); + if (Attributes != NULL) { + *Attributes = Variable.CurrPtr->Attributes; + } + + *DataSize = VarDataSize; + UpdateVariableInfo (VariableName, VendorGuid, Variable.Volatile, TRUE, FALSE, FALSE, FALSE); + + Status = EFI_SUCCESS; + goto Done; + } else { + *DataSize = VarDataSize; + Status = EFI_BUFFER_TOO_SMALL; + goto Done; + } + +Done: + ReleaseLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock); + return Status; +} + + +/** + This code Finds the Next available variable. + + Caution: This function may receive untrusted input. + This function may be invoked in SMM mode. This function will do basic validation, before parse the data. + + @param[in] VariableName Pointer to variable name. + @param[in] VendorGuid Variable Vendor Guid. + @param[out] VariablePtr Pointer to variable header address. + + @return EFI_SUCCESS Find the specified variable. + @return EFI_NOT_FOUND Not found. + +**/ +EFI_STATUS +EFIAPI +VariableServiceGetNextInMemoryVariableInternal ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + OUT VARIABLE_HEADER **VariablePtr, + OUT BOOLEAN *CurrentVariableInMemory + ) +{ + VARIABLE_STORE_TYPE Type; + VARIABLE_STORE_TYPE MaxSearch; + VARIABLE_POINTER_TRACK Variable; + VARIABLE_POINTER_TRACK VariablePtrTrack; + EFI_STATUS Status; + VARIABLE_STORE_HEADER *VariableStoreHeader[VariableStoreTypeMax]; + + *CurrentVariableInMemory = FALSE; + if (VariableName[0] != 0 && VendorGuid == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // 0: Volatile, 1: HOB + // The index and attributes mapping must be kept in this order to implement + // the variable enumeration search algorithm. + // + VariableStoreHeader[VariableStoreTypeVolatile] = (VARIABLE_STORE_HEADER *) (UINTN) mVariableModuleGlobal->VariableGlobal.VolatileVariableBase; + VariableStoreHeader[VariableStoreTypeHob] = (VARIABLE_STORE_HEADER *) (UINTN) mVariableModuleGlobal->VariableGlobal.HobVariableBase; + VariableStoreHeader[VariableStoreTypeNvCache] = mNvVariableCache; + if (mForceVolatileVariable) { + MaxSearch = VariableStoreTypeNvCache; + } else { + MaxSearch = VariableStoreTypeHob; + } + + // + // Find the current variable by walk through HOB and volatile variable store. + // + Variable.CurrPtr = NULL; + Status = EFI_NOT_FOUND; + for (Type = (VARIABLE_STORE_TYPE) 0; Type <= MaxSearch; Type++) { + if (VariableStoreHeader[Type] == NULL) { + continue; + } + + Variable.StartPtr = GetStartPointer (VariableStoreHeader[Type]); + Variable.EndPtr = GetEndPointer (VariableStoreHeader[Type]); + Variable.Volatile = (BOOLEAN) (Type == VariableStoreTypeVolatile); + + Status = FindVariableEx (VariableName, VendorGuid, FALSE, &Variable); + if (!EFI_ERROR (Status)) { + break; + } + } + if (Variable.CurrPtr == NULL || EFI_ERROR (Status)) { + goto Done; + } + *CurrentVariableInMemory = TRUE; + + if (VariableName[0] != 0) { + // + // If variable name is not NULL, get next variable. + // + Variable.CurrPtr = GetNextVariablePtr (Variable.CurrPtr); + } + + while (TRUE) { + // + // Switch from Volatile to HOB + // + while (!IsValidVariableHeader (Variable.CurrPtr, Variable.EndPtr)) { + // + // Find current storage index + // + for (Type = (VARIABLE_STORE_TYPE) 0; Type <= VariableStoreTypeHob; Type++) { + if ((VariableStoreHeader[Type] != NULL) && (Variable.StartPtr == GetStartPointer (VariableStoreHeader[Type]))) { + break; + } + } + ASSERT (Type <= VariableStoreTypeHob); + // + // Switch to next storage + // + for (Type++; Type < VariableStoreTypeMax; Type++) { + if (VariableStoreHeader[Type] != NULL) { + break; + } + } + // + // Capture the case that + // 1. current storage is the last one, or + // 2. no further storage + // + if (Type > VariableStoreTypeHob) { + Status = EFI_NOT_FOUND; + goto Done; + } + Variable.StartPtr = GetStartPointer (VariableStoreHeader[Type]); + Variable.EndPtr = GetEndPointer (VariableStoreHeader[Type]); + Variable.CurrPtr = Variable.StartPtr; + } + + // + // Variable is found + // + if (Variable.CurrPtr->State == VAR_ADDED || Variable.CurrPtr->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED)) { + if (!AtRuntime () || ((Variable.CurrPtr->Attributes & EFI_VARIABLE_RUNTIME_ACCESS) != 0)) { + if (Variable.CurrPtr->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED)) { + // + // If it is a IN_DELETED_TRANSITION variable, + // and there is also a same ADDED one at the same time, + // don't return it. + // + VariablePtrTrack.StartPtr = Variable.StartPtr; + VariablePtrTrack.EndPtr = Variable.EndPtr; + Status = FindVariableEx ( + GetVariableNamePtr (Variable.CurrPtr), + GetVendorGuidPtr (Variable.CurrPtr), + FALSE, + &VariablePtrTrack + ); + if (!EFI_ERROR (Status) && VariablePtrTrack.CurrPtr->State == VAR_ADDED) { + Variable.CurrPtr = GetNextVariablePtr (Variable.CurrPtr); + continue; + } + } + + *VariablePtr = Variable.CurrPtr; + Status = EFI_SUCCESS; + goto Done; + } + } + + Variable.CurrPtr = GetNextVariablePtr (Variable.CurrPtr); + } + +Done: + return Status; +} + + +/** + Determines if a variable exists in the default HOB + + @param[in] VariableName A pointer to a null-terminated string that is the variable's name. + @param[in] VariableGuid A pointer to an EFI_GUID that is the variable's GUID. The combination of + VariableGuid and VariableName must be unique. + + @retval TRUE The variable exists in the HOBs + @retval FALSE The variable does not exist in the HOBs + +**/ +BOOLEAN +EFIAPI +VariableExistsInHob ( + IN CONST CHAR16 *VariableName, + IN CONST EFI_GUID *VariableGuid + ) +{ + VARIABLE_POINTER_TRACK Variable; + EFI_STATUS Status; + + Variable.CurrPtr = NULL; + Variable.StartPtr = GetStartPointer ((VARIABLE_STORE_HEADER *) (UINTN) mVariableModuleGlobal->VariableGlobal.HobVariableBase); + Variable.EndPtr = GetEndPointer ((VARIABLE_STORE_HEADER *) (UINTN) mVariableModuleGlobal->VariableGlobal.HobVariableBase); + Variable.Volatile = FALSE; + + Status = FindVariableEx ((CHAR16 *) VariableName, (EFI_GUID *) VariableGuid, FALSE, &Variable); + if ((Variable.CurrPtr != NULL) && (!EFI_ERROR (Status))) { + return TRUE; + } else { + if (Status == EFI_NOT_FOUND) { + return FALSE; + } + ASSERT_EFI_ERROR (Status); + return FALSE; + } +} + + +/** + This code Finds the Next available variable. + + Caution: This function may receive untrusted input. + This function may be invoked in SMM mode. This function will do basic validation, before parse the data. + + @param[in, out] VariableNameSize Size of the variable name. + @param[in, out] VariableName Pointer to variable name. + @param[in, out] VendorGuid Variable Vendor Guid. + + @return EFI_SUCCESS Find the specified variable. + @return EFI_NOT_FOUND Not found. + +**/ +EFI_STATUS +EFIAPI +VariableServiceGetNextVariableInternal ( + IN OUT UINTN *VariableNameSize, + IN OUT CHAR16 *VariableName, + IN OUT EFI_GUID *VariableGuid + ) +{ + EFI_GUID VariableStorageId; + EFI_GUID InstanceGuid; + VARIABLE_STORAGE_PROTOCOL *VariableStorage; + VARIABLE_HEADER *VariablePtr; + UINTN Index; + UINTN VarNameSize; + UINT32 VarAttributes; + UINTN CallerVariableNameBufferSize; + EFI_STATUS Status; + BOOLEAN SearchComplete; + BOOLEAN CurrentVariableInMemory; + + CallerVariableNameBufferSize = *VariableNameSize; + + // + // Check the volatile and HOB variables first + // + Status = VariableServiceGetNextInMemoryVariableInternal ( + VariableName, + VariableGuid, + &VariablePtr, + &CurrentVariableInMemory + ); + if (!EFI_ERROR (Status)) { + VarNameSize = NameSizeOfVariable (VariablePtr); + ASSERT (VarNameSize != 0); + if (VarNameSize <= *VariableNameSize) { + CopyMem (VariableName, GetVariableNamePtr (VariablePtr), VarNameSize); + CopyMem (VariableGuid, GetVendorGuidPtr (VariablePtr), sizeof (EFI_GUID)); + Status = EFI_SUCCESS; + } else { + Status = EFI_BUFFER_TOO_SMALL; + } + + *VariableNameSize = VarNameSize; + return Status; + } else if (Status != EFI_NOT_FOUND) { + DEBUG ((DEBUG_INFO, "VariableServiceGetNextInMemoryVariableInternal status %r\n", Status)); + return Status; + } + + if (mForceVolatileVariable) { + return EFI_NOT_FOUND; + } + + // + // If VariableName is an empty string or we reached the end of the volatile + // and HOB variables, get the first variable from the first NV storage + // + if (VariableName[0] == 0 || (Status == EFI_NOT_FOUND && CurrentVariableInMemory)) { + if (mVariableModuleGlobal->VariableGlobal.VariableStoresCount <= 0) { + return EFI_NOT_FOUND; + } + ZeroMem ((VOID *) VariableName, *VariableNameSize); + ZeroMem ((VOID *) VariableGuid, sizeof (VariableGuid)); + VariableStorage = mVariableModuleGlobal->VariableGlobal.VariableStores[0]; + Status = VariableStorage->GetNextVariableName ( + VariableStorage, + VariableNameSize, + VariableName, + VariableGuid, + &VarAttributes + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "VariableStorageProtocol->GetNextVariableName status %r\n", Status)); + return Status; + } else { + // + // Don't return this variable if we are at runtime and the variable's attributes do not include + // the EFI_VARIABLE_RUNTIME_ACCESS flag. If this is true, advance to the next variable + // + if (((VarAttributes & EFI_VARIABLE_RUNTIME_ACCESS) != 0) || !AtRuntime ()) { + return Status; + } + } + } + + SearchComplete = FALSE; + while (!SearchComplete) { + Status = GetVariableStorageId (VariableName, VariableGuid, TRUE, &VariableStorageId); + if (EFI_ERROR (Status)) { + ASSERT_EFI_ERROR (Status); + return Status; + } + + VarNameSize = CallerVariableNameBufferSize; + + for ( Index = 0; + Index < mVariableModuleGlobal->VariableGlobal.VariableStoresCount; + Index++) { + VariableStorage = mVariableModuleGlobal->VariableGlobal.VariableStores[Index]; + ZeroMem ((VOID *) &InstanceGuid, sizeof (EFI_GUID)); + Status = VariableStorage->GetId (&InstanceGuid); + if (EFI_ERROR (Status)) { + return Status; + } + if (CompareGuid (&VariableStorageId, &InstanceGuid)) { + Status = VariableStorage->GetNextVariableName ( + VariableStorage, + &VarNameSize, + VariableName, + VariableGuid, + &VarAttributes + ); + if (!EFI_ERROR (Status)) { + if (VariableExistsInHob (VariableName, VariableGuid)) { + // + // Don't return this variable if there is a HOB variable that overrides it + // advance to the next variable + // + break; + } + if (((VarAttributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0) && AtRuntime ()) { + // + // Don't return this variable if we are at runtime and the variable's attributes do not include + // the EFI_VARIABLE_RUNTIME_ACCESS flag. If this is true, advance to the next variable + // + break; + } + goto Done; + } else if (Status == EFI_NOT_FOUND) { + // + // If we reached the end of the variables in the current NV storage + // get the first variable in the next NV storage + // + if ((Index + 1) < mVariableModuleGlobal->VariableGlobal.VariableStoresCount) { + VarNameSize = CallerVariableNameBufferSize; + VariableStorage = mVariableModuleGlobal->VariableGlobal.VariableStores[Index + 1]; + + ZeroMem ((VOID *) VariableGuid, sizeof (VariableGuid)); + Status = VariableStorage->GetNextVariableName ( + VariableStorage, + &VarNameSize, + VariableName, + VariableGuid, + &VarAttributes + ); + if (!EFI_ERROR (Status)) { + if (VariableExistsInHob (VariableName, VariableGuid)) { + // + // Don't return this variable if there is a HOB variable that overrides it + // advance to the next variable + // + break; + } + if (((VarAttributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0) && AtRuntime ()) { + // + // Don't return this variable if we are at runtime and the variable's attributes do not include + // the EFI_VARIABLE_RUNTIME_ACCESS flag. If this is true, advance to the next variable + // + break; + } + } else { + DEBUG ((DEBUG_ERROR, "VariableStorageProtocol->GetNextVariableName status %r\n", Status)); + return Status; + } + DEBUG ((DEBUG_INFO, "Variable.c: Status returned from the variable storage protocol is %r\n", Status)); + goto Done; + } else { + // This is the last variable + SearchComplete = TRUE; + break; + } + } else { + DEBUG ((DEBUG_ERROR, "VariableStorageProtocol->GetNextVariableName status %r\n", Status)); + return Status; + } + } + } + } + + return EFI_NOT_FOUND; + +Done: + *VariableNameSize = VarNameSize; + + if (CallerVariableNameBufferSize < VarNameSize) { + return EFI_BUFFER_TOO_SMALL; + } + + return EFI_SUCCESS; +} + + +/** + This code Finds the Next available variable. + + Caution: This function may receive untrusted input. + This function may be invoked in SMM mode. This function will do basic validation, before parse the data. + + @param[in, out] VariableNameSize Size of the variable name. + @param[in, out] VariableName Pointer to variable name. + @param[in, out] 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 + ) +{ + UINTN VariableNameBufferSize; + EFI_STATUS Status; + + if (VariableNameSize == NULL || VariableName == NULL || VendorGuid == NULL) { + return EFI_INVALID_PARAMETER; + } + + AcquireLockOnlyAtBootTime(&mVariableModuleGlobal->VariableGlobal.VariableServicesLock); + + VariableNameBufferSize = sizeof (mVariableNameBuffer); + ZeroMem ((VOID *) &mVariableNameBuffer[0], VariableNameBufferSize); + StrCpyS (&mVariableNameBuffer[0], VariableNameBufferSize, VariableName); + + Status = VariableServiceGetNextVariableInternal (&VariableNameBufferSize, &mVariableNameBuffer[0], VendorGuid); + ASSERT (Status != EFI_BUFFER_TOO_SMALL); + + if (!EFI_ERROR (Status)) { + if (VariableNameBufferSize > *VariableNameSize) { + *VariableNameSize = VariableNameBufferSize; + Status = EFI_BUFFER_TOO_SMALL; + } else { + StrCpyS (VariableName, *VariableNameSize, &mVariableNameBuffer[0]); + *VariableNameSize = VariableNameBufferSize; + } + } + + ReleaseLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock); + + return Status; +} + + +/** + This code sets variable in storage blocks (Volatile or Non-Volatile). + + Caution: This function may receive untrusted input. + This function may be invoked in SMM mode, and datasize and data are external input. + This function will do basic validation, before parse the data. + This function will parse the authentication carefully to avoid security issues, like + buffer overflow, integer overflow. + This function will check attribute carefully to avoid authentication bypass. + + @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. + + @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 + ) +{ + VARIABLE_POINTER_TRACK Variable; + EFI_STATUS Status; + UINTN PayloadSize; + + // + // Check input parameters. + // + if (VariableName == NULL || VariableName[0] == 0 || VendorGuid == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (DataSize != 0 && Data == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Check for the reserverd bit in the variable attributes. + // + if ((Attributes & (~EFI_VARIABLE_ATTRIBUTES_MASK)) != 0) { + return EFI_INVALID_PARAMETER; + } + + // + // Make sure that if the runtime bit is set, the boot service bit is also set. + // + if ((Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == EFI_VARIABLE_RUNTIME_ACCESS) { + return EFI_INVALID_PARAMETER; + } else if ((Attributes & VARIABLE_ATTRIBUTE_AT_AW) != 0) { + if (!mVariableModuleGlobal->VariableGlobal.AuthSupport) { + // + // Authenticated variable writes are not supported. + // + return EFI_INVALID_PARAMETER; + } + } else if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != 0) { + if (PcdGet32 (PcdHwErrStorageSize) == 0) { + // + // Harware error record variables are not supported on this platform. + // + return EFI_INVALID_PARAMETER; + } + } + + // + // EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS and EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS + // cannot both be set. + // + if (((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) == EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) + && ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) == EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) { + return EFI_INVALID_PARAMETER; + } + + if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) == EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) { + if (DataSize < AUTHINFO_SIZE) { + // + // User attempted to write to a Authenticated Variable without AuthInfo. + // + return EFI_SECURITY_VIOLATION; + } + PayloadSize = DataSize - AUTHINFO_SIZE; + } else if ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) == EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) { + // + // Sanity check for EFI_VARIABLE_AUTHENTICATION_2 descriptor. + // + if (DataSize < OFFSET_OF_AUTHINFO2_CERT_DATA || + ((EFI_VARIABLE_AUTHENTICATION_2 *) Data)->AuthInfo.Hdr.dwLength > DataSize - (OFFSET_OF (EFI_VARIABLE_AUTHENTICATION_2, AuthInfo)) || + ((EFI_VARIABLE_AUTHENTICATION_2 *) Data)->AuthInfo.Hdr.dwLength < OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData)) { + return EFI_SECURITY_VIOLATION; + } + PayloadSize = DataSize - AUTHINFO2_SIZE (Data); + } else { + PayloadSize = DataSize; + } + + if ((UINTN)(~0) - PayloadSize < StrSize(VariableName)){ + // + // Prevent whole variable size overflow + // + return EFI_INVALID_PARAMETER; + } + + // + // The size of the VariableName, including the Unicode Null in bytes plus + // the DataSize is limited to maximum size of PcdGet32 (PcdMaxHardwareErrorVariableSize) + // bytes for HwErrRec#### variable. + // + if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) { + if (StrSize (VariableName) + PayloadSize > PcdGet32 (PcdMaxHardwareErrorVariableSize) - GetVariableHeaderSize ()) { + return EFI_INVALID_PARAMETER; + } + } else { + // + // The size of the VariableName, including the Unicode Null in bytes plus + // the DataSize is limited to maximum size of Max(Auth)VariableSize bytes. + // + if ((Attributes & VARIABLE_ATTRIBUTE_AT_AW) != 0) { + if (StrSize (VariableName) + PayloadSize > mVariableModuleGlobal->MaxAuthVariableSize - GetVariableHeaderSize ()) { + return EFI_INVALID_PARAMETER; + } + } else { + if (StrSize (VariableName) + PayloadSize > mVariableModuleGlobal->MaxVariableSize - GetVariableHeaderSize ()) { + return EFI_INVALID_PARAMETER; + } + } + } + + // + // Special Handling for MOR Lock variable. + // + Status = SetVariableCheckHandlerMor (VariableName, VendorGuid, Attributes, PayloadSize, (VOID *) ((UINTN) Data + DataSize - PayloadSize)); + if (Status == EFI_ALREADY_STARTED) { + // + // EFI_ALREADY_STARTED means the SetVariable() action is handled inside of SetVariableCheckHandlerMor(). + // Variable driver can just return SUCCESS. + // + return EFI_SUCCESS; + } + if (EFI_ERROR (Status)) { + return Status; + } + + Status = VarCheckLibSetVariableCheck (VariableName, VendorGuid, Attributes, PayloadSize, (VOID *) ((UINTN) Data + DataSize - PayloadSize), mRequestSource); + if (EFI_ERROR (Status)) { + return Status; + } + + AcquireLockOnlyAtBootTime(&mVariableModuleGlobal->VariableGlobal.VariableServicesLock); + + // + // Consider reentrant in MCA/INIT/NMI. It needs be reupdated. + // + InterlockedIncrement (&mVariableModuleGlobal->VariableGlobal.ReentrantState); + + // + // Check whether the input variable is already existed. + // + Status = FindVariable (VariableName, VendorGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, TRUE); + if (!EFI_ERROR (Status)) { + if (((Variable.CurrPtr->Attributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0) && AtRuntime ()) { + Status = EFI_WRITE_PROTECTED; + goto Done; + } + if (Attributes != 0 && (Attributes & (~EFI_VARIABLE_APPEND_WRITE)) != Variable.CurrPtr->Attributes) { + // + // If a preexisting variable is rewritten with different attributes, SetVariable() shall not + // modify the variable and shall return EFI_INVALID_PARAMETER. Two exceptions to this rule: + // 1. No access attributes specified + // 2. The only attribute differing is EFI_VARIABLE_APPEND_WRITE + // + Status = EFI_INVALID_PARAMETER; + DEBUG ((EFI_D_INFO, " Variable Driver: Rewritten a preexisting variable(0x%08x) with different attributes(0x%08x) - %g:%s\n", Variable.CurrPtr->Attributes, Attributes, VendorGuid, VariableName)); + goto Done; + } + } + + Status = VarCheckLibSetVariableCheck (VariableName, VendorGuid, Attributes, PayloadSize, (VOID *) ((UINTN) Data + DataSize - PayloadSize), mRequestSource); + if (EFI_ERROR (Status)) { + return Status; + } + + if (!FeaturePcdGet (PcdUefiVariableDefaultLangDeprecate)) { + // + // Hook the operation of setting PlatformLangCodes/PlatformLang and LangCodes/Lang. + // + Status = AutoUpdateLangVariable (VariableName, Data, DataSize); + if (EFI_ERROR (Status)) { + // + // The auto update operation failed, directly return to avoid inconsistency between PlatformLang and Lang. + // + goto Done; + } + } + + if (mVariableModuleGlobal->VariableGlobal.AuthSupport) { + Status = AuthVariableLibProcessVariable (VariableName, VendorGuid, Data, DataSize, Attributes); + } else { + Status = UpdateVariable (VariableName, VendorGuid, Data, DataSize, Attributes, 0, 0, &Variable, NULL); + } + +Done: + InterlockedDecrement (&mVariableModuleGlobal->VariableGlobal.ReentrantState); + ReleaseLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock); + + if (!AtRuntime ()) { + if (!EFI_ERROR (Status)) { + SecureBootHook ( + VariableName, + VendorGuid + ); + } + } + + return Status; +} + + +/** + This code returns information about the EFI variables. + + Caution: This function may receive untrusted input. + This function may be invoked in SMM mode. This function will do basic validation, before parse the data. + + @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. + + @return EFI_SUCCESS Query successfully. + +**/ +EFI_STATUS +EFIAPI +VariableServiceQueryVariableInfoInternal ( + IN UINT32 Attributes, + OUT UINT64 *MaximumVariableStorageSize, + OUT UINT64 *RemainingVariableStorageSize, + OUT UINT64 *MaximumVariableSize + ) +{ + VARIABLE_HEADER *Variable; + VARIABLE_HEADER *NextVariable; + UINT64 VariableSize; + VARIABLE_STORE_HEADER *VariableStoreHeader; + UINT64 CommonVariableTotalSize; + UINT64 HwErrVariableTotalSize; + VARIABLE_STORAGE_PROTOCOL *VariableStorageProtocol; + UINTN Index; + UINT32 VariableStoreSize; + UINT32 VspCommonVariablesTotalSize; + UINT32 VspHwErrVariablesTotalSize; + EFI_STATUS Status; + VARIABLE_POINTER_TRACK VariablePtrTrack; + + CommonVariableTotalSize = 0; + HwErrVariableTotalSize = 0; + + if ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0) { + // + // Query is Volatile related. + // + VariableStoreHeader = (VARIABLE_STORE_HEADER *) ((UINTN) mVariableModuleGlobal->VariableGlobal.VolatileVariableBase); + } else { + // + // Query is Non-Volatile related. + // + VariableStoreHeader = mNvVariableCache; + } + + // + // Now let's fill *MaximumVariableStorageSize *RemainingVariableStorageSize + // with the storage size (excluding the storage header size). + // + *MaximumVariableStorageSize = VariableStoreHeader->Size - sizeof (VARIABLE_STORE_HEADER); + + // + // Harware error record variable needs larger size. + // + if ((Attributes & (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_HARDWARE_ERROR_RECORD)) == (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_HARDWARE_ERROR_RECORD)) { + *MaximumVariableStorageSize = PcdGet32 (PcdHwErrStorageSize); + *MaximumVariableSize = PcdGet32 (PcdMaxHardwareErrorVariableSize) - GetVariableHeaderSize (); + } else { + if ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) { + if (AtRuntime ()) { + *MaximumVariableStorageSize = mVariableModuleGlobal->CommonRuntimeVariableSpace; + } else { + *MaximumVariableStorageSize = mVariableModuleGlobal->CommonVariableSpace; + } + } + + // + // Let *MaximumVariableSize be Max(Auth)VariableSize with the exception of the variable header size. + // + if ((Attributes & VARIABLE_ATTRIBUTE_AT_AW) != 0) { + *MaximumVariableSize = mVariableModuleGlobal->MaxAuthVariableSize - GetVariableHeaderSize (); + } else { + *MaximumVariableSize = mVariableModuleGlobal->MaxVariableSize - GetVariableHeaderSize (); + } + } + + // + // Point to the starting address of the variables. + // + Variable = GetStartPointer (VariableStoreHeader); + + if ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0) { + // + // For Volatile related, walk through the variable store. + // + while (IsValidVariableHeader (Variable, GetEndPointer (VariableStoreHeader))) { + NextVariable = GetNextVariablePtr (Variable); + VariableSize = (UINT64) (UINTN) NextVariable - (UINT64) (UINTN) Variable; + + if (AtRuntime ()) { + // + // 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. + // + if ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) { + HwErrVariableTotalSize += VariableSize; + } else { + CommonVariableTotalSize += VariableSize; + } + } else { + // + // Only care about Variables with State VAR_ADDED, because + // the space not marked as VAR_ADDED is reclaimable now. + // + if (Variable->State == VAR_ADDED) { + if ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) { + HwErrVariableTotalSize += VariableSize; + } else { + CommonVariableTotalSize += VariableSize; + } + } else if (Variable->State == (VAR_IN_DELETED_TRANSITION & VAR_ADDED)) { + // + // If it is a IN_DELETED_TRANSITION variable, + // and there is not also a same ADDED one at the same time, + // this IN_DELETED_TRANSITION variable is valid. + // + VariablePtrTrack.StartPtr = GetStartPointer (VariableStoreHeader); + VariablePtrTrack.EndPtr = GetEndPointer (VariableStoreHeader); + Status = FindVariableEx ( + GetVariableNamePtr (Variable), + GetVendorGuidPtr (Variable), + FALSE, + &VariablePtrTrack + ); + if (!EFI_ERROR (Status) && VariablePtrTrack.CurrPtr->State != VAR_ADDED) { + if ((Variable->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) { + HwErrVariableTotalSize += VariableSize; + } else { + CommonVariableTotalSize += VariableSize; + } + } + } + } + + // + // Go to the next one. + // + Variable = NextVariable; + } + } else { + // + // For Non Volatile related, call GetStorageUsage() on the VARIABLE_STORAGE_PROTOCOLs + // + for ( Index = 0; + Index < mVariableModuleGlobal->VariableGlobal.VariableStoresCount; + Index++) { + VariableStorageProtocol = mVariableModuleGlobal->VariableGlobal.VariableStores[Index]; + Status = VariableStorageProtocol->GetStorageUsage ( + VariableStorageProtocol, + AtRuntime (), + &VariableStoreSize, + &VspCommonVariablesTotalSize, + &VspHwErrVariablesTotalSize + ); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + return Status; + } + CommonVariableTotalSize += VspCommonVariablesTotalSize; + HwErrVariableTotalSize += VspHwErrVariablesTotalSize; + } + } + + if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) == EFI_VARIABLE_HARDWARE_ERROR_RECORD){ + *RemainingVariableStorageSize = *MaximumVariableStorageSize - HwErrVariableTotalSize; + } else { + if (*MaximumVariableStorageSize < CommonVariableTotalSize) { + *RemainingVariableStorageSize = 0; + } else { + *RemainingVariableStorageSize = *MaximumVariableStorageSize - CommonVariableTotalSize; + } + } + + if (*RemainingVariableStorageSize < GetVariableHeaderSize ()) { + *MaximumVariableSize = 0; + } else if ((*RemainingVariableStorageSize - GetVariableHeaderSize ()) < *MaximumVariableSize) { + *MaximumVariableSize = *RemainingVariableStorageSize - GetVariableHeaderSize (); + } + + return EFI_SUCCESS; +} + + +/** + This code returns information about the EFI variables. + + Caution: This function may receive untrusted input. + This function may be invoked in SMM mode. This function will do basic validation, before parse the data. + + @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. + + @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 + ) +{ + EFI_STATUS Status; + + if(MaximumVariableStorageSize == NULL || RemainingVariableStorageSize == NULL || MaximumVariableSize == NULL || Attributes == 0) { + return EFI_INVALID_PARAMETER; + } + + if ((Attributes & EFI_VARIABLE_ATTRIBUTES_MASK) == 0) { + // + // Make sure the Attributes combination is supported by the platform. + // + return EFI_UNSUPPORTED; + } else if ((Attributes & (EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS)) == EFI_VARIABLE_RUNTIME_ACCESS) { + // + // Make sure if runtime bit is set, boot service bit is set also. + // + return EFI_INVALID_PARAMETER; + } else if (AtRuntime () && ((Attributes & EFI_VARIABLE_RUNTIME_ACCESS) == 0)) { + // + // Make sure RT Attribute is set if we are in Runtime phase. + // + return EFI_INVALID_PARAMETER; + } else if ((Attributes & (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_HARDWARE_ERROR_RECORD)) == EFI_VARIABLE_HARDWARE_ERROR_RECORD) { + // + // Make sure Hw Attribute is set with NV. + // + return EFI_INVALID_PARAMETER; + } else if ((Attributes & VARIABLE_ATTRIBUTE_AT_AW) != 0) { + if (!mVariableModuleGlobal->VariableGlobal.AuthSupport) { + // + // Not support authenticated variable write. + // + return EFI_UNSUPPORTED; + } + } else if ((Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) != 0) { + if (PcdGet32 (PcdHwErrStorageSize) == 0) { + // + // Not support harware error record variable variable. + // + return EFI_UNSUPPORTED; + } + } + + AcquireLockOnlyAtBootTime(&mVariableModuleGlobal->VariableGlobal.VariableServicesLock); + + Status = VariableServiceQueryVariableInfoInternal ( + Attributes, + MaximumVariableStorageSize, + RemainingVariableStorageSize, + MaximumVariableSize + ); + + ReleaseLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock); + return Status; +} + + +/** + This function reclaims variable storage if free size is below the threshold. + + Caution: This function may be invoked at SMM mode. + Care must be taken to make sure not security issue. + +**/ +VOID +ReclaimForOS( + VOID + ) +{ + EFI_STATUS Status; + UINTN RemainingCommonRuntimeVariableSpace; + UINTN RemainingHwErrVariableSpace; + UINTN CacheOffset; + STATIC BOOLEAN Reclaimed; + + // + // This function will be called only once at EndOfDxe or ReadyToBoot event. + // + if (Reclaimed) { + return; + } + Reclaimed = TRUE; + + Status = EFI_SUCCESS; + + if (mVariableModuleGlobal->CommonRuntimeVariableSpace < mVariableModuleGlobal->CommonVariableTotalSize) { + RemainingCommonRuntimeVariableSpace = 0; + } else { + RemainingCommonRuntimeVariableSpace = mVariableModuleGlobal->CommonRuntimeVariableSpace - mVariableModuleGlobal->CommonVariableTotalSize; + } + + RemainingHwErrVariableSpace = PcdGet32 (PcdHwErrStorageSize) - mVariableModuleGlobal->HwErrVariableTotalSize; + + // + // Check if the free area is below a threshold. + // + if (((RemainingCommonRuntimeVariableSpace < mVariableModuleGlobal->MaxVariableSize) || + (RemainingCommonRuntimeVariableSpace < mVariableModuleGlobal->MaxAuthVariableSize)) || + ((PcdGet32 (PcdHwErrStorageSize) != 0) && + (RemainingHwErrVariableSpace < PcdGet32 (PcdMaxHardwareErrorVariableSize)))) { + Status = Reclaim ( + (EFI_PHYSICAL_ADDRESS) (UINTN) mNvVariableCache, + &CacheOffset, + FALSE, + NULL, + NULL, + 0 + ); + ASSERT_EFI_ERROR (Status); + } +} + + +/** + Get non-volatile maximum variable size. + + @return Non-volatile maximum variable size. + +**/ +UINTN +GetNonVolatileMaxVariableSize ( + VOID + ) +{ + if (PcdGet32 (PcdHwErrStorageSize) != 0) { + return MAX (MAX (PcdGet32 (PcdMaxVariableSize), PcdGet32 (PcdMaxAuthVariableSize)), + PcdGet32 (PcdMaxHardwareErrorVariableSize)); + } else { + return MAX (PcdGet32 (PcdMaxVariableSize), PcdGet32 (PcdMaxAuthVariableSize)); + } +} + + +/** + A callback invoked by each VARIABLE_STORAGE_PROTOCOL to indicate to the core variable driver that + SetVariable() is ready for use on that VARIABLE_STORAGE_PROTOCOL + + @retval EFI_SUCCESS Change to WriteServiceIsReady() status was processed successfully. + +**/ +EFI_STATUS +EFIAPI +VariableStorageWriteServiceReadyCallback ( + VOID + ) +{ + EFI_STATUS Status; + UINTN Index; + VARIABLE_STORAGE_PROTOCOL *VariableStorageProtocol; + BOOLEAN WriteServiceReady; + + Status = EFI_SUCCESS; + WriteServiceReady = TRUE; + for ( Index = 0; + Index < mVariableModuleGlobal->VariableGlobal.VariableStoresCount; + Index++) { + VariableStorageProtocol = mVariableModuleGlobal->VariableGlobal.VariableStores[Index]; + if (!VariableStorageProtocol->WriteServiceIsReady (VariableStorageProtocol)) { + WriteServiceReady = FALSE; + break; + } + } + if (WriteServiceReady && !mVariableModuleGlobal->WriteServiceReady) { + mVariableModuleGlobal->WriteServiceReady = TRUE; + Status = VariableWriteServiceInitialize (); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, " Variable Driver: Variable write service initialization failed. Status = %r\n", Status)); + } else { + // + // Install the Variable Write Architectural protocol + // + InstallVariableWriteReady (); + } + } + return Status; +} + + +/** + Init non-volatile variable store. + + @retval EFI_SUCCESS Function successfully executed. + @retval EFI_OUT_OF_RESOURCES Fail to allocate enough memory resource. + @retval EFI_VOLUME_CORRUPTED Variable Store or Firmware Volume for Variable Store is corrupted. + +**/ +EFI_STATUS +InitNonVolatileVariableStore ( + VOID + ) +{ + EFI_STATUS Status; + UINTN Index; + VARIABLE_STORAGE_PROTOCOL *VariableStorageProtocol; + UINT32 NvStoreTotalSize; + UINT32 VariableStoreSize; + UINT32 CommonVariablesTotalSize; + UINT32 HwErrVariablesTotalSize; + UINT32 HwErrStorageSize; + UINT32 MaxUserNvVariableSpaceSize; + UINT32 BoottimeReservedNvVariableSpaceSize; + BOOLEAN AuthSupported; + + // + // Get the total NV storage size from all VARIABLE_STORAGE_PROTOCOLs, and the amount of space + // used + // + NvStoreTotalSize = 0; + mVariableModuleGlobal->CommonVariableTotalSize = 0; + mVariableModuleGlobal->HwErrVariableTotalSize = 0; + mVariableModuleGlobal->VariableGlobal.AuthFormat = TRUE; + mVariableModuleGlobal->WriteServiceReady = FALSE; + + if (!mForceVolatileVariable) { + for ( Index = 0; + Index < mVariableModuleGlobal->VariableGlobal.VariableStoresCount; + Index++) { + VariableStorageProtocol = mVariableModuleGlobal->VariableGlobal.VariableStores[Index]; + Status = VariableStorageProtocol->GetStorageUsage ( + VariableStorageProtocol, + AtRuntime (), + &VariableStoreSize, + &CommonVariablesTotalSize, + &HwErrVariablesTotalSize + ); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + return Status; + } + + DEBUG ((EFI_D_INFO, "+-+-> Variable Driver: Data from StorageUsage VariableStorageProtocol[%d]:\n VariableStoreSize = %d bytes.\n CommonVariablesTotalSize = %d bytes. HwErrVariablesTotalSize = %d bytes.\n", + Index, VariableStoreSize, CommonVariablesTotalSize, HwErrVariablesTotalSize)); + + NvStoreTotalSize += VariableStoreSize; + mVariableModuleGlobal->CommonVariableTotalSize += CommonVariablesTotalSize; + mVariableModuleGlobal->HwErrVariableTotalSize += HwErrVariablesTotalSize; + + // + // Determine authenticated variables support. If all of the + // VARIABLE_STORAGE_PROTOCOLs support authenticated variables, enable it. If + // any of the VARIABLE_STORAGE_PROTOCOLs do not support it disable it globally. + // + if (mVariableModuleGlobal->VariableGlobal.AuthFormat) { + AuthSupported = FALSE; + Status = VariableStorageProtocol->GetAuthenticatedSupport ( + VariableStorageProtocol, + &AuthSupported + ); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + return Status; + } + if (!AuthSupported) { + mVariableModuleGlobal->VariableGlobal.AuthFormat = FALSE; + } + } + } + } else { + NvStoreTotalSize = DEFAULT_NV_STORE_SIZE; + mVariableModuleGlobal->CommonVariableTotalSize = 0; + mVariableModuleGlobal->HwErrVariableTotalSize = 0; + } + + DEBUG ((EFI_D_INFO, "+-+-> Variable Driver: Total data from StorageUsage:\n NvStoreTotalSize = %d bytes.\n CommonVariablesTotalSize = %d bytes. HwErrVariablesTotalSize = %d bytes.\n", + NvStoreTotalSize, mVariableModuleGlobal->CommonVariableTotalSize, mVariableModuleGlobal->HwErrVariableTotalSize)); + + // + // Allocate NV Storage Cache and initialize it to all 1's (like an erased FV) + // + // The NV Storage Cache allows the variable services to cache any NV variable + // returned from the VARIABLE_STORAGE_PROTOCOLs so that subsequent reads will + // be returned from memory and not require additional NV access + // + NvStoreTotalSize = ALIGN_VALUE (NvStoreTotalSize, sizeof (UINT32)); + mNvVariableCache = (VARIABLE_STORE_HEADER *) AllocateRuntimePool (NvStoreTotalSize); + if (mNvVariableCache == NULL) { + return EFI_OUT_OF_RESOURCES; + } + SetMem32 ((VOID *) mNvVariableCache, NvStoreTotalSize, (UINT32) 0xFFFFFFFF); + // + // Initialize the VARIABLE_STORE_HEADER for the NV Storage Cache + // + ZeroMem ((VOID *) mNvVariableCache, sizeof (VARIABLE_STORE_HEADER)); + if (mVariableModuleGlobal->VariableGlobal.AuthFormat) { + CopyMem (&mNvVariableCache->Signature, &gEfiAuthenticatedVariableGuid, sizeof (EFI_GUID)); + } else { + CopyMem (&mNvVariableCache->Signature, &gEfiVariableGuid, sizeof (EFI_GUID)); + } + mNvVariableCache->Size = NvStoreTotalSize; + mNvVariableCache->Format = VARIABLE_STORE_FORMATTED; + mNvVariableCache->State = VARIABLE_STORE_HEALTHY; + // + // Initialize mVariableModuleGlobal + // + HwErrStorageSize = PcdGet32 (PcdHwErrStorageSize); + MaxUserNvVariableSpaceSize = PcdGet32 (PcdMaxUserNvVariableSpaceSize); + BoottimeReservedNvVariableSpaceSize = PcdGet32 (PcdBoottimeReservedNvVariableSpaceSize); + // + // Note that in EdkII variable driver implementation, Hardware Error Record type variable + // is stored with common variable in the same NV region. So the platform integrator should + // ensure that the value of PcdHwErrStorageSize is less than the value of + // (NvStoreTotalSize - sizeof (VARIABLE_STORE_HEADER)). + // + ASSERT (HwErrStorageSize < (NvStoreTotalSize - sizeof (VARIABLE_STORE_HEADER))); + // + // Ensure that the value of PcdMaxUserNvVariableSpaceSize is less than the value of + // (NvStoreTotalSize - sizeof (VARIABLE_STORE_HEADER)) - PcdGet32 (PcdHwErrStorageSize). + // + ASSERT (MaxUserNvVariableSpaceSize < (NvStoreTotalSize - sizeof (VARIABLE_STORE_HEADER) - HwErrStorageSize)); + // + // Ensure that the value of PcdBoottimeReservedNvVariableSpaceSize is less than the value of + // (NvStoreTotalSize - sizeof (VARIABLE_STORE_HEADER)) - PcdGet32 (PcdHwErrStorageSize). + // + ASSERT (BoottimeReservedNvVariableSpaceSize < (NvStoreTotalSize - sizeof (VARIABLE_STORE_HEADER) - HwErrStorageSize)); + + mVariableModuleGlobal->CommonVariableSpace = ((UINTN) NvStoreTotalSize - sizeof (VARIABLE_STORE_HEADER) - HwErrStorageSize); + mVariableModuleGlobal->CommonMaxUserVariableSpace = ((MaxUserNvVariableSpaceSize != 0) ? MaxUserNvVariableSpaceSize : mVariableModuleGlobal->CommonVariableSpace); + mVariableModuleGlobal->CommonRuntimeVariableSpace = mVariableModuleGlobal->CommonVariableSpace - BoottimeReservedNvVariableSpaceSize; + + DEBUG ((EFI_D_INFO, " Variable Driver: Variable driver common space: 0x%x 0x%x 0x%x\n", mVariableModuleGlobal->CommonVariableSpace, mVariableModuleGlobal->CommonMaxUserVariableSpace, mVariableModuleGlobal->CommonRuntimeVariableSpace)); + + // + // The max NV variable size should be < (NvStoreTotalSize - sizeof (VARIABLE_STORE_HEADER)). + // + ASSERT (GetNonVolatileMaxVariableSize () < (NvStoreTotalSize - sizeof (VARIABLE_STORE_HEADER))); + + mVariableModuleGlobal->MaxVariableSize = PcdGet32 (PcdMaxVariableSize); + mVariableModuleGlobal->MaxAuthVariableSize = ((PcdGet32 (PcdMaxAuthVariableSize) != 0) ? PcdGet32 (PcdMaxAuthVariableSize) : mVariableModuleGlobal->MaxVariableSize); + + return EFI_SUCCESS; +} + + +/** + Flush the HOB variable to flash. + + @param[in] VariableName Name of variable has been updated or deleted. + @param[in] VendorGuid Guid of variable has been updated or deleted. + +**/ +VOID +FlushHobVariableToFlash ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid + ) +{ + EFI_STATUS Status; + VARIABLE_STORE_HEADER *VariableStoreHeader; + VARIABLE_HEADER *Variable; + VOID *VariableData; + VARIABLE_POINTER_TRACK VariablePtrTrack; + BOOLEAN ErrorFlag; + + ErrorFlag = FALSE; + + // + // Flush the HOB variable to flash. + // + if (mVariableModuleGlobal->VariableGlobal.HobVariableBase != 0) { + VariableStoreHeader = (VARIABLE_STORE_HEADER *) (UINTN) mVariableModuleGlobal->VariableGlobal.HobVariableBase; + // + // Set HobVariableBase to 0, it can avoid SetVariable to call back. + // + mVariableModuleGlobal->VariableGlobal.HobVariableBase = 0; + for ( Variable = GetStartPointer (VariableStoreHeader) + ; IsValidVariableHeader (Variable, GetEndPointer (VariableStoreHeader)) + ; Variable = GetNextVariablePtr (Variable) + ) { + if (Variable->State != VAR_ADDED) { + // + // The HOB variable has been set to DELETED state in local. + // + continue; + } + ASSERT ((Variable->Attributes & EFI_VARIABLE_NON_VOLATILE) != 0); + if (VendorGuid == NULL || VariableName == NULL || + !CompareGuid (VendorGuid, GetVendorGuidPtr (Variable)) || + StrCmp (VariableName, GetVariableNamePtr (Variable)) != 0) { + VariableData = GetVariableDataPtr (Variable); + FindVariable (GetVariableNamePtr (Variable), GetVendorGuidPtr (Variable), &VariablePtrTrack, &mVariableModuleGlobal->VariableGlobal, FALSE); + Status = UpdateVariable ( + GetVariableNamePtr (Variable), + GetVendorGuidPtr (Variable), + VariableData, + DataSizeOfVariable (Variable), + Variable->Attributes, + 0, + 0, + &VariablePtrTrack, + NULL + ); + DEBUG ((EFI_D_INFO, " Variable Driver: Flushed the HOB variable to flash: %g %s %r\n", GetVendorGuidPtr (Variable), GetVariableNamePtr (Variable), Status)); + } else { + // + // The updated or deleted variable is matched with this HOB variable. + // Don't break here because we will try to set other HOB variables + // since this variable could be set successfully. + // + Status = EFI_SUCCESS; + } + if (!EFI_ERROR (Status)) { + // + // If set variable successful, or the updated or deleted variable is matched with the HOB variable, + // set the HOB variable to DELETED state in local. + // + DEBUG ((EFI_D_INFO, " Variable Driver: Set the HOB variable to DELETED state in local: %g %s\n", GetVendorGuidPtr (Variable), GetVariableNamePtr (Variable))); + Variable->State &= VAR_DELETED; + } else { + ErrorFlag = TRUE; + } + } + if (ErrorFlag) { + // + // We still have HOB variable(s) not flushed in flash. + // + mVariableModuleGlobal->VariableGlobal.HobVariableBase = (EFI_PHYSICAL_ADDRESS) (UINTN) VariableStoreHeader; + } else { + // + // All HOB variables have been flushed in flash. + // + DEBUG ((EFI_D_INFO, " Variable Driver: All HOB variables have been flushed in flash.\n")); + if (!AtRuntime ()) { + FreePool ((VOID *) VariableStoreHeader); + } + } + } + +} + + +/** + Initializes variable write service after FTW was ready. + + @retval EFI_SUCCESS Function successfully executed. + @retval Others Fail to initialize the variable service. + +**/ +EFI_STATUS +VariableWriteServiceInitialize ( + VOID + ) +{ + EFI_STATUS Status; + UINTN Index; + VARIABLE_ENTRY_PROPERTY *VariableEntry; + + AcquireLockOnlyAtBootTime(&mVariableModuleGlobal->VariableGlobal.VariableServicesLock); + + FlushHobVariableToFlash (NULL, NULL); + + Status = EFI_SUCCESS; + ZeroMem (&mAuthContextOut, sizeof (mAuthContextOut)); + if (mVariableModuleGlobal->VariableGlobal.AuthFormat) { + // + // Authenticated variable initialize. + // + mAuthContextIn.StructSize = sizeof (AUTH_VAR_LIB_CONTEXT_IN); + mAuthContextIn.MaxAuthVariableSize = mVariableModuleGlobal->MaxAuthVariableSize - GetVariableHeaderSize (); + Status = AuthVariableLibInitialize (&mAuthContextIn, &mAuthContextOut); + if (!EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, " Variable Driver: Variable driver will work with auth variable support!\n")); + mVariableModuleGlobal->VariableGlobal.AuthSupport = TRUE; + if (mAuthContextOut.AuthVarEntry != NULL) { + for (Index = 0; Index < mAuthContextOut.AuthVarEntryCount; Index++) { + VariableEntry = &mAuthContextOut.AuthVarEntry[Index]; + Status = VarCheckLibVariablePropertySet ( + VariableEntry->Name, + VariableEntry->Guid, + &VariableEntry->VariableProperty + ); + ASSERT_EFI_ERROR (Status); + } + } + } else if (Status == EFI_UNSUPPORTED) { + DEBUG ((EFI_D_INFO, " Variable Driver: NOTICE - AuthVariableLibInitialize() returns %r!\n", Status)); + DEBUG ((EFI_D_INFO, " Variable Driver: Will continue to work without auth variable support!\n")); + mVariableModuleGlobal->VariableGlobal.AuthSupport = FALSE; + Status = EFI_SUCCESS; + } + } + + if (!EFI_ERROR (Status)) { + for (Index = 0; Index < sizeof (mVariableEntryProperty) / sizeof (mVariableEntryProperty[0]); Index++) { + VariableEntry = &mVariableEntryProperty[Index]; + Status = VarCheckLibVariablePropertySet (VariableEntry->Name, VariableEntry->Guid, &VariableEntry->VariableProperty); + ASSERT_EFI_ERROR (Status); + } + } + + ReleaseLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock); + // + // Initialize MOR Lock variable. + // + MorLockInit(); + + return Status; +} + + +/** + 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 + ) +{ + EFI_STATUS Status; + VARIABLE_STORE_HEADER *VolatileVariableStore; + VARIABLE_STORE_HEADER *VariableStoreHeader; + UINT64 VariableStoreLength; + UINTN ScratchSize; + EFI_HOB_GUID_TYPE *GuidHob; + EFI_GUID *VariableGuid; + UINTN Index; + VARIABLE_STORAGE_PROTOCOL *VariableStorageProtocol; + + InitializeLock (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock, TPL_NOTIFY); + + // + // Allocate memory for volatile variable store, note that there is a scratch space to store scratch data. + // + ScratchSize = GetNonVolatileMaxVariableSize (); + mVariableModuleGlobal->ScratchBufferSize = ScratchSize; + VolatileVariableStore = AllocateRuntimePool (PcdGet32 (PcdVariableStoreSize) + ScratchSize); + if (VolatileVariableStore == NULL) { + FreePool (mVariableModuleGlobal); + return EFI_OUT_OF_RESOURCES; + } + mVariableDataBuffer = AllocateRuntimePool (ScratchSize); + if (mVariableDataBuffer == NULL) { + FreePool (mVariableModuleGlobal); + FreePool (VolatileVariableStore); + return EFI_OUT_OF_RESOURCES; + } + + // + // Init non-volatile variable store. + // + Status = InitNonVolatileVariableStore (); + if (EFI_ERROR (Status)) { + FreePool (mVariableModuleGlobal); + FreePool (mVariableDataBuffer); + FreePool (VolatileVariableStore); + return Status; + } + + // + // mVariableModuleGlobal->VariableGlobal.AuthFormat + // has been initialized in InitNonVolatileVariableStore(). + // + if (mVariableModuleGlobal->VariableGlobal.AuthFormat) { + DEBUG ((EFI_D_INFO, " Variable Driver: Will work with auth variable format!\n")); + // + // Set AuthSupport to FALSE first, VariableWriteServiceInitialize() will initialize it. + // + mVariableModuleGlobal->VariableGlobal.AuthSupport = FALSE; + VariableGuid = &gEfiAuthenticatedVariableGuid; + } else { + DEBUG ((EFI_D_INFO, " Variable Driver: Will work without auth variable support!\n")); + mVariableModuleGlobal->VariableGlobal.AuthSupport = FALSE; + VariableGuid = &gEfiVariableGuid; + } + + // + // Get HOB variable store. + // + GuidHob = GetFirstGuidHob (VariableGuid); + if (GuidHob != NULL) { + VariableStoreHeader = GET_GUID_HOB_DATA (GuidHob); + VariableStoreLength = (UINT64) (GuidHob->Header.HobLength - sizeof (EFI_HOB_GUID_TYPE)); + if (GetVariableStoreStatus (VariableStoreHeader) == EfiValid) { + mVariableModuleGlobal->VariableGlobal.HobVariableBase = (EFI_PHYSICAL_ADDRESS) (UINTN) AllocateRuntimeCopyPool ((UINTN) VariableStoreLength, (VOID *) VariableStoreHeader); + if (mVariableModuleGlobal->VariableGlobal.HobVariableBase == 0) { + FreePool (mNvVariableCache); + FreePool (mVariableModuleGlobal); + FreePool (mVariableDataBuffer); + FreePool (VolatileVariableStore); + return EFI_OUT_OF_RESOURCES; + } + } else { + DEBUG ((EFI_D_ERROR, "HOB Variable Store header is corrupted!\n")); + } + } + + SetMem (VolatileVariableStore, PcdGet32 (PcdVariableStoreSize) + ScratchSize, 0xff); + + // + // Initialize Variable Specific Data. + // + mVariableModuleGlobal->VariableGlobal.VolatileVariableBase = (EFI_PHYSICAL_ADDRESS) (UINTN) VolatileVariableStore; + mVariableModuleGlobal->VolatileLastVariableOffset = (UINTN) GetStartPointer (VolatileVariableStore) - (UINTN) VolatileVariableStore; + + CopyGuid (&VolatileVariableStore->Signature, VariableGuid); + VolatileVariableStore->Size = PcdGet32 (PcdVariableStoreSize); + VolatileVariableStore->Format = VARIABLE_STORE_FORMATTED; + VolatileVariableStore->State = VARIABLE_STORE_HEALTHY; + VolatileVariableStore->Reserved = 0; + VolatileVariableStore->Reserved1 = 0; + + // + // Setup the callback to determine when to enable variable writes + // + if (!mForceVolatileVariable) { + for ( Index = 0; + Index < mVariableModuleGlobal->VariableGlobal.VariableStoresCount; + Index++) { + VariableStorageProtocol = mVariableModuleGlobal->VariableGlobal.VariableStores[Index]; + VariableStorageProtocol->RegisterWriteServiceReadyCallback ( + VariableStorageProtocol, + VariableStorageWriteServiceReadyCallback + ); + } + } else { + mVariableModuleGlobal->WriteServiceReady = TRUE; + Status = VariableWriteServiceInitialize (); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, " Variable Driver: Variable write service initialization failed. Status = %r\n", Status)); + } else { + // + // Install the Variable Write Architectural protocol + // + InstallVariableWriteReady (); + } + } + + return EFI_SUCCESS; +} + diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.h b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.h new file mode 100644 index 0000000000..eebf4e878d --- /dev/null +++ b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/Variable.h @@ -0,0 +1,841 @@ +/** @file + The internal header file includes the common header files, defines + internal structure and functions used by Variable modules. + + Copyright (c) 2006 - 2016, 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_H_ +#define _VARIABLE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define EFI_VARIABLE_ATTRIBUTES_MASK (EFI_VARIABLE_NON_VOLATILE | \ + EFI_VARIABLE_BOOTSERVICE_ACCESS | \ + EFI_VARIABLE_RUNTIME_ACCESS | \ + EFI_VARIABLE_HARDWARE_ERROR_RECORD | \ + EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS | \ + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS | \ + EFI_VARIABLE_APPEND_WRITE) + +/// +/// The size of a 3 character ISO639 language code. +/// +#define ISO_639_2_ENTRY_SIZE 3 + +#define MAX_VARIABLE_NAME_SIZE 1024 +#define DEFAULT_NV_STORE_SIZE 0x20000 + +typedef enum { + VariableStoreTypeVolatile, + VariableStoreTypeHob, + VariableStoreTypeNvCache, + VariableStoreTypeMax +} VARIABLE_STORE_TYPE; + +typedef struct { + VARIABLE_HEADER *CurrPtr; + // + // If both ADDED and IN_DELETED_TRANSITION variable are present, + // InDeletedTransitionPtr will point to the IN_DELETED_TRANSITION one. + // Otherwise, CurrPtr will point to the ADDED or IN_DELETED_TRANSITION one, + // and InDeletedTransitionPtr will be NULL at the same time. + // + VARIABLE_HEADER *InDeletedTransitionPtr; + VARIABLE_HEADER *EndPtr; + VARIABLE_HEADER *StartPtr; + BOOLEAN Volatile; +} VARIABLE_POINTER_TRACK; + +typedef struct { + EFI_PHYSICAL_ADDRESS HobVariableBase; + EFI_PHYSICAL_ADDRESS VolatileVariableBase; + VARIABLE_STORAGE_PROTOCOL **VariableStores; + UINTN VariableStoresCount; + EFI_LOCK VariableServicesLock; + UINT32 ReentrantState; + BOOLEAN AuthFormat; + BOOLEAN AuthSupport; +} VARIABLE_GLOBAL; + +typedef struct { + VARIABLE_GLOBAL VariableGlobal; + UINTN VolatileLastVariableOffset; + UINTN CommonVariableSpace; + UINTN CommonMaxUserVariableSpace; + UINTN CommonRuntimeVariableSpace; + UINTN CommonVariableTotalSize; + UINTN CommonUserVariableTotalSize; + UINTN HwErrVariableTotalSize; + UINTN MaxVariableSize; + UINTN MaxAuthVariableSize; + UINTN ScratchBufferSize; + CHAR8 *PlatformLangCodes; + CHAR8 *LangCodes; + CHAR8 *PlatformLang; + CHAR8 Lang[ISO_639_2_ENTRY_SIZE + 1]; + BOOLEAN WriteServiceReady; +} VARIABLE_MODULE_GLOBAL; + +/** + Flush the HOB variable to flash. + + @param[in] VariableName Name of variable has been updated or deleted. + @param[in] VendorGuid Guid of variable has been updated or deleted. + +**/ +VOID +FlushHobVariableToFlash ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid + ); + +/** + Writes a buffer to variable storage space, in the working block. + + 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[in] VariableBase Base address of the variable to write. + @param[in] VariableBuffer Point to the variable 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. + +**/ +EFI_STATUS +FtwVariableSpace ( + IN EFI_PHYSICAL_ADDRESS VariableBase, + IN VARIABLE_STORE_HEADER *VariableBuffer + ); + +/** + Finds variable in storage blocks of volatile and non-volatile storage areas. + + This code finds variable in storage blocks of volatile and non-volatile storage areas. + If VariableName is an empty string, then we just return the first + qualified variable without comparing VariableName and VendorGuid. + If IgnoreRtCheck is TRUE, then we ignore the EFI_VARIABLE_RUNTIME_ACCESS attribute check + at runtime when searching existing variable, only VariableName and VendorGuid are compared. + Otherwise, variables without EFI_VARIABLE_RUNTIME_ACCESS are not visible at runtime. + + @param[in] VariableName Name of the variable to be found. + @param[in] VendorGuid Vendor GUID to be found. + @param[out] PtrTrack VARIABLE_POINTER_TRACK structure for output, + including the range searched and the target position. + @param[in] Global Pointer to VARIABLE_GLOBAL structure, including + base of volatile variable storage area, base of + NV variable storage area, and a lock. + @param[in] IgnoreRtCheck Ignore EFI_VARIABLE_RUNTIME_ACCESS attribute + check at runtime when searching variable. + + @retval EFI_INVALID_PARAMETER If VariableName is not an empty string, while + VendorGuid is NULL. + @retval EFI_SUCCESS Variable successfully found. + @retval EFI_NOT_FOUND Variable not found. + +**/ +EFI_STATUS +FindVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + OUT VARIABLE_POINTER_TRACK *PtrTrack, + IN VARIABLE_GLOBAL *Global, + IN BOOLEAN IgnoreRtCheck + ); + +/** + + Gets the pointer to the end of the variable storage area. + + This function gets pointer to the end of the variable storage + area, according to the input variable store header. + + @param[in] VarStoreHeader Pointer to the Variable Store Header. + + @return Pointer to the end of the variable storage area. + +**/ +VARIABLE_HEADER * +GetEndPointer ( + IN VARIABLE_STORE_HEADER *VarStoreHeader + ); + +/** + This code gets the size of variable header. + + @return Size of variable header in bytes in type UINTN. + +**/ +UINTN +GetVariableHeaderSize ( + VOID + ); + +/** + + This code gets the pointer to the variable name. + + @param[in] Variable Pointer to the Variable Header. + + @return Pointer to Variable Name which is Unicode encoding. + +**/ +CHAR16 * +GetVariableNamePtr ( + IN VARIABLE_HEADER *Variable + ); + +/** + This code gets the pointer to the variable guid. + + @param[in] Variable Pointer to the Variable Header. + + @return A EFI_GUID* pointer to Vendor Guid. + +**/ +EFI_GUID * +GetVendorGuidPtr ( + IN VARIABLE_HEADER *Variable + ); + +/** + + This code gets the pointer to the variable data. + + @param[in] Variable Pointer to the Variable Header. + + @return Pointer to Variable Data. + +**/ +UINT8 * +GetVariableDataPtr ( + IN VARIABLE_HEADER *Variable + ); + +/** + + This code gets the size of variable data. + + @param[in] Variable Pointer to the Variable Header. + + @return Size of variable in bytes. + +**/ +UINTN +DataSizeOfVariable ( + IN VARIABLE_HEADER *Variable + ); + +/** + This function is to check if the remaining variable space is enough to set + all Variables from argument list successfully. The purpose of the check + is to keep the consistency of the Variables to be in variable storage. + + Note: Variables are assumed to be in same storage. + The set sequence of Variables will be same with the sequence of VariableEntry from argument list, + so follow the argument sequence to check the Variables. + + @param[in] Attributes Variable attributes for Variable entries. + @param[in] Marker VA_LIST style variable argument list. + The variable argument list with type VARIABLE_ENTRY_CONSISTENCY *. + A NULL terminates the list. The VariableSize of + VARIABLE_ENTRY_CONSISTENCY is the variable data size as input. + It will be changed to variable total size as output. + + @retval TRUE Have enough variable space to set the Variables successfully. + @retval FALSE No enough variable space to set the Variables successfully. + +**/ +BOOLEAN +EFIAPI +CheckRemainingSpaceForConsistencyInternal ( + IN UINT32 Attributes, + IN VA_LIST Marker + ); + +/** + Update the variable region with Variable information. If EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS is set, + index of associated public key is needed. + + @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 Attributes of the variable. + @param[in] KeyIndex Index of associated public key. + @param[in] MonotonicCount Value of associated monotonic count. + @param[in, out] Variable The variable information that is used to keep track of variable usage. + + @param[in] TimeStamp Value of associated TimeStamp. + + @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 UINT32 KeyIndex OPTIONAL, + IN UINT64 MonotonicCount OPTIONAL, + IN OUT VARIABLE_POINTER_TRACK *Variable, + IN EFI_TIME *TimeStamp OPTIONAL + ); + + +/** + 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[in] Lock A pointer to the lock data structure to initialize. + @param[in] 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[in] 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[in] Lock A pointer to the lock to release. + +**/ +VOID +ReleaseLockOnlyAtBootTime ( + IN EFI_LOCK *Lock + ); + +/** + 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 + ); + +/** + Get non-volatile maximum variable size. + + @return Non-volatile maximum variable size. + +**/ +UINTN +GetNonVolatileMaxVariableSize ( + 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 + ); + +/** + This code finds variable in storage blocks (Volatile or Non-Volatile). + + Caution: This function may receive untrusted input. + This function may be invoked in SMM mode, and datasize and data are external input. + This function will do basic validation, before parse the data. + + @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. + + @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. + + Caution: This function may receive untrusted input. + This function may be invoked in SMM mode. This function will do basic validation, before parse the data. + + @param[in] VariableNameSize Size of the variable name. + @param[in] VariableName Pointer to variable name. + @param[in] VendorGuid Variable Vendor Guid. + + @return EFI_SUCCESS Find the specified variable. + @return EFI_NOT_FOUND Not found. + +**/ +EFI_STATUS +EFIAPI +VariableServiceGetNextVariableInternal ( + IN OUT UINTN *VariableNameSize, + IN OUT CHAR16 *VariableName, + IN OUT EFI_GUID *VariableGuid + ); + +/** + + This code Finds the Next available variable. + + Caution: This function may receive untrusted input. + This function may be invoked in SMM mode. This function will do basic validation, before parse the data. + + @param[in, out] VariableNameSize Size of the variable name. + @param[in, out] VariableName Pointer to variable name. + @param[in, out] 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). + + Caution: This function may receive untrusted input. + This function may be invoked in SMM mode, and datasize and data are external input. + This function will do basic validation, before parse the data. + This function will parse the authentication carefully to avoid security issues, like + buffer overflow, integer overflow. + This function will check attribute carefully to avoid authentication bypass. + + @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. + + @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. + + Caution: This function may receive untrusted input. + This function may be invoked in SMM mode. This function will do basic validation, before parse the data. + + @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. + + @return EFI_SUCCESS Query successfully. + +**/ +EFI_STATUS +EFIAPI +VariableServiceQueryVariableInfoInternal ( + IN UINT32 Attributes, + OUT UINT64 *MaximumVariableStorageSize, + OUT UINT64 *RemainingVariableStorageSize, + OUT UINT64 *MaximumVariableSize + ); + +/** + This code returns information about the EFI variables. + + Caution: This function may receive untrusted input. + This function may be invoked in SMM mode. This function will do basic validation, before parse the data. + + @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. + + @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 + ); + +/** + Mark a variable that will become read-only after leaving the DXE phase of execution. + + @param[in] This The VARIABLE_LOCK_PROTOCOL instance. + @param[in] VariableName A pointer to the variable name that will be made read-only subsequently. + @param[in] VendorGuid A pointer to the vendor GUID that will be made read-only subsequently. + + @retval EFI_SUCCESS The variable specified by the VariableName and the VendorGuid was marked + as pending to be read-only. + @retval EFI_INVALID_PARAMETER VariableName or VendorGuid is NULL. + Or VariableName is an empty string. + @retval EFI_ACCESS_DENIED EFI_END_OF_DXE_EVENT_GROUP_GUID or EFI_EVENT_GROUP_READY_TO_BOOT has + already been signaled. + @retval EFI_OUT_OF_RESOURCES There is not enough resource to hold the lock request. + +**/ +EFI_STATUS +EFIAPI +VariableLockRequestToLock ( + IN CONST EDKII_VARIABLE_LOCK_PROTOCOL *This, + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid + ); + +/** + Register SetVariable check handler. + + @param[in] Handler Pointer to check handler. + + @retval EFI_SUCCESS The SetVariable check handler was registered successfully. + @retval EFI_INVALID_PARAMETER Handler is NULL. + @retval EFI_ACCESS_DENIED EFI_END_OF_DXE_EVENT_GROUP_GUID or EFI_EVENT_GROUP_READY_TO_BOOT has + already been signaled. + @retval EFI_OUT_OF_RESOURCES There is not enough resource for the SetVariable check handler register request. + @retval EFI_UNSUPPORTED This interface is not implemented. + For example, it is unsupported in VarCheck protocol if both VarCheck and SmmVarCheck protocols are present. + +**/ +EFI_STATUS +EFIAPI +VarCheckRegisterSetVariableCheckHandler ( + IN VAR_CHECK_SET_VARIABLE_CHECK_HANDLER Handler + ); + +/** + Variable property set. + + @param[in] Name Pointer to the variable name. + @param[in] Guid Pointer to the vendor GUID. + @param[in] VariableProperty Pointer to the input variable property. + + @retval EFI_SUCCESS The property of variable specified by the Name and Guid was set successfully. + @retval EFI_INVALID_PARAMETER Name, Guid or VariableProperty is NULL, or Name is an empty string, + or the fields of VariableProperty are not valid. + @retval EFI_ACCESS_DENIED EFI_END_OF_DXE_EVENT_GROUP_GUID or EFI_EVENT_GROUP_READY_TO_BOOT has + already been signaled. + @retval EFI_OUT_OF_RESOURCES There is not enough resource for the variable property set request. + +**/ +EFI_STATUS +EFIAPI +VarCheckVariablePropertySet ( + IN CHAR16 *Name, + IN EFI_GUID *Guid, + IN VAR_CHECK_VARIABLE_PROPERTY *VariableProperty + ); + +/** + Variable property get. + + @param[in] Name Pointer to the variable name. + @param[in] Guid Pointer to the vendor GUID. + @param[out] VariableProperty Pointer to the output variable property. + + @retval EFI_SUCCESS The property of variable specified by the Name and Guid was got successfully. + @retval EFI_INVALID_PARAMETER Name, Guid or VariableProperty is NULL, or Name is an empty string. + @retval EFI_NOT_FOUND The property of variable specified by the Name and Guid was not found. + +**/ +EFI_STATUS +EFIAPI +VarCheckVariablePropertyGet ( + IN CHAR16 *Name, + IN EFI_GUID *Guid, + OUT VAR_CHECK_VARIABLE_PROPERTY *VariableProperty + ); + +/** + Initialize variable quota. + +**/ +VOID +InitializeVariableQuota ( + VOID + ); + +extern VARIABLE_MODULE_GLOBAL *mVariableModuleGlobal; +extern BOOLEAN mForceVolatileVariable; +extern AUTH_VAR_LIB_CONTEXT_OUT mAuthContextOut; + +/** + Finds variable in storage blocks of volatile and non-volatile storage areas. + + This code finds variable in storage blocks of volatile and non-volatile storage areas. + If VariableName is an empty string, then we just return the first + qualified variable without comparing VariableName and VendorGuid. + + @param[in] VariableName Name of the variable to be found. + @param[in] VendorGuid Variable vendor GUID to be found. + @param[out] AuthVariableInfo Pointer to AUTH_VARIABLE_INFO structure for + output of the variable found. + + @retval EFI_INVALID_PARAMETER If VariableName is not an empty string, + while VendorGuid is NULL. + @retval EFI_SUCCESS Variable successfully found. + @retval EFI_NOT_FOUND Variable not found + +**/ +EFI_STATUS +EFIAPI +VariableExLibFindVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + OUT AUTH_VARIABLE_INFO *AuthVariableInfo + ); + +/** + Finds next variable in storage blocks of volatile and non-volatile storage areas. + + This code finds next variable in storage blocks of volatile and non-volatile storage areas. + If VariableName is an empty string, then we just return the first + qualified variable without comparing VariableName and VendorGuid. + + @param[in] VariableName Name of the variable to be found. + @param[in] VendorGuid Variable vendor GUID to be found. + @param[out] AuthVariableInfo Pointer to AUTH_VARIABLE_INFO structure for + output of the next variable. + + @retval EFI_INVALID_PARAMETER If VariableName is not an empty string, + while VendorGuid is NULL. + @retval EFI_SUCCESS Variable successfully found. + @retval EFI_NOT_FOUND Variable not found + +**/ +EFI_STATUS +EFIAPI +VariableExLibFindNextVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + OUT AUTH_VARIABLE_INFO *AuthVariableInfo + ); + +/** + Update the variable region with Variable information. + + @param[in] AuthVariableInfo Pointer AUTH_VARIABLE_INFO structure for + input of the variable. + + @retval EFI_SUCCESS The update operation is success. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_WRITE_PROTECTED Variable is write-protected. + @retval EFI_OUT_OF_RESOURCES There is not enough resource. + +**/ +EFI_STATUS +EFIAPI +VariableExLibUpdateVariable ( + IN AUTH_VARIABLE_INFO *AuthVariableInfo + ); + +/** + Get scratch buffer. + + @param[in, out] ScratchBufferSize Scratch buffer size. If input size is greater than + the maximum supported buffer size, this value contains + the maximum supported buffer size as output. + @param[out] ScratchBuffer Pointer to scratch buffer address. + + @retval EFI_SUCCESS Get scratch buffer successfully. + @retval EFI_UNSUPPORTED If input size is greater than the maximum supported buffer size. + +**/ +EFI_STATUS +EFIAPI +VariableExLibGetScratchBuffer ( + IN OUT UINTN *ScratchBufferSize, + OUT VOID **ScratchBuffer + ); + +/** + This function is to check if the remaining variable space is enough to set + all Variables from argument list successfully. The purpose of the check + is to keep the consistency of the Variables to be in variable storage. + + Note: Variables are assumed to be in same storage. + The set sequence of Variables will be same with the sequence of VariableEntry from argument list, + so follow the argument sequence to check the Variables. + + @param[in] Attributes Variable attributes for Variable entries. + @param ... The variable argument list with type VARIABLE_ENTRY_CONSISTENCY *. + A NULL terminates the list. The VariableSize of + VARIABLE_ENTRY_CONSISTENCY is the variable data size as input. + It will be changed to variable total size as output. + + @retval TRUE Have enough variable space to set the Variables successfully. + @retval FALSE No enough variable space to set the Variables successfully. + +**/ +BOOLEAN +EFIAPI +VariableExLibCheckRemainingSpaceForConsistency ( + IN UINT32 Attributes, + ... + ); + +/** + Return TRUE if at OS runtime. + + @retval TRUE If at OS runtime. + @retval FALSE If at boot time. + +**/ +BOOLEAN +EFIAPI +VariableExLibAtRuntime ( + VOID + ); + +/** + Non-Volatile variable write service is ready event handler. + +**/ +VOID +EFIAPI +InstallVariableWriteReady ( + VOID + ); + +#endif + diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c new file mode 100644 index 0000000000..6400bae9ca --- /dev/null +++ b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c @@ -0,0 +1,436 @@ +/** @file + Implement all four UEFI Runtime Variable services for the nonvolatile + and volatile storage space and install variable architecture protocol. + + Copyright (c) 2006 - 2016, 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; +extern VARIABLE_INFO_ENTRY *gVariableInfo; +EFI_HANDLE mHandle = NULL; +EFI_EVENT mVirtualAddressChangeEvent = NULL; +extern BOOLEAN mEndOfDxe; +VOID ***mVarCheckAddressPointer = NULL; +UINTN mVarCheckAddressPointerCount = 0; +EDKII_VARIABLE_LOCK_PROTOCOL mVariableLock = { VariableLockRequestToLock }; +EDKII_VAR_CHECK_PROTOCOL mVarCheck = { VarCheckRegisterSetVariableCheckHandler, + VarCheckVariablePropertySet, + VarCheckVariablePropertyGet }; + +/** + Return 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[in] Lock A pointer to the lock data structure to initialize. + @param[in] 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[in] 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[in] Lock A pointer to the lock to release. + +**/ +VOID +ReleaseLockOnlyAtBootTime ( + IN EFI_LOCK *Lock + ) +{ + if (!AtRuntime ()) { + EfiReleaseLock (Lock); + } +} + + +/** + 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 +VariableClassAddressChangeEvent ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + UINTN Index; + VARIABLE_STORAGE_PROTOCOL *VariableStorageProtocol; + + // + // Setup the callback to determine when to enable variable writes + // + for ( Index = 0; + Index < mVariableModuleGlobal->VariableGlobal.VariableStoresCount; + Index++) { + VariableStorageProtocol = mVariableModuleGlobal->VariableGlobal.VariableStores[Index]; + EfiConvertPointer (0x0, (VOID **) &VariableStorageProtocol->GetId); + EfiConvertPointer (0x0, (VOID **) &VariableStorageProtocol->GetVariable); + EfiConvertPointer (0x0, (VOID **) &VariableStorageProtocol->GetAuthenticatedVariable); + EfiConvertPointer (0x0, (VOID **) &VariableStorageProtocol->GetNextVariableName); + EfiConvertPointer (0x0, (VOID **) &VariableStorageProtocol->GetStorageUsage); + EfiConvertPointer (0x0, (VOID **) &VariableStorageProtocol->GetAuthenticatedSupport); + EfiConvertPointer (0x0, (VOID **) &VariableStorageProtocol->SetVariable); + EfiConvertPointer (0x0, (VOID **) &VariableStorageProtocol->WriteServiceIsReady); + EfiConvertPointer (0x0, (VOID **) &VariableStorageProtocol->RegisterWriteServiceReadyCallback); + EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->VariableGlobal.VariableStores[Index]); + } + EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->VariableGlobal.VariableStores); + EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->PlatformLangCodes); + EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->LangCodes); + EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->PlatformLang); + EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->VariableGlobal.VolatileVariableBase); + EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->VariableGlobal.HobVariableBase); + EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal); + EfiConvertPointer (0x0, (VOID **) &mNvVariableCache); + + if (mAuthContextOut.AddressPointer != NULL) { + for (Index = 0; Index < mAuthContextOut.AddressPointerCount; Index++) { + EfiConvertPointer (0x0, (VOID **) mAuthContextOut.AddressPointer[Index]); + } + } + + if (mVarCheckAddressPointer != NULL) { + for (Index = 0; Index < mVarCheckAddressPointerCount; Index++) { + EfiConvertPointer (0x0, (VOID **) mVarCheckAddressPointer[Index]); + } + } +} + + +/** + 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 + ) +{ + if (!mEndOfDxe) { + // + // Set the End Of DXE bit in case the EFI_END_OF_DXE_EVENT_GROUP_GUID event is not signaled. + // + mEndOfDxe = TRUE; + mVarCheckAddressPointer = VarCheckLibInitializeAtEndOfDxe (&mVarCheckAddressPointerCount); + // + // The initialization for variable quota. + // + InitializeVariableQuota (); + } + ReclaimForOS (); + if (FeaturePcdGet (PcdVariableCollectStatistics)) { + if (mVariableModuleGlobal->VariableGlobal.AuthFormat) { + gBS->InstallConfigurationTable (&gEfiAuthenticatedVariableGuid, gVariableInfo); + } else { + gBS->InstallConfigurationTable (&gEfiVariableGuid, gVariableInfo); + } + } + + gBS->CloseEvent (Event); +} + + +/** + Notification function of EFI_END_OF_DXE_EVENT_GROUP_GUID event group. + + This is a notification function registered on EFI_END_OF_DXE_EVENT_GROUP_GUID event group. + + @param Event Event whose notification function is being invoked. + @param Context Pointer to the notification function's context. + +**/ +VOID +EFIAPI +OnEndOfDxe ( + EFI_EVENT Event, + VOID *Context + ) +{ + DEBUG ((EFI_D_INFO, "[Variable]END_OF_DXE is signaled\n")); + mEndOfDxe = TRUE; + mVarCheckAddressPointer = VarCheckLibInitializeAtEndOfDxe (&mVarCheckAddressPointerCount); + // + // The initialization for variable quota. + // + InitializeVariableQuota (); + if (PcdGetBool (PcdReclaimVariableSpaceAtEndOfDxe)) { + ReclaimForOS (); + } + + gBS->CloseEvent (Event); +} + + +/** + Non-Volatile variable write service is ready event handler. + +**/ +VOID +EFIAPI +InstallVariableWriteReady ( + VOID + ) +{ + EFI_STATUS Status; + + DEBUG ((DEBUG_INFO, "Installing EfiVariableWriteArchProtocolGuid\n")); + // + // Install the Variable Write Architectural protocol. + // + 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 available. 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; + EFI_EVENT EndOfDxeEvent; + EFI_HANDLE *Handles; + UINTN Index; + + // + // Allocate runtime memory for variable driver global structure. + // + mVariableModuleGlobal = AllocateRuntimeZeroPool (sizeof (VARIABLE_MODULE_GLOBAL)); + if (mVariableModuleGlobal == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Get the VARIABLE_STORAGE_PROTOCOL instances + // + mVariableModuleGlobal->VariableGlobal.VariableStoresCount = 0; + Handles = NULL; + + // + // Determine if Force Volatile Mode is enabled + // + mForceVolatileVariable = PcdGetBool (PcdForceVolatileVariable); + + if (!mForceVolatileVariable) { + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gVariableStorageProtocolGuid, + NULL, + &mVariableModuleGlobal->VariableGlobal.VariableStoresCount, + &Handles + ); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR(Status)) { + return Status; + } + mVariableModuleGlobal->VariableGlobal.VariableStores = + AllocateRuntimeZeroPool ( + sizeof (VARIABLE_STORAGE_PROTOCOL *) * + mVariableModuleGlobal->VariableGlobal.VariableStoresCount + ); + ASSERT (mVariableModuleGlobal->VariableGlobal.VariableStores != NULL); + if (mVariableModuleGlobal->VariableGlobal.VariableStores == NULL) { + FreePool (Handles); + return EFI_OUT_OF_RESOURCES; + } + for ( Index = 0; + Index < mVariableModuleGlobal->VariableGlobal.VariableStoresCount; + Index++) { + Status = gBS->OpenProtocol ( + Handles[Index], + &gVariableStorageProtocolGuid, + (VOID **) &mVariableModuleGlobal->VariableGlobal.VariableStores[Index], + gImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + FreePool (Handles); + return Status; + } + } + FreePool (Handles); + } + + // + // Initialize variable services + // + Status = VariableCommonInitialize (); + ASSERT_EFI_ERROR (Status); + + Status = gBS->InstallMultipleProtocolInterfaces ( + &mHandle, + &gEdkiiVariableLockProtocolGuid, + &mVariableLock, + NULL + ); + ASSERT_EFI_ERROR (Status); + + Status = gBS->InstallMultipleProtocolInterfaces ( + &mHandle, + &gEdkiiVarCheckProtocolGuid, + &mVarCheck, + NULL + ); + 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); + + 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 + ); + ASSERT_EFI_ERROR (Status); + + // + // Register the event handling function to set the End Of DXE flag. + // + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + OnEndOfDxe, + NULL, + &gEfiEndOfDxeEventGroupGuid, + &EndOfDxeEvent + ); + ASSERT_EFI_ERROR (Status); + + return EFI_SUCCESS; +} + diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableExLib.c b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableExLib.c new file mode 100644 index 0000000000..00f9eb92d2 --- /dev/null +++ b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableExLib.c @@ -0,0 +1,291 @@ +/** @file + Provides variable driver extended services. + + Copyright (c) 2015 - 2016, 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" + + +/** + Finds variable in storage blocks of volatile and non-volatile storage areas. + + This code finds variable in storage blocks of volatile and non-volatile storage areas. + If VariableName is an empty string, then we just return the first + qualified variable without comparing VariableName and VendorGuid. + + @param[in] VariableName Name of the variable to be found. + @param[in] VendorGuid Variable vendor GUID to be found. + @param[out] AuthVariableInfo Pointer to AUTH_VARIABLE_INFO structure for + output of the variable found. + + @retval EFI_INVALID_PARAMETER If VariableName is not an empty string, + while VendorGuid is NULL. + @retval EFI_SUCCESS Variable successfully found. + @retval EFI_NOT_FOUND Variable not found. + +**/ +EFI_STATUS +EFIAPI +VariableExLibFindVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + OUT AUTH_VARIABLE_INFO *AuthVariableInfo + ) +{ + EFI_STATUS Status; + VARIABLE_POINTER_TRACK Variable; + AUTHENTICATED_VARIABLE_HEADER *AuthVariable; + + Status = FindVariable ( + VariableName, + VendorGuid, + &Variable, + &mVariableModuleGlobal->VariableGlobal, + FALSE + ); + if (EFI_ERROR (Status)) { + AuthVariableInfo->Data = NULL; + AuthVariableInfo->DataSize = 0; + AuthVariableInfo->Attributes = 0; + AuthVariableInfo->PubKeyIndex = 0; + AuthVariableInfo->MonotonicCount = 0; + AuthVariableInfo->TimeStamp = NULL; + return Status; + } + + AuthVariableInfo->DataSize = DataSizeOfVariable (Variable.CurrPtr); + AuthVariableInfo->Data = GetVariableDataPtr (Variable.CurrPtr); + AuthVariableInfo->Attributes = Variable.CurrPtr->Attributes; + if (mVariableModuleGlobal->VariableGlobal.AuthFormat) { + AuthVariable = (AUTHENTICATED_VARIABLE_HEADER *) Variable.CurrPtr; + AuthVariableInfo->PubKeyIndex = AuthVariable->PubKeyIndex; + AuthVariableInfo->MonotonicCount = ReadUnaligned64 (&(AuthVariable->MonotonicCount)); + AuthVariableInfo->TimeStamp = &AuthVariable->TimeStamp; + } + + return EFI_SUCCESS; +} + + +/** + Finds next variable in storage blocks of volatile and non-volatile storage areas. + + This code finds next variable in storage blocks of volatile and non-volatile storage areas. + If VariableName is an empty string, then we just return the first + qualified variable without comparing VariableName and VendorGuid. + + @param[in] VariableName Name of the variable to be found. + @param[in] VendorGuid Variable vendor GUID to be found. + @param[out] AuthVariableInfo Pointer to AUTH_VARIABLE_INFO structure for + output of the next variable. + + @retval EFI_INVALID_PARAMETER If VariableName is not an empty string, + while VendorGuid is NULL. + @retval EFI_SUCCESS Variable successfully found. + @retval EFI_NOT_FOUND Variable not found + +**/ +EFI_STATUS +EFIAPI +VariableExLibFindNextVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + OUT AUTH_VARIABLE_INFO *AuthVariableInfo + ) +{ + CHAR16 TempName[2048]; + EFI_GUID TempGuid; + VARIABLE_POINTER_TRACK Variable; + EFI_STATUS Status; + UINTN TempNameSize; + VARIABLE_HEADER *VariablePtr; + AUTHENTICATED_VARIABLE_HEADER *AuthVariablePtr; + + ASSERT (StrLen (VariableName) < 2048); + if (StrLen (VariableName) >= 2048) { + return EFI_OUT_OF_RESOURCES; + } + TempNameSize = 2048 * sizeof (CHAR16); + StrCpyS ((CHAR16 *) TempName, TempNameSize, VariableName); + CopyMem (&TempGuid, VendorGuid, sizeof (EFI_GUID)); + Status = VariableServiceGetNextVariableInternal ( + &TempNameSize, + &TempName[0], + &TempGuid + ); + if (EFI_ERROR (Status)) { + ASSERT (Status != EFI_BUFFER_TOO_SMALL); + AuthVariableInfo->VariableName = NULL; + AuthVariableInfo->VendorGuid = NULL; + AuthVariableInfo->Data = NULL; + AuthVariableInfo->DataSize = 0; + AuthVariableInfo->Attributes = 0; + AuthVariableInfo->PubKeyIndex = 0; + AuthVariableInfo->MonotonicCount = 0; + AuthVariableInfo->TimeStamp = NULL; + return Status; + } else { + Status = FindVariable (&TempName[0], &TempGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, FALSE); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + AuthVariableInfo->VariableName = NULL; + AuthVariableInfo->VendorGuid = NULL; + AuthVariableInfo->Data = NULL; + AuthVariableInfo->DataSize = 0; + AuthVariableInfo->Attributes = 0; + AuthVariableInfo->PubKeyIndex = 0; + AuthVariableInfo->MonotonicCount = 0; + AuthVariableInfo->TimeStamp = NULL; + return Status; + } + VariablePtr = Variable.CurrPtr; + } + + AuthVariableInfo->VariableName = GetVariableNamePtr (VariablePtr); + AuthVariableInfo->VendorGuid = GetVendorGuidPtr (VariablePtr); + AuthVariableInfo->DataSize = DataSizeOfVariable (VariablePtr); + AuthVariableInfo->Data = GetVariableDataPtr (VariablePtr); + AuthVariableInfo->Attributes = VariablePtr->Attributes; + if (mVariableModuleGlobal->VariableGlobal.AuthFormat) { + AuthVariablePtr = (AUTHENTICATED_VARIABLE_HEADER *) VariablePtr; + AuthVariableInfo->PubKeyIndex = AuthVariablePtr->PubKeyIndex; + AuthVariableInfo->MonotonicCount = ReadUnaligned64 (&(AuthVariablePtr->MonotonicCount)); + AuthVariableInfo->TimeStamp = &AuthVariablePtr->TimeStamp; + } + + return EFI_SUCCESS; +} + + +/** + Update the variable region with Variable information. + + @param[in] AuthVariableInfo Pointer AUTH_VARIABLE_INFO structure for + input of the variable. + + @retval EFI_SUCCESS The update operation is success. + @retval EFI_INVALID_PARAMETER Invalid parameter. + @retval EFI_WRITE_PROTECTED Variable is write-protected. + @retval EFI_OUT_OF_RESOURCES There is not enough resource. + +**/ +EFI_STATUS +EFIAPI +VariableExLibUpdateVariable ( + IN AUTH_VARIABLE_INFO *AuthVariableInfo + ) +{ + VARIABLE_POINTER_TRACK Variable; + + FindVariable (AuthVariableInfo->VariableName, AuthVariableInfo->VendorGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, FALSE); + return UpdateVariable ( + AuthVariableInfo->VariableName, + AuthVariableInfo->VendorGuid, + AuthVariableInfo->Data, + AuthVariableInfo->DataSize, + AuthVariableInfo->Attributes, + AuthVariableInfo->PubKeyIndex, + AuthVariableInfo->MonotonicCount, + &Variable, + AuthVariableInfo->TimeStamp + ); +} + + +/** + Get scratch buffer. + + @param[in, out] ScratchBufferSize Scratch buffer size. If input size is greater than + the maximum supported buffer size, this value contains + the maximum supported buffer size as output. + @param[out] ScratchBuffer Pointer to scratch buffer address. + + @retval EFI_SUCCESS Get scratch buffer successfully. + @retval EFI_UNSUPPORTED If input size is greater than the maximum supported buffer size. + +**/ +EFI_STATUS +EFIAPI +VariableExLibGetScratchBuffer ( + IN OUT UINTN *ScratchBufferSize, + OUT VOID **ScratchBuffer + ) +{ + UINTN MaxBufferSize; + + MaxBufferSize = mVariableModuleGlobal->ScratchBufferSize; + if (*ScratchBufferSize > MaxBufferSize) { + *ScratchBufferSize = MaxBufferSize; + return EFI_UNSUPPORTED; + } + + *ScratchBuffer = GetEndPointer ((VARIABLE_STORE_HEADER *) ((UINTN) mVariableModuleGlobal->VariableGlobal.VolatileVariableBase)); + + return EFI_SUCCESS; +} + + +/** + This function is to check if the remaining variable space is enough to set + all Variables from argument list successfully. The purpose of the check + is to keep the consistency of the Variables to be in variable storage. + + Note: Variables are assumed to be in same storage. + The set sequence of Variables will be same with the sequence of VariableEntry from argument list, + so follow the argument sequence to check the Variables. + + @param[in] Attributes Variable attributes for Variable entries. + @param ... The variable argument list with type VARIABLE_ENTRY_CONSISTENCY *. + A NULL terminates the list. The VariableSize of + VARIABLE_ENTRY_CONSISTENCY is the variable data size as input. + It will be changed to variable total size as output. + + @retval TRUE Have enough variable space to set the Variables successfully. + @retval FALSE No enough variable space to set the Variables successfully. + +**/ +BOOLEAN +EFIAPI +VariableExLibCheckRemainingSpaceForConsistency ( + IN UINT32 Attributes, + ... + ) +{ + VA_LIST Marker; + BOOLEAN Return; + + VA_START (Marker, Attributes); + + Return = CheckRemainingSpaceForConsistencyInternal (Attributes, Marker); + + VA_END (Marker); + + return Return; +} + +/** + Return TRUE if at OS runtime. + + @retval TRUE If at OS runtime. + @retval FALSE If at boot time. + +**/ +BOOLEAN +EFIAPI +VariableExLibAtRuntime ( + VOID + ) +{ + return AtRuntime (); +} + diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf new file mode 100644 index 0000000000..35dff53114 --- /dev/null +++ b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf @@ -0,0 +1,129 @@ +## @file +# Provides variable service. +# +# This module installs variable arch protocol and variable write arch protocol to provide +# variable services: SetVariable, GetVariable, GetNextVariableName and QueryVariableInfo. +# +# Caution: This module requires additional review when modified. +# This driver will have external input - variable data. +# This external input must be validated carefully to avoid security issues such as +# buffer overflow or integer overflow. +# +# Copyright (c) 2006 - 2016, 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 = VariableRuntimeDxe + MODULE_UNI_FILE = VariableRuntimeDxe.uni + FILE_GUID = CBD2E4D5-7068-4FF5-B462-9822B4AD8D60 + MODULE_TYPE = DXE_RUNTIME_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = VariableServiceInitialize + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# +# VIRTUAL_ADDRESS_MAP_CALLBACK = VariableClassAddressChangeEvent +# + +[Sources] + Variable.c + VariableDxe.c + Variable.h + Measurement.c + TcgMorLockDxe.c + VarCheck.c + VariableExLib.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + BroxtonSiPkg/BroxtonSiPkg.dec + +[LibraryClasses] + MemoryAllocationLib + BaseLib + SynchronizationLib + UefiLib + UefiBootServicesTableLib + BaseMemoryLib + DebugLib + UefiRuntimeLib + DxeServicesTableLib + UefiDriverEntryPoint + PcdLib + HobLib + TpmMeasurementLib + AuthVariableLib + VarCheckLib + VariableStorageSelectorLib + +[Protocols] + gVariableStorageProtocolGuid ## CONSUMES + ## CONSUMES + ## NOTIFY + gEfiVariableWriteArchProtocolGuid ## PRODUCES + gEfiVariableArchProtocolGuid ## PRODUCES + gEdkiiVariableLockProtocolGuid ## PRODUCES + gEdkiiVarCheckProtocolGuid ## PRODUCES + +[Guids] + ## PRODUCES ## GUID # Signature of Variable store header + ## CONSUMES ## GUID # Signature of Variable store header + ## SOMETIMES_CONSUMES ## HOB + ## SOMETIMES_PRODUCES ## SystemTable + gEfiAuthenticatedVariableGuid + + ## PRODUCES ## GUID # Signature of Variable store header + ## CONSUMES ## GUID # Signature of Variable store header + ## SOMETIMES_CONSUMES ## HOB + ## SOMETIMES_PRODUCES ## SystemTable + gEfiVariableGuid + + ## SOMETIMES_CONSUMES ## Variable:L"PlatformLang" + ## SOMETIMES_PRODUCES ## Variable:L"PlatformLang" + ## SOMETIMES_CONSUMES ## Variable:L"Lang" + ## SOMETIMES_PRODUCES ## Variable:L"Lang" + gEfiGlobalVariableGuid + gEfiMemoryOverwriteControlDataGuid ## CONSUMES ## Variable:L"MemoryOverwriteRequestControl" + gEfiMemoryOverwriteRequestControlLockGuid ## PRODUCES ## Variable:L"MemoryOverwriteRequestControlLock" + gEfiEventVirtualAddressChangeGuid ## CONSUMES ## Event + gEfiEndOfDxeEventGroupGuid ## CONSUMES ## Event + gEdkiiVarErrorFlagGuid ## CONSUMES ## GUID + + ## SOMETIMES_CONSUMES ## Variable:L"DB" + ## SOMETIMES_CONSUMES ## Variable:L"DBX" + gEfiImageSecurityDatabaseGuid + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdMaxVariableSize ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdMaxAuthVariableSize ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdMaxHardwareErrorVariableSize ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdVariableStoreSize ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdHwErrStorageSize ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdMaxUserNvVariableSpaceSize ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdBoottimeReservedNvVariableSpaceSize ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdReclaimVariableSpaceAtEndOfDxe ## CONSUMES + gSiPkgTokenSpaceGuid.PcdForceVolatileVariable ## CONSUMES + +[FeaturePcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdVariableCollectStatistics ## CONSUMES # Variable statistic information + gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultLangDeprecate ## CONSUMES # Auto update PlatformLang/Lang + +[Depex] + gVariableStorageProtocolGuid + +[UserExtensions.TianoCore."ExtraFiles"] + VariableRuntimeDxeExtra.uni diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.uni b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.uni new file mode 100644 index 0000000000..1c3e7f07e4 Binary files /dev/null and b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.uni differ diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxeExtra.uni b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxeExtra.uni new file mode 100644 index 0000000000..a3a76dacb5 Binary files /dev/null and b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxeExtra.uni differ diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c new file mode 100644 index 0000000000..78439fc5a7 --- /dev/null +++ b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.c @@ -0,0 +1,911 @@ +/** @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. + + Caution: This module requires additional review when modified. + This driver will have external input - variable data and communicate buffer in SMM mode. + This external input must be validated carefully to avoid security issue like + buffer overflow, integer overflow. + + SmmVariableHandler() will receive untrusted input and do basic validation. + + Each sub function VariableServiceGetVariable(), VariableServiceGetNextVariableName(), + VariableServiceSetVariable(), VariableServiceQueryVariableInfo(), ReclaimForOS(), + SmmVariableGetStatistics() should also do validation based on its own knowledge. + + Copyright (c) 2010 - 2016, 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 "Variable.h" + +extern VARIABLE_INFO_ENTRY *gVariableInfo; +EFI_HANDLE mSmmVariableHandle = NULL; +EFI_HANDLE mVariableHandle = NULL; +BOOLEAN mAtRuntime = FALSE; +UINT8 *mVariableBufferPayload = NULL; +UINTN mVariableBufferPayloadSize; +extern BOOLEAN mEndOfDxe; +extern VAR_CHECK_REQUEST_SOURCE mRequestSource; + + +/** + SecureBoot Hook for SetVariable. + + @param[in] VariableName Name of Variable to be found. + @param[in] VendorGuid Variable vendor GUID. + +**/ +VOID +EFIAPI +SecureBootHook ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid + ) +{ + return ; +} + + +/** + 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. + + @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 +SmmVariableSetVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN UINT32 Attributes, + IN UINTN DataSize, + IN VOID *Data + ) +{ + EFI_STATUS Status; + + // + // Disable write protection when the calling SetVariable() through EFI_SMM_VARIABLE_PROTOCOL. + // + mRequestSource = VarCheckFromTrusted; + Status = VariableServiceSetVariable ( + VariableName, + VendorGuid, + Attributes, + DataSize, + Data + ); + mRequestSource = VarCheckFromUntrusted; + return Status; +} + +EFI_SMM_VARIABLE_PROTOCOL gSmmVariable = { + VariableServiceGetVariable, + VariableServiceGetNextVariableName, + SmmVariableSetVariable, + VariableServiceQueryVariableInfo +}; + +EDKII_SMM_VAR_CHECK_PROTOCOL mSmmVarCheck = { VarCheckRegisterSetVariableCheckHandler, + VarCheckVariablePropertySet, + VarCheckVariablePropertyGet }; + + +/** + Return 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[in] Lock A pointer to the lock data structure to initialize. + @param[in] 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[in] 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[in] Lock A pointer to the lock to release. + +**/ +VOID +ReleaseLockOnlyAtBootTime ( + IN EFI_LOCK *Lock + ) +{ + +} + + +/** + Get the variable statistics information from the information buffer pointed by gVariableInfo. + + Caution: This function may be invoked at SMM runtime. + InfoEntry and InfoSize are external input. Care must be taken to make sure not security issue at runtime. + + @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. + @retval EFI_INVALID_PARAMETER Input parameter is invalid. + +**/ +EFI_STATUS +SmmVariableGetStatistics ( + IN OUT VARIABLE_INFO_ENTRY *InfoEntry, + IN OUT UINTN *InfoSize + ) +{ + VARIABLE_INFO_ENTRY *VariableInfo; + UINTN NameLength; + UINTN StatisticsInfoSize; + CHAR16 *InfoName; + EFI_GUID VendorGuid; + + if (InfoEntry == NULL) { + return EFI_INVALID_PARAMETER; + } + + VariableInfo = gVariableInfo; + if (VariableInfo == NULL) { + return EFI_UNSUPPORTED; + } + + StatisticsInfoSize = sizeof (VARIABLE_INFO_ENTRY) + StrSize (VariableInfo->Name); + if (*InfoSize < StatisticsInfoSize) { + *InfoSize = StatisticsInfoSize; + return EFI_BUFFER_TOO_SMALL; + } + InfoName = (CHAR16 *)(InfoEntry + 1); + + CopyGuid (&VendorGuid, &InfoEntry->VendorGuid); + + if (CompareGuid (&VendorGuid, &gZeroGuid)) { + // + // Return the first variable info + // + CopyMem (InfoEntry, VariableInfo, sizeof (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, &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 (VARIABLE_INFO_ENTRY) + StrSize (VariableInfo->Name); + if (*InfoSize < StatisticsInfoSize) { + *InfoSize = StatisticsInfoSize; + return EFI_BUFFER_TOO_SMALL; + } + + CopyMem (InfoEntry, VariableInfo, sizeof (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. + + Caution: This function may receive untrusted input. + This variable data and communicate buffer are external input, so this function will do basic validation. + Each sub function VariableServiceGetVariable(), VariableServiceGetNextVariableName(), + VariableServiceSetVariable(), VariableServiceQueryVariableInfo(), ReclaimForOS(), + SmmVariableGetStatistics() should also do validation based on its own knowledge. + + @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_GET_PAYLOAD_SIZE *GetPayloadSize; + VARIABLE_INFO_ENTRY *VariableInfo; + SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE *VariableToLock; + SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY *CommVariableProperty; + UINTN InfoSize; + UINTN NameBufferSize; + UINTN CommBufferPayloadSize; + UINTN TempCommBufferSize; + + // + // If input is invalid, stop processing this SMI + // + if (CommBuffer == NULL || CommBufferSize == NULL) { + return EFI_SUCCESS; + } + + TempCommBufferSize = *CommBufferSize; + + if (TempCommBufferSize < SMM_VARIABLE_COMMUNICATE_HEADER_SIZE) { + DEBUG ((EFI_D_ERROR, "SmmVariableHandler: SMM communication buffer size invalid!\n")); + return EFI_SUCCESS; + } + CommBufferPayloadSize = TempCommBufferSize - SMM_VARIABLE_COMMUNICATE_HEADER_SIZE; + if (CommBufferPayloadSize > mVariableBufferPayloadSize) { + DEBUG ((EFI_D_ERROR, "SmmVariableHandler: SMM communication buffer payload size invalid!\n")); + return EFI_SUCCESS; + } + + if (!SmmIsBufferOutsideSmmValid ((UINTN)CommBuffer, TempCommBufferSize)) { + DEBUG ((EFI_D_ERROR, "SmmVariableHandler: SMM communication buffer in SMRAM or overflow!\n")); + return EFI_SUCCESS; + } + + SmmVariableFunctionHeader = (SMM_VARIABLE_COMMUNICATE_HEADER *) CommBuffer; + switch (SmmVariableFunctionHeader->Function) { + case SMM_VARIABLE_FUNCTION_GET_VARIABLE: + if (CommBufferPayloadSize < OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) { + DEBUG ((EFI_D_ERROR, "GetVariable: SMM communication buffer size invalid!\n")); + return EFI_SUCCESS; + } + // + // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload. + // + CopyMem (mVariableBufferPayload, SmmVariableFunctionHeader->Data, CommBufferPayloadSize); + SmmVariableHeader = (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *) mVariableBufferPayload; + if (((UINTN)(~0) - SmmVariableHeader->DataSize < OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) || + ((UINTN)(~0) - SmmVariableHeader->NameSize < OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + SmmVariableHeader->DataSize)) { + // + // Prevent InfoSize overflow happen + // + Status = EFI_ACCESS_DENIED; + goto EXIT; + } + InfoSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + + SmmVariableHeader->DataSize + SmmVariableHeader->NameSize; + + // + // SMRAM range check already covered before + // + if (InfoSize > CommBufferPayloadSize) { + DEBUG ((EFI_D_ERROR, "GetVariable: Data size exceed communication buffer size limit!\n")); + Status = EFI_ACCESS_DENIED; + goto EXIT; + } + + if (SmmVariableHeader->NameSize < sizeof (CHAR16) || SmmVariableHeader->Name[SmmVariableHeader->NameSize / sizeof (CHAR16) - 1] != L'\0') { + // + // Make sure VariableName is A Null-terminated string. + // + Status = EFI_ACCESS_DENIED; + goto EXIT; + } + + Status = VariableServiceGetVariable ( + SmmVariableHeader->Name, + &SmmVariableHeader->Guid, + &SmmVariableHeader->Attributes, + &SmmVariableHeader->DataSize, + (UINT8 *)SmmVariableHeader->Name + SmmVariableHeader->NameSize + ); + CopyMem (SmmVariableFunctionHeader->Data, mVariableBufferPayload, CommBufferPayloadSize); + break; + + case SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME: + if (CommBufferPayloadSize < OFFSET_OF (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name)) { + DEBUG ((EFI_D_ERROR, "GetNextVariableName: SMM communication buffer size invalid!\n")); + return EFI_SUCCESS; + } + // + // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload. + // + CopyMem (mVariableBufferPayload, SmmVariableFunctionHeader->Data, CommBufferPayloadSize); + GetNextVariableName = (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME *) mVariableBufferPayload; + if ((UINTN) (~0) - GetNextVariableName->NameSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name)) { + // + // Prevent InfoSize overflow happen + // + Status = EFI_ACCESS_DENIED; + goto EXIT; + } + InfoSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name) + GetNextVariableName->NameSize; + + // + // SMRAM range check already covered before + // + if (InfoSize > CommBufferPayloadSize) { + DEBUG ((EFI_D_ERROR, "GetNextVariableName: Data size exceed communication buffer size limit!\n")); + Status = EFI_ACCESS_DENIED; + goto EXIT; + } + + NameBufferSize = CommBufferPayloadSize - OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name); + if (NameBufferSize < sizeof (CHAR16) || GetNextVariableName->Name[NameBufferSize / sizeof (CHAR16) - 1] != L'\0') { + // + // Make sure input VariableName is A Null-terminated string. + // + Status = EFI_ACCESS_DENIED; + goto EXIT; + } + + Status = VariableServiceGetNextVariableName ( + &GetNextVariableName->NameSize, + GetNextVariableName->Name, + &GetNextVariableName->Guid + ); + CopyMem (SmmVariableFunctionHeader->Data, mVariableBufferPayload, CommBufferPayloadSize); + break; + + case SMM_VARIABLE_FUNCTION_SET_VARIABLE: + if (CommBufferPayloadSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) { + DEBUG ((EFI_D_ERROR, "SetVariable: SMM communication buffer size invalid!\n")); + return EFI_SUCCESS; + } + // + // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload. + // + CopyMem (mVariableBufferPayload, SmmVariableFunctionHeader->Data, CommBufferPayloadSize); + SmmVariableHeader = (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *) mVariableBufferPayload; + if (((UINTN) (~0) - SmmVariableHeader->DataSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) || + ((UINTN) (~0) - SmmVariableHeader->NameSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + SmmVariableHeader->DataSize)) { + // + // Prevent InfoSize overflow happen + // + Status = EFI_ACCESS_DENIED; + goto EXIT; + } + InfoSize = OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + + SmmVariableHeader->DataSize + SmmVariableHeader->NameSize; + + // + // SMRAM range check already covered before + // Data buffer should not contain SMM range + // + if (InfoSize > CommBufferPayloadSize) { + DEBUG ((EFI_D_ERROR, "SetVariable: Data size exceed communication buffer size limit!\n")); + Status = EFI_ACCESS_DENIED; + goto EXIT; + } + + if (SmmVariableHeader->NameSize < sizeof (CHAR16) || SmmVariableHeader->Name[SmmVariableHeader->NameSize / sizeof (CHAR16) - 1] != L'\0') { + // + // Make sure VariableName is A Null-terminated string. + // + Status = EFI_ACCESS_DENIED; + goto EXIT; + } + + Status = VariableServiceSetVariable ( + SmmVariableHeader->Name, + &SmmVariableHeader->Guid, + SmmVariableHeader->Attributes, + SmmVariableHeader->DataSize, + (UINT8 *) SmmVariableHeader->Name + SmmVariableHeader->NameSize + ); + break; + + case SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO: + if (CommBufferPayloadSize < sizeof (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO)) { + DEBUG ((EFI_D_ERROR, "QueryVariableInfo: SMM communication buffer size invalid!\n")); + return EFI_SUCCESS; + } + QueryVariableInfo = (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *) SmmVariableFunctionHeader->Data; + + Status = VariableServiceQueryVariableInfo ( + QueryVariableInfo->Attributes, + &QueryVariableInfo->MaximumVariableStorageSize, + &QueryVariableInfo->RemainingVariableStorageSize, + &QueryVariableInfo->MaximumVariableSize + ); + break; + + case SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE: + if (CommBufferPayloadSize < sizeof (SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE)) { + DEBUG ((EFI_D_ERROR, "GetPayloadSize: SMM communication buffer size invalid!\n")); + return EFI_SUCCESS; + } + GetPayloadSize = (SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE *) SmmVariableFunctionHeader->Data; + GetPayloadSize->VariablePayloadSize = mVariableBufferPayloadSize; + Status = EFI_SUCCESS; + break; + + case SMM_VARIABLE_FUNCTION_READY_TO_BOOT: + if (AtRuntime()) { + Status = EFI_UNSUPPORTED; + break; + } + if (!mEndOfDxe) { + mEndOfDxe = TRUE; + VarCheckLibInitializeAtEndOfDxe (NULL); + // + // The initialization for variable quota. + // + InitializeVariableQuota (); + } + 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 = (VARIABLE_INFO_ENTRY *) SmmVariableFunctionHeader->Data; + InfoSize = TempCommBufferSize - SMM_VARIABLE_COMMUNICATE_HEADER_SIZE; + + // + // Do not need to check SmmVariableFunctionHeader->Data in SMRAM here. + // It is covered by previous CommBuffer check + // + if (!SmmIsBufferOutsideSmmValid ((EFI_PHYSICAL_ADDRESS) (UINTN) CommBufferSize, sizeof (UINTN))) { + DEBUG ((EFI_D_ERROR, "GetStatistics: SMM communication buffer in SMRAM!\n")); + Status = EFI_ACCESS_DENIED; + goto EXIT; + } + + Status = SmmVariableGetStatistics (VariableInfo, &InfoSize); + *CommBufferSize = InfoSize + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE; + break; + + case SMM_VARIABLE_FUNCTION_LOCK_VARIABLE: + if (mEndOfDxe) { + Status = EFI_ACCESS_DENIED; + } else { + VariableToLock = (SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE *) SmmVariableFunctionHeader->Data; + Status = VariableLockRequestToLock ( + NULL, + VariableToLock->Name, + &VariableToLock->Guid + ); + } + break; + case SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_SET: + if (mEndOfDxe) { + Status = EFI_ACCESS_DENIED; + } else { + CommVariableProperty = (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY *) SmmVariableFunctionHeader->Data; + Status = VarCheckVariablePropertySet ( + CommVariableProperty->Name, + &CommVariableProperty->Guid, + &CommVariableProperty->VariableProperty + ); + } + break; + case SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET: + if (CommBufferPayloadSize < OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY, Name)) { + DEBUG ((EFI_D_ERROR, "VarCheckVariablePropertyGet: SMM communication buffer size invalid!\n")); + return EFI_SUCCESS; + } + // + // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload. + // + CopyMem (mVariableBufferPayload, SmmVariableFunctionHeader->Data, CommBufferPayloadSize); + CommVariableProperty = (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY *) mVariableBufferPayload; + if ((UINTN) (~0) - CommVariableProperty->NameSize < OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY, Name)) { + // + // Prevent InfoSize overflow happen + // + Status = EFI_ACCESS_DENIED; + goto EXIT; + } + InfoSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY, Name) + CommVariableProperty->NameSize; + + // + // SMRAM range check already covered before + // + if (InfoSize > CommBufferPayloadSize) { + DEBUG ((EFI_D_ERROR, "VarCheckVariablePropertyGet: Data size exceed communication buffer size limit!\n")); + Status = EFI_ACCESS_DENIED; + goto EXIT; + } + + if (CommVariableProperty->NameSize < sizeof (CHAR16) || CommVariableProperty->Name[CommVariableProperty->NameSize/sizeof (CHAR16) - 1] != L'\0') { + // + // Make sure VariableName is A Null-terminated string. + // + Status = EFI_ACCESS_DENIED; + goto EXIT; + } + + Status = VarCheckVariablePropertyGet ( + CommVariableProperty->Name, + &CommVariableProperty->Guid, + &CommVariableProperty->VariableProperty + ); + CopyMem (SmmVariableFunctionHeader->Data, mVariableBufferPayload, CommBufferPayloadSize); + break; + + default: + Status = EFI_UNSUPPORTED; + } + +EXIT: + + SmmVariableFunctionHeader->ReturnStatus = Status; + + return EFI_SUCCESS; +} + + +/** + SMM END_OF_DXE 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 SmmEndOfDxeCallback runs successfully + +**/ +EFI_STATUS +EFIAPI +SmmEndOfDxeCallback ( + IN CONST EFI_GUID *Protocol, + IN VOID *Interface, + IN EFI_HANDLE Handle + ) +{ + DEBUG ((EFI_D_INFO, "[Variable]SMM_END_OF_DXE is signaled\n")); + mEndOfDxe = TRUE; + VarCheckLibInitializeAtEndOfDxe (NULL); + // + // The initialization for variable quota. + // + InitializeVariableQuota (); + if (PcdGetBool (PcdReclaimVariableSpaceAtEndOfDxe)) { + ReclaimForOS (); + } + + return EFI_SUCCESS; +} + + +/** + Non-Volatile variable write service is ready event handler. + +**/ +VOID +EFIAPI +InstallVariableWriteReady ( + VOID + ) +{ + EFI_STATUS Status; + + DEBUG ((DEBUG_INFO, "Installing SmmVariableWriteGuid to notify wrapper\n")); + // + // Notify the variable wrapper driver the variable write service is ready + // + Status = gBS->InstallProtocolInterface ( + &mSmmVariableHandle, + &gSmmVariableWriteGuid, + 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 available. 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 *SmmEndOfDxeRegistration; + EFI_HANDLE *Handles; + UINTN Index; + + // + // Allocate runtime memory for variable driver global structure. + // + mVariableModuleGlobal = NULL; + Status = gSmst->SmmAllocatePool ( + EfiRuntimeServicesData, + sizeof (VARIABLE_MODULE_GLOBAL), + (VOID **) &mVariableModuleGlobal + ); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status) || mVariableModuleGlobal == NULL) { + return EFI_OUT_OF_RESOURCES; + } + ZeroMem (mVariableModuleGlobal, sizeof (VARIABLE_MODULE_GLOBAL)); + + // + // Get the VARIABLE_STORAGE_PROTOCOL instances + // + mVariableModuleGlobal->VariableGlobal.VariableStoresCount = 0; + Handles = NULL; + + // + // Determine if Force Volatile Mode is enabled + // + mForceVolatileVariable = PcdGetBool (PcdForceVolatileVariable); + + if (!mForceVolatileVariable) { + // + // Determine the number of handles + // + Status = gSmst->SmmLocateHandle ( + ByProtocol, + &gVariableStorageProtocolGuid, + NULL, + &mVariableModuleGlobal->VariableGlobal.VariableStoresCount, + Handles + ); + if (Status != EFI_BUFFER_TOO_SMALL) { + DEBUG ((DEBUG_INFO, "No SMM VariableStorageProtocol instances exist\n")); + return EFI_NOT_READY; + } + Handles = AllocateZeroPool (mVariableModuleGlobal->VariableGlobal.VariableStoresCount); + if (Handles == NULL) { + ASSERT (Handles != NULL); + return EFI_OUT_OF_RESOURCES; + } + mVariableModuleGlobal->VariableGlobal.VariableStores = NULL; + Status = gSmst->SmmAllocatePool ( + EfiRuntimeServicesData, + sizeof (VARIABLE_STORAGE_PROTOCOL *) * (mVariableModuleGlobal->VariableGlobal.VariableStoresCount / sizeof (EFI_HANDLE)), + (VOID **) &mVariableModuleGlobal->VariableGlobal.VariableStores + ); + ASSERT_EFI_ERROR (Status); + ASSERT (mVariableModuleGlobal->VariableGlobal.VariableStores != NULL); + if (EFI_ERROR (Status) || mVariableModuleGlobal->VariableGlobal.VariableStores == NULL) { + FreePool (Handles); + return EFI_OUT_OF_RESOURCES; + } + // + // Get the handles + // + Status = gSmst->SmmLocateHandle ( + ByProtocol, + &gVariableStorageProtocolGuid, + NULL, + &mVariableModuleGlobal->VariableGlobal.VariableStoresCount, + Handles + ); + if (EFI_ERROR (Status)) { + ASSERT_EFI_ERROR (Status); + gSmst->SmmFreePool (mVariableModuleGlobal->VariableGlobal.VariableStores); + FreePool (Handles); + return Status; + } + mVariableModuleGlobal->VariableGlobal.VariableStoresCount /= sizeof (EFI_HANDLE); + + for ( Index = 0; + Index < mVariableModuleGlobal->VariableGlobal.VariableStoresCount; + Index++) { + Status = gSmst->SmmHandleProtocol ( + Handles[Index], + &gVariableStorageProtocolGuid, + (VOID **) &mVariableModuleGlobal->VariableGlobal.VariableStores[Index] + ); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + FreePool (Handles); + return Status; + } + } + FreePool (Handles); + } + + // + // 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); + + Status = gSmst->SmmInstallProtocolInterface ( + &VariableHandle, + &gEdkiiSmmVarCheckProtocolGuid, + EFI_NATIVE_INTERFACE, + &mSmmVarCheck + ); + ASSERT_EFI_ERROR (Status); + + mVariableBufferPayloadSize = GetNonVolatileMaxVariableSize () + + OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY, Name) - GetVariableHeaderSize (); + + Status = gSmst->SmmAllocatePool ( + EfiRuntimeServicesData, + mVariableBufferPayloadSize, + (VOID **)&mVariableBufferPayload + ); + 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 EFI_SMM_END_OF_DXE_PROTOCOL_GUID notify function. + // + Status = gSmst->SmmRegisterProtocolNotify ( + &gEfiSmmEndOfDxeProtocolGuid, + SmmEndOfDxeCallback, + &SmmEndOfDxeRegistration + ); + ASSERT_EFI_ERROR (Status); + + return EFI_SUCCESS; +} + diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf new file mode 100644 index 0000000000..d0cec98ae4 --- /dev/null +++ b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf @@ -0,0 +1,114 @@ +## @file +# Provides SMM variable service. +# +# 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. +# +# Caution: This module requires additional review when modified. +# This driver will have external input - variable data and communicate buffer in SMM mode. +# This external input must be validated carefully to avoid security issues such as +# buffer overflow or integer overflow. +# The whole SMM authentication variable design relies on the integrity of flash part and SMM. +# which is assumed to be protected by platform. All variable code and metadata in flash/SMM Memory +# may not be modified without authorization. If platform fails to protect these resources, +# the authentication service provided in this driver will be broken, and the behavior is undefined. +# +# Copyright (c) 2010 - 2016, 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 + MODULE_UNI_FILE = VariableSmm.uni + 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] + Variable.c + VariableSmm.c + VarCheck.c + Variable.h + VariableExLib.c + TcgMorLockSmm.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + BroxtonSiPkg/BroxtonSiPkg.dec + +[LibraryClasses] + UefiDriverEntryPoint + MemoryAllocationLib + BaseLib + SynchronizationLib + UefiLib + SmmServicesTableLib + BaseMemoryLib + DebugLib + DxeServicesTableLib + HobLib + PcdLib + SmmMemLib + AuthVariableLib + VarCheckLib + VariableStorageSelectorLib + +[Protocols] + gVariableStorageProtocolGuid ## CONSUMES + gEfiSmmVariableProtocolGuid + gEfiSmmEndOfDxeProtocolGuid ## NOTIFY + gEdkiiSmmVarCheckProtocolGuid ## PRODUCES + +[Guids] + gEfiAuthenticatedVariableGuid + gEfiVariableGuid + gEfiGlobalVariableGuid + gEfiMemoryOverwriteControlDataGuid ## CONSUMES ## Variable:L"MemoryOverwriteRequestControl" + gEfiMemoryOverwriteRequestControlLockGuid ## PRODUCES ## Variable:L"MemoryOverwriteRequestControlLock" + + gSmmVariableWriteGuid ## PRODUCES ## GUID # Install protocol + gEdkiiVarErrorFlagGuid ## CONSUMES ## GUID + gZeroGuid ## SOMETIMES_CONSUMES ## GUID + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdMaxVariableSize ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdMaxAuthVariableSize ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdMaxHardwareErrorVariableSize ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdVariableStoreSize ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdHwErrStorageSize ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdMaxUserNvVariableSpaceSize ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdBoottimeReservedNvVariableSpaceSize ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdReclaimVariableSpaceAtEndOfDxe ## CONSUMES + gSiPkgTokenSpaceGuid.PcdForceVolatileVariable ## CONSUMES + +[FeaturePcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdVariableCollectStatistics ## CONSUMES # Variable statistic information + gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultLangDeprecate ## CONSUMES # Auto update PlatformLang/Lang + +[Depex] + gVariableStorageProtocolGuid + +[UserExtensions.TianoCore."ExtraFiles"] + VariableSmmExtra.uni diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.uni b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.uni new file mode 100644 index 0000000000..79513cd49c Binary files /dev/null and b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.uni differ diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmExtra.uni b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmExtra.uni new file mode 100644 index 0000000000..f4b74a363b Binary files /dev/null and b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmExtra.uni differ diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c new file mode 100644 index 0000000000..21ed15ed40 --- /dev/null +++ b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c @@ -0,0 +1,1228 @@ +/** @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. + + Caution: This module requires additional review when modified. + This driver will have external input - variable data. + This external input must be validated carefully to avoid security issue like + buffer overflow, integer overflow. + + RuntimeServiceGetVariable() and RuntimeServiceSetVariable() are external API + to receive data buffer. The size should be checked carefully. + + InitCommunicateBuffer() is really function to check the variable data size. + + Copyright (c) 2010 - 2016, 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 +#include +#include +#include + +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; +UINTN mVariableBufferSize; +UINTN mVariableBufferPayloadSize; +EFI_LOCK mVariableServicesLock; +EDKII_VARIABLE_LOCK_PROTOCOL mVariableLock; +EDKII_VAR_CHECK_PROTOCOL mVarCheck; + + +/** + SecureBoot Hook for SetVariable. + + @param[in] VariableName Name of Variable to be found. + @param[in] VendorGuid Variable vendor GUID. + +**/ +VOID +EFIAPI +SecureBootHook ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid + ); + + +/** + 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[in] 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[in] Lock A pointer to the lock to release. + +**/ +VOID +ReleaseLockOnlyAtBootTime ( + IN EFI_LOCK *Lock + ) +{ + if (!EfiAtRuntime ()) { + EfiReleaseLock (Lock); + } +} + + +/** + Initialize the communicate buffer using DataSize and Function. + + The communicate size is: SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + + DataSize. + + Caution: This function may receive untrusted input. + The data size external input, so this function will validate it carefully to avoid buffer overflow. + + @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; +} + + +/** + Mark a variable that will become read-only after leaving the DXE phase of execution. + + @param[in] This The VARIABLE_LOCK_PROTOCOL instance. + @param[in] VariableName A pointer to the variable name that will be made read-only subsequently. + @param[in] VendorGuid A pointer to the vendor GUID that will be made read-only subsequently. + + @retval EFI_SUCCESS The variable specified by the VariableName and the VendorGuid was marked + as pending to be read-only. + @retval EFI_INVALID_PARAMETER VariableName or VendorGuid is NULL. + Or VariableName is an empty string. + @retval EFI_ACCESS_DENIED EFI_END_OF_DXE_EVENT_GROUP_GUID or EFI_EVENT_GROUP_READY_TO_BOOT has + already been signaled. + @retval EFI_OUT_OF_RESOURCES There is not enough resource to hold the lock request. + +**/ +EFI_STATUS +EFIAPI +VariableLockRequestToLock ( + IN CONST EDKII_VARIABLE_LOCK_PROTOCOL *This, + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid + ) +{ + EFI_STATUS Status; + UINTN VariableNameSize; + UINTN PayloadSize; + SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE *VariableToLock; + + if (VariableName == NULL || VariableName[0] == 0 || VendorGuid == NULL) { + return EFI_INVALID_PARAMETER; + } + + VariableNameSize = StrSize (VariableName); + VariableToLock = NULL; + + // + // If VariableName exceeds SMM payload limit. Return failure + // + if (VariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE, Name)) { + return EFI_INVALID_PARAMETER; + } + + AcquireLockOnlyAtBootTime(&mVariableServicesLock); + + // + // 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_LOCK_VARIABLE, Name) + VariableNameSize; + Status = InitCommunicateBuffer ((VOID **) &VariableToLock, PayloadSize, SMM_VARIABLE_FUNCTION_LOCK_VARIABLE); + if (EFI_ERROR (Status)) { + goto Done; + } + if (VariableToLock == NULL) { + ASSERT (VariableToLock != NULL); + return EFI_OUT_OF_RESOURCES; + } + + CopyGuid (&VariableToLock->Guid, VendorGuid); + VariableToLock->NameSize = VariableNameSize; + CopyMem (VariableToLock->Name, VariableName, VariableToLock->NameSize); + + // + // Send data to SMM. + // + Status = SendCommunicateBuffer (PayloadSize); + +Done: + ReleaseLockOnlyAtBootTime (&mVariableServicesLock); + return Status; +} + + +/** + Register SetVariable check handler. + + @param[in] Handler Pointer to check handler. + + @retval EFI_SUCCESS The SetVariable check handler was registered successfully. + @retval EFI_INVALID_PARAMETER Handler is NULL. + @retval EFI_ACCESS_DENIED EFI_END_OF_DXE_EVENT_GROUP_GUID or EFI_EVENT_GROUP_READY_TO_BOOT has + already been signaled. + @retval EFI_OUT_OF_RESOURCES There is not enough resource for the SetVariable check handler register request. + @retval EFI_UNSUPPORTED This interface is not implemented. + For example, it is unsupported in VarCheck protocol if both VarCheck and SmmVarCheck protocols are present. + +**/ +EFI_STATUS +EFIAPI +VarCheckRegisterSetVariableCheckHandler ( + IN VAR_CHECK_SET_VARIABLE_CHECK_HANDLER Handler + ) +{ + return EFI_UNSUPPORTED; +} + + +/** + Variable property set. + + @param[in] Name Pointer to the variable name. + @param[in] Guid Pointer to the vendor GUID. + @param[in] VariableProperty Pointer to the input variable property. + + @retval EFI_SUCCESS The property of variable specified by the Name and Guid was set successfully. + @retval EFI_INVALID_PARAMETER Name, Guid or VariableProperty is NULL, or Name is an empty string, + or the fields of VariableProperty are not valid. + @retval EFI_ACCESS_DENIED EFI_END_OF_DXE_EVENT_GROUP_GUID or EFI_EVENT_GROUP_READY_TO_BOOT has + already been signaled. + @retval EFI_OUT_OF_RESOURCES There is not enough resource for the variable property set request. + +**/ +EFI_STATUS +EFIAPI +VarCheckVariablePropertySet ( + IN CHAR16 *Name, + IN EFI_GUID *Guid, + IN VAR_CHECK_VARIABLE_PROPERTY *VariableProperty + ) +{ + EFI_STATUS Status; + UINTN VariableNameSize; + UINTN PayloadSize; + SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY *CommVariableProperty; + + if (Name == NULL || Name[0] == 0 || Guid == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (VariableProperty == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (VariableProperty->Revision != VAR_CHECK_VARIABLE_PROPERTY_REVISION) { + return EFI_INVALID_PARAMETER; + } + + VariableNameSize = StrSize (Name); + CommVariableProperty = NULL; + + // + // If VariableName exceeds SMM payload limit. Return failure + // + if (VariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY, Name)) { + return EFI_INVALID_PARAMETER; + } + + AcquireLockOnlyAtBootTime (&mVariableServicesLock); + + // + // 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_VAR_CHECK_VARIABLE_PROPERTY, Name) + VariableNameSize; + Status = InitCommunicateBuffer ((VOID **) &CommVariableProperty, PayloadSize, SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_SET); + if (EFI_ERROR (Status)) { + goto Done; + } + + if (CommVariableProperty == NULL) { + ASSERT (CommVariableProperty != NULL); + return EFI_OUT_OF_RESOURCES; + } + + CopyGuid (&CommVariableProperty->Guid, Guid); + CopyMem (&CommVariableProperty->VariableProperty, VariableProperty, sizeof (*VariableProperty)); + CommVariableProperty->NameSize = VariableNameSize; + CopyMem (CommVariableProperty->Name, Name, CommVariableProperty->NameSize); + + // + // Send data to SMM. + // + Status = SendCommunicateBuffer (PayloadSize); + +Done: + ReleaseLockOnlyAtBootTime (&mVariableServicesLock); + return Status; +} + + +/** + Variable property get. + + @param[in] Name Pointer to the variable name. + @param[in] Guid Pointer to the vendor GUID. + @param[out] VariableProperty Pointer to the output variable property. + + @retval EFI_SUCCESS The property of variable specified by the Name and Guid was got successfully. + @retval EFI_INVALID_PARAMETER Name, Guid or VariableProperty is NULL, or Name is an empty string. + @retval EFI_NOT_FOUND The property of variable specified by the Name and Guid was not found. + +**/ +EFI_STATUS +EFIAPI +VarCheckVariablePropertyGet ( + IN CHAR16 *Name, + IN EFI_GUID *Guid, + OUT VAR_CHECK_VARIABLE_PROPERTY *VariableProperty + ) +{ + EFI_STATUS Status; + UINTN VariableNameSize; + UINTN PayloadSize; + SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY *CommVariableProperty; + + if (Name == NULL || Name[0] == 0 || Guid == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (VariableProperty == NULL) { + return EFI_INVALID_PARAMETER; + } + + VariableNameSize = StrSize (Name); + CommVariableProperty = NULL; + + // + // If VariableName exceeds SMM payload limit. Return failure + // + if (VariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY, Name)) { + return EFI_INVALID_PARAMETER; + } + + AcquireLockOnlyAtBootTime (&mVariableServicesLock); + + // + // 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_VAR_CHECK_VARIABLE_PROPERTY, Name) + VariableNameSize; + Status = InitCommunicateBuffer ((VOID **) &CommVariableProperty, PayloadSize, SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET); + if (EFI_ERROR (Status)) { + goto Done; + } + + if (CommVariableProperty == NULL) { + ASSERT (CommVariableProperty != NULL); + return EFI_OUT_OF_RESOURCES; + } + + CopyGuid (&CommVariableProperty->Guid, Guid); + CommVariableProperty->NameSize = VariableNameSize; + CopyMem (CommVariableProperty->Name, Name, CommVariableProperty->NameSize); + + // + // Send data to SMM. + // + Status = SendCommunicateBuffer (PayloadSize); + if (Status == EFI_SUCCESS) { + CopyMem (VariableProperty, &CommVariableProperty->VariableProperty, sizeof (*VariableProperty)); + } + +Done: + ReleaseLockOnlyAtBootTime (&mVariableServicesLock); + return Status; +} + + +/** + This code finds variable in storage blocks (Volatile or Non-Volatile). + + Caution: This function may receive untrusted input. + The data size is external input, so this function will validate it carefully to avoid buffer overflow. + + @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; + UINTN TempDataSize; + UINTN VariableNameSize; + + if (VariableName == NULL || VendorGuid == NULL || DataSize == NULL) { + return EFI_INVALID_PARAMETER; + } + + TempDataSize = *DataSize; + VariableNameSize = StrSize (VariableName); + SmmVariableHeader = NULL; + + // + // If VariableName exceeds SMM payload limit. Return failure + // + if (VariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) { + return EFI_INVALID_PARAMETER; + } + + AcquireLockOnlyAtBootTime(&mVariableServicesLock); + + // + // Init the communicate buffer. The buffer data size is: + // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize. + // + if (TempDataSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) - VariableNameSize) { + // + // If output data buffer exceed SMM payload limit. Trim output buffer to SMM payload size + // + TempDataSize = mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) - VariableNameSize; + } + PayloadSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + VariableNameSize + TempDataSize; + + Status = InitCommunicateBuffer ((VOID **) &SmmVariableHeader, PayloadSize, SMM_VARIABLE_FUNCTION_GET_VARIABLE); + if (EFI_ERROR (Status)) { + goto Done; + } + if (SmmVariableHeader == NULL) { + ASSERT (SmmVariableHeader != NULL); + return EFI_OUT_OF_RESOURCES; + } + + CopyGuid (&SmmVariableHeader->Guid, VendorGuid); + SmmVariableHeader->DataSize = TempDataSize; + SmmVariableHeader->NameSize = VariableNameSize; + 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. + // + if (Status == EFI_SUCCESS || Status == EFI_BUFFER_TOO_SMALL) { + // + // SMM CommBuffer DataSize can be a trimed value + // Only update DataSize when needed + // + *DataSize = SmmVariableHeader->DataSize; + } + if (Attributes != NULL) { + *Attributes = SmmVariableHeader->Attributes; + } + + if (EFI_ERROR (Status)) { + goto Done; + } + + if (Data != NULL) { + CopyMem (Data, (UINT8 *)SmmVariableHeader->Name + SmmVariableHeader->NameSize, SmmVariableHeader->DataSize); + } else { + Status = EFI_INVALID_PARAMETER; + } + +Done: + ReleaseLockOnlyAtBootTime (&mVariableServicesLock); + 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; + UINTN OutVariableNameSize; + UINTN InVariableNameSize; + + if (VariableNameSize == NULL || VariableName == NULL || VendorGuid == NULL) { + return EFI_INVALID_PARAMETER; + } + + OutVariableNameSize = *VariableNameSize; + InVariableNameSize = StrSize (VariableName); + SmmGetNextVariableName = NULL; + + // + // If input string exceeds SMM payload limit. Return failure + // + if (InVariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name)) { + return EFI_INVALID_PARAMETER; + } + + AcquireLockOnlyAtBootTime(&mVariableServicesLock); + + // + // Init the communicate buffer. The buffer data size is: + // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize. + // + if (OutVariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name)) { + // + // If output buffer exceed SMM payload limit. Trim output buffer to SMM payload size + // + OutVariableNameSize = mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name); + } + // + // Payload should be Guid + NameSize + MAX of Input & Output buffer + // + PayloadSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name) + MAX (OutVariableNameSize, InVariableNameSize); + + Status = InitCommunicateBuffer ((VOID **) &SmmGetNextVariableName, PayloadSize, SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME); + if (EFI_ERROR (Status)) { + goto Done; + } + if (SmmGetNextVariableName == NULL) { + ASSERT (SmmGetNextVariableName != NULL); + return EFI_OUT_OF_RESOURCES; + } + + // + // SMM comm buffer->NameSize is buffer size for return string + // + SmmGetNextVariableName->NameSize = OutVariableNameSize; + + CopyGuid (&SmmGetNextVariableName->Guid, VendorGuid); + // + // Copy whole string + // + CopyMem (SmmGetNextVariableName->Name, VariableName, InVariableNameSize); + if (OutVariableNameSize > InVariableNameSize) { + ZeroMem ((UINT8 *) SmmGetNextVariableName->Name + InVariableNameSize, OutVariableNameSize - InVariableNameSize); + } + + // + // Send data to SMM + // + Status = SendCommunicateBuffer (PayloadSize); + + // + // Get data from SMM. + // + if (Status == EFI_SUCCESS || Status == EFI_BUFFER_TOO_SMALL) { + // + // SMM CommBuffer NameSize can be a trimed value + // Only update VariableNameSize when needed + // + *VariableNameSize = SmmGetNextVariableName->NameSize; + } + if (EFI_ERROR (Status)) { + goto Done; + } + + CopyGuid (VendorGuid, &SmmGetNextVariableName->Guid); + CopyMem (VariableName, SmmGetNextVariableName->Name, SmmGetNextVariableName->NameSize); + +Done: + ReleaseLockOnlyAtBootTime (&mVariableServicesLock); + return Status; +} + + +/** + This code sets variable in storage blocks (Volatile or Non-Volatile). + + Caution: This function may receive untrusted input. + The data size and data are external input, so this function will validate it carefully to avoid buffer overflow. + + @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; + UINTN VariableNameSize; + + // + // Check input parameters. + // + if (VariableName == NULL || VariableName[0] == 0 || VendorGuid == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (DataSize != 0 && Data == NULL) { + return EFI_INVALID_PARAMETER; + } + + VariableNameSize = StrSize (VariableName); + SmmVariableHeader = NULL; + + // + // If VariableName or DataSize exceeds SMM payload limit. Return failure + // + if ((VariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) || + (DataSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) - VariableNameSize)){ + return EFI_INVALID_PARAMETER; + } + + AcquireLockOnlyAtBootTime(&mVariableServicesLock); + + // + // 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) + VariableNameSize + DataSize; + Status = InitCommunicateBuffer ((VOID **) &SmmVariableHeader, PayloadSize, SMM_VARIABLE_FUNCTION_SET_VARIABLE); + if (EFI_ERROR (Status)) { + goto Done; + } + + if (SmmVariableHeader == NULL) { + ASSERT (SmmVariableHeader != NULL); + return EFI_OUT_OF_RESOURCES; + } + + CopyGuid ((EFI_GUID *) &SmmVariableHeader->Guid, VendorGuid); + SmmVariableHeader->DataSize = DataSize; + SmmVariableHeader->NameSize = VariableNameSize; + SmmVariableHeader->Attributes = Attributes; + CopyMem (SmmVariableHeader->Name, VariableName, SmmVariableHeader->NameSize); + CopyMem ((UINT8 *) SmmVariableHeader->Name + SmmVariableHeader->NameSize, Data, DataSize); + + // + // Send data to SMM. + // + Status = SendCommunicateBuffer (PayloadSize); + +Done: + ReleaseLockOnlyAtBootTime (&mVariableServicesLock); + + if (!EfiAtRuntime ()) { + if (!EFI_ERROR (Status)) { + SecureBootHook ( + VariableName, + VendorGuid + ); + } + } + 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; + + SmmQueryVariableInfo = NULL; + + if(MaximumVariableStorageSize == NULL || RemainingVariableStorageSize == NULL || MaximumVariableSize == NULL || Attributes == 0) { + return EFI_INVALID_PARAMETER; + } + + AcquireLockOnlyAtBootTime (&mVariableServicesLock); + + // + // Init the communicate buffer. The buffer data size is: + // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize; + // + PayloadSize = sizeof (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO); + Status = InitCommunicateBuffer ((VOID **) &SmmQueryVariableInfo, PayloadSize, SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO); + if (EFI_ERROR (Status)) { + goto Done; + } + if (SmmQueryVariableInfo == NULL) { + ASSERT (SmmQueryVariableInfo != NULL); + return EFI_OUT_OF_RESOURCES; + } + + SmmQueryVariableInfo->Attributes = Attributes; + + // + // Send data to SMM. + // + Status = SendCommunicateBuffer (PayloadSize); + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // Get data from SMM. + // + *MaximumVariableSize = SmmQueryVariableInfo->MaximumVariableSize; + *MaximumVariableStorageSize = SmmQueryVariableInfo->MaximumVariableStorageSize; + *RemainingVariableStorageSize = SmmQueryVariableInfo->RemainingVariableStorageSize; + +Done: + ReleaseLockOnlyAtBootTime (&mVariableServicesLock); + return Status; +} + + +/** + 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); + + gBS->CloseEvent (Event); +} + + +/** + 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); +} + + +/** + This code gets variable payload size. + + @param[out] VariablePayloadSize Output pointer to variable payload size. + + @retval EFI_SUCCESS Get successfully. + @retval Others Get unsuccessfully. + +**/ +EFI_STATUS +EFIAPI +GetVariablePayloadSize ( + OUT UINTN *VariablePayloadSize + ) +{ + EFI_STATUS Status; + SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE *SmmGetPayloadSize; + EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; + SMM_VARIABLE_COMMUNICATE_HEADER *SmmVariableFunctionHeader; + UINTN CommSize; + UINT8 *CommBuffer; + + SmmGetPayloadSize = NULL; + CommBuffer = NULL; + + if(VariablePayloadSize == NULL) { + return EFI_INVALID_PARAMETER; + } + + AcquireLockOnlyAtBootTime (&mVariableServicesLock); + + // + // Init the communicate buffer. The buffer data size is: + // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + sizeof (SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE); + // + CommSize = SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + sizeof (SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE); + CommBuffer = AllocateZeroPool (CommSize); + if (CommBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + SmmCommunicateHeader = (EFI_SMM_COMMUNICATE_HEADER *) CommBuffer; + CopyGuid (&SmmCommunicateHeader->HeaderGuid, &gEfiSmmVariableProtocolGuid); + SmmCommunicateHeader->MessageLength = SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + sizeof (SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE); + + SmmVariableFunctionHeader = (SMM_VARIABLE_COMMUNICATE_HEADER *) SmmCommunicateHeader->Data; + SmmVariableFunctionHeader->Function = SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE; + SmmGetPayloadSize = (SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE *) SmmVariableFunctionHeader->Data; + + // + // Send data to SMM. + // + Status = mSmmCommunication->Communicate (mSmmCommunication, CommBuffer, &CommSize); + ASSERT_EFI_ERROR (Status); + + Status = SmmVariableFunctionHeader->ReturnStatus; + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // Get data from SMM. + // + *VariablePayloadSize = SmmGetPayloadSize->VariablePayloadSize; + +Done: + if (CommBuffer != NULL) { + FreePool (CommBuffer); + } + ReleaseLockOnlyAtBootTime (&mVariableServicesLock); + return Status; +} + + +/** + 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 communicate buffer. + // + Status = GetVariablePayloadSize (&mVariableBufferPayloadSize); + ASSERT_EFI_ERROR (Status); + mVariableBufferSize = SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + mVariableBufferPayloadSize; + 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); + + mVariableLock.RequestToLock = VariableLockRequestToLock; + Status = gBS->InstallMultipleProtocolInterfaces ( + &mHandle, + &gEdkiiVariableLockProtocolGuid, + &mVariableLock, + NULL + ); + ASSERT_EFI_ERROR (Status); + + mVarCheck.RegisterSetVariableCheckHandler = VarCheckRegisterSetVariableCheckHandler; + mVarCheck.VariablePropertySet = VarCheckVariablePropertySet; + mVarCheck.VariablePropertyGet = VarCheckVariablePropertyGet; + Status = gBS->InstallMultipleProtocolInterfaces ( + &mHandle, + &gEdkiiVarCheckProtocolGuid, + &mVarCheck, + NULL + ); + ASSERT_EFI_ERROR (Status); + + gBS->CloseEvent (Event); +} + + +/** + 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 (&gSmmVariableWriteGuid, NULL, (VOID **) &ProtocolOps); + if (EFI_ERROR (Status)) { + return; + } + + Status = gBS->InstallProtocolInterface ( + &mHandle, + &gEfiVariableWriteArchProtocolGuid, + EFI_NATIVE_INTERFACE, + NULL + ); + ASSERT_EFI_ERROR (Status); + + 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 available. 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; + EFI_EVENT LegacyBootEvent; + + EfiInitializeLock (&mVariableServicesLock, TPL_NOTIFY); + + // + // Smm variable service is ready + // + EfiCreateProtocolNotifyEvent ( + &gEfiSmmVariableProtocolGuid, + TPL_CALLBACK, + SmmVariableReady, + NULL, + &SmmVariableRegistration + ); + + // + // Smm Non-Volatile variable write service is ready + // + EfiCreateProtocolNotifyEvent ( + &gSmmVariableWriteGuid, + 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 inform SMM variable that it is at runtime for legacy boot. + // Reuse OnExitBootServices() here. + // + EfiCreateEventLegacyBootEx( + TPL_NOTIFY, + OnExitBootServices, + NULL, + &LegacyBootEvent + ); + + // + // 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/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf new file mode 100644 index 0000000000..8741960dd1 --- /dev/null +++ b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf @@ -0,0 +1,81 @@ +## @file +# Runtime DXE part corresponding to SMM authenticated variable module. +# +# This module installs variable arch protocol and variable write arch protocol to provide +# variable service. This module need work together with SMM authenticated variable module. +# +# Caution: This module requires additional review when modified. +# This driver will have external input - variable data. +# This external input must be validated carefully to avoid security issues such as +# buffer overflow or integer overflow. +# The whole SMM authentication variable design relies on the integrity of flash part and SMM. +# which is assumed to be protected by platform. All variable code and metadata in flash/SMM Memory +# may not be modified without authorization. If platform fails to protect these resources, +# the authentication service provided in this driver will be broken, and the behavior is undefined. +# +# Copyright (c) 2010 - 2016, 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 + MODULE_UNI_FILE = VariableSmmRuntimeDxe.uni + 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 + Measurement.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + MemoryAllocationLib + BaseLib + UefiBootServicesTableLib + DebugLib + UefiRuntimeLib + DxeServicesTableLib + UefiDriverEntryPoint + TpmMeasurementLib + +[Protocols] + gEfiVariableWriteArchProtocolGuid ## PRODUCES + gEfiVariableArchProtocolGuid ## PRODUCES + gEfiSmmCommunicationProtocolGuid ## CONSUMES + gEfiSmmVariableProtocolGuid + gEdkiiVariableLockProtocolGuid ## PRODUCES + gEdkiiVarCheckProtocolGuid ## PRODUCES + +[Guids] + gEfiEventVirtualAddressChangeGuid ## CONSUMES ## Event + gEfiEventExitBootServicesGuid ## CONSUMES ## Event + gSmmVariableWriteGuid + gEfiGlobalVariableGuid + gEfiImageSecurityDatabaseGuid + +[Depex] + gEfiSmmCommunicationProtocolGuid + +[UserExtensions.TianoCore."ExtraFiles"] + VariableSmmRuntimeDxeExtra.uni diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.uni b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.uni new file mode 100644 index 0000000000..696a04fed3 Binary files /dev/null and b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.uni differ diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxeExtra.uni b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxeExtra.uni new file mode 100644 index 0000000000..721b8235ca Binary files /dev/null and b/Silicon/BroxtonSoC/BroxtonSiPkg/SampleCode/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxeExtra.uni differ -- cgit v1.2.3