Merged PR 1355: Flash Variables to BERT

Created driver that looks through flash for variables with gEfiHardwareErrorVariableGuid and publishes them to BERT ACPI table.
This commit is contained in:
Max Knutsen 2019-07-18 18:23:29 +00:00
Родитель c12a65bdbe
Коммит 134dac1966
5 изменённых файлов: 835 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,277 @@
/** @file -- BertHelper
Helper functions that take CPER record and publish each section to BERT table.
Copyright (c), Microsoft Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**/
#include <Guid/Cper.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/MemoryAllocationLib.h>
#include <IndustryStandard/Acpi.h>
#include <Protocol/AcpiTable.h>
#include <Protocol/AcpiSystemDescriptionTable.h>
#include <Library/UefiBootServicesTableLib.h>
#include "BertHelper.h"
EFI_ACPI_TABLE_PROTOCOL *mAcpiTableProtocol = NULL;
/**
Identify different sections in CPER and send each to AddCperSectionToBert.
@return FALSE if BootErrorRegion is out of space
**/
BOOLEAN
EFIAPI
BertAddAllCperSections (
IN EFI_ACPI_6_1_BOOT_ERROR_RECORD_TABLE_HEADER *Bert,
IN VOID *ErrorData
) {
EFI_COMMON_ERROR_RECORD_HEADER *CperHdr;
EFI_ERROR_SECTION_DESCRIPTOR *CperErrSecDscp;
UINTN Index = 0;
if (Bert == NULL || ErrorData == NULL) {
DEBUG((DEBUG_ERROR, "%a - null parameter\n", __FUNCTION__));
return FALSE;
}
CperHdr = (EFI_COMMON_ERROR_RECORD_HEADER*)ErrorData;
CperErrSecDscp = (EFI_ERROR_SECTION_DESCRIPTOR *) (CperHdr + 1);
do{
if (!BertAddCperSection(Bert, CperHdr, CperErrSecDscp)) {
// Boot Error Region is out of space!
return FALSE;
}
CperErrSecDscp ++;
Index ++;
DEBUG((DEBUG_VERBOSE, "%a %d - Section %d of %d \n", __FUNCTION__, __LINE__, Index, CperHdr->SectionCount));
} while(Index < CperHdr->SectionCount);
return TRUE;
}
/**
Take CPER header and section descriptor,
use that to form a generic data entry,
then stuff that entry in BERT Boot Error Region.
**/
BOOLEAN
EFIAPI
BertAddCperSection (
IN EFI_ACPI_6_1_BOOT_ERROR_RECORD_TABLE_HEADER *Bert,
IN EFI_COMMON_ERROR_RECORD_HEADER *CperHdr,
IN EFI_ERROR_SECTION_DESCRIPTOR *CperErrSecDscp
) {
return BertErrorBlockAddErrorData (
(VOID*)Bert->BootErrorRegion,
Bert->BootErrorRegionLength,
&CperErrSecDscp->SectionType,
(VOID*) (((UINT8*) CperHdr) + (CperErrSecDscp->SectionOffset)),
CperErrSecDscp->SectionLength,
CperErrSecDscp->Severity,
TRUE // correctable
);
}
/**
Allocate and populate EFI_ACPI_6_1_BOOT_ERROR_RECORD_TABLE_HEADER
in the BERT_CONTEXT that was provided.
**/
VOID
EFIAPI
BertHeaderCreator (
IN BERT_CONTEXT *Context,
IN UINT32 ErrorBlockSize
)
{
if (Context == NULL) {
return;
}
Context->BertHeader = AllocateZeroPool (sizeof(EFI_ACPI_6_1_BOOT_ERROR_RECORD_TABLE_HEADER));
Context->Block = AllocateReservedZeroPool (ErrorBlockSize);
Context->BlockSize = ErrorBlockSize;
Context->BertHeader->Header.Signature = EFI_ACPI_6_1_BOOT_ERROR_RECORD_TABLE_SIGNATURE;
Context->BertHeader->Header.Length = sizeof(EFI_ACPI_6_1_BOOT_ERROR_RECORD_TABLE_HEADER);
Context->BertHeader->Header.Revision = EFI_ACPI_6_1_BOOT_ERROR_RECORD_TABLE_REVISION;
Context->BertHeader->Header.OemTableId = PcdGet64(PcdAcpiDefaultOemTableId);
Context->BertHeader->Header.CreatorId = PcdGet32(PcdAcpiDefaultCreatorId);
Context->BertHeader->Header.CreatorRevision = PcdGet32(PcdAcpiDefaultOemRevision);
CopyMem(Context->BertHeader->Header.OemId, PcdGetPtr(PcdAcpiDefaultOemId), sizeof(Context->BertHeader->Header.OemId));
Context->BertHeader->BootErrorRegionLength = Context->BlockSize;
Context->BertHeader->BootErrorRegion = (UINT64) Context->Block;
}
/**
Calculate BERT header checksum and publish BERT table via mAcpiTableProtocol
**/
EFI_STATUS
BertSetAcpiTable (
IN BERT_CONTEXT *Context
)
{
UINTN AcpiTableHandle;
EFI_STATUS Status;
EFI_ACPI_6_1_BOOT_ERROR_RECORD_TABLE_HEADER* Bert;
if (Context == NULL) {
return EFI_INVALID_PARAMETER;
}
Bert = Context->BertHeader;
Bert->Header.Checksum = CalculateCheckSum8 ((UINT8*)(Bert), Bert->Header.Length);
AcpiTableHandle = 0;
Status = gBS->LocateProtocol (
&gEfiAcpiTableProtocolGuid,
NULL,
(VOID**)&mAcpiTableProtocol);
if (!EFI_ERROR(Status)) {
Status = mAcpiTableProtocol->InstallAcpiTable (
mAcpiTableProtocol,
Bert,
Bert->Header.Length,
&AcpiTableHandle);
}
return Status;
}
/**
Create initial block in BERTs Boot Error Region.
**/
EFI_ACPI_6_1_GENERIC_ERROR_STATUS_STRUCTURE*
BertErrorBlockInitial (
VOID *Block,
UINT32 Severity
)
{
EFI_ACPI_6_1_GENERIC_ERROR_STATUS_STRUCTURE* BlockHeader = Block;
BlockHeader->BlockStatus = (EFI_ACPI_6_1_ERROR_BLOCK_STATUS) {0, 0, 0, 0, 0};
BlockHeader->RawDataOffset = 0;
BlockHeader->RawDataLength = 0;
BlockHeader->DataLength = 0;
BlockHeader->ErrorSeverity = Severity;
return BlockHeader;
}
/**
Add Error Block to BERT table's Boot Error Region
@return FALSE if ExpectedNewDataLength > MaxBlockLength; TRUE otherwise
**/
BOOLEAN
BertErrorBlockAddErrorData (
IN VOID *ErrorBlock,
IN UINT32 MaxBlockLength,
IN EFI_GUID *Guid,
IN VOID *GenericErrorData,
IN UINT32 SizeOfGenericErrorData,
IN UINT32 ErrorSeverity,
IN BOOLEAN Correctable
)
{
VOID *GenericErrorDataFollowEntry;
EFI_ACPI_6_1_ERROR_BLOCK_STATUS *BlockStatus;
EFI_ACPI_6_1_GENERIC_ERROR_STATUS_STRUCTURE *BlockHeader;
EFI_ACPI_6_1_GENERIC_ERROR_DATA_ENTRY_STRUCTURE *Entry;
if (ErrorBlock == NULL || GenericErrorData == NULL) {
DEBUG((DEBUG_ERROR, "%a - %d: Invalid Param \n", __FUNCTION__, __LINE__));
return FALSE;
}
DEBUG((DEBUG_VERBOSE, "%a - %d: Dumping GenericErrorData contents: \n", __FUNCTION__, __LINE__));
DEBUG_BUFFER(DEBUG_VERBOSE, GenericErrorData, SizeOfGenericErrorData, DEBUG_DM_PRINT_ADDRESS | DEBUG_DM_PRINT_ASCII);
// Setup BERT structures
BlockHeader = ErrorBlock;
BlockStatus = &BlockHeader->BlockStatus;
// Calculate length of BERT error region (including new entry)
UINT32 ExpectedNewDataLength = BlockHeader->DataLength +
sizeof(EFI_ACPI_6_1_GENERIC_ERROR_DATA_ENTRY_STRUCTURE) +
SizeOfGenericErrorData;
// Fail if we don't have room
if (sizeof(EFI_ACPI_6_1_GENERIC_ERROR_STATUS_STRUCTURE) + ExpectedNewDataLength > MaxBlockLength) {
return FALSE;
}
// Set BlockStatus Correctable/Uncorrectable fields
if (Correctable == TRUE) {
if (BlockStatus->CorrectableErrorValid == 0) {
BlockStatus->CorrectableErrorValid = 1;
} else {
BlockStatus->MultipleCorrectableErrors = 1;
}
} else {
if (BlockStatus->UncorrectableErrorValid == 0) {
BlockStatus->UncorrectableErrorValid = 1;
} else {
BlockStatus->MultipleUncorrectableErrors = 1;
}
}
// Setup Generic Error Data Entry with the values that were passed in
BlockStatus->ErrorDataEntryCount++;
Entry = (EFI_ACPI_6_1_GENERIC_ERROR_DATA_ENTRY_STRUCTURE*)(((UINT8*)ErrorBlock) +
sizeof(EFI_ACPI_6_1_GENERIC_ERROR_STATUS_STRUCTURE) +
BlockHeader->DataLength);
// Setup Entry header
gBS->SetMem (Entry, sizeof(EFI_ACPI_6_1_GENERIC_ERROR_DATA_ENTRY_STRUCTURE), 0);
gBS->CopyMem (&Entry->SectionType, Guid, sizeof(EFI_GUID));
Entry->ErrorSeverity = ErrorSeverity;
Entry->Revision = EFI_ACPI_6_1_GENERIC_ERROR_DATA_ENTRY_REVISION;
Entry->ErrorDataLength = SizeOfGenericErrorData;
// Copy data right after header
GenericErrorDataFollowEntry = (VOID*)(Entry +
sizeof(EFI_ACPI_6_1_GENERIC_ERROR_DATA_ENTRY_STRUCTURE));
gBS->CopyMem (
GenericErrorDataFollowEntry,
GenericErrorData,
SizeOfGenericErrorData);
// Setup the header with the new size
BlockHeader->DataLength = ExpectedNewDataLength;
return TRUE;
}

Просмотреть файл

@ -0,0 +1,101 @@
/** @file --
Copyright (c), Microsoft Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**/
#ifndef _BERT_H_
#define _BERT_H_
//
// ACPI table information used to initialize tables.
//
#define BOOT_ERROR_REGION_SIZE 0x1000
#define EFI_HW_ERR_REC_VAR_NAME L"HwErrRec"
#define EFI_HW_ERR_REC_VAR_NAME_LEN 13 // Buffer length covers at least "HwErrRec####\0"
// A macro to initialise the common header part of EFI ACPI tables as defined by
// EFI_ACPI_DESCRIPTION_HEADER structure.
#define ACPI_HEADER(Signature, Type, Revision) { \
Signature, /* UINT32 Signature */ \
sizeof (Type), /* UINT32 Length */ \
Revision, /* UINT8 Revision */ \
0, /* UINT8 Checksum */ \
{ EFI_ACPI_OEM_ID }, /* UINT8 OemId[6] */ \
EFI_ACPI_OEM_TABLE_ID, /* UINT64 OemTableId */ \
EFI_ACPI_OEM_REVISION, /* UINT32 OemRevision */ \
EFI_ACPI_CREATOR_ID, /* UINT32 CreatorId */ \
EFI_ACPI_CREATOR_REVISION /* UINT32 CreatorRevision */ \
}
typedef struct _BERT_CONTEXT {
EFI_ACPI_6_1_BOOT_ERROR_RECORD_TABLE_HEADER *BertHeader;
VOID *Block;
UINT32 BlockSize;
} BERT_CONTEXT;
EFI_STATUS
BertSetAcpiTable (
IN BERT_CONTEXT *Context
);
VOID
BertHeaderCreator (
BERT_CONTEXT *Context,
UINT32 ErrorBlockSize
);
EFI_ACPI_6_1_GENERIC_ERROR_STATUS_STRUCTURE*
BertErrorBlockInitial(
VOID *Block,
UINT32 Severity
);
BOOLEAN
BertErrorBlockAddErrorData (
IN VOID *ErrorBlock,
IN UINT32 MaxBlockLength,
IN EFI_GUID *Guid,
IN VOID *GenericErrorData,
IN UINT32 SizeOfGenericErrorData,
IN UINT32 ErrorSeverity,
IN BOOLEAN Correctable
);
BOOLEAN
EFIAPI
BertAddCperSection(
IN EFI_ACPI_6_1_BOOT_ERROR_RECORD_TABLE_HEADER *Bert,
IN EFI_COMMON_ERROR_RECORD_HEADER *CperHdr,
IN EFI_ERROR_SECTION_DESCRIPTOR *CperErrSecDscp
);
BOOLEAN
EFIAPI
BertAddAllCperSections (
IN EFI_ACPI_6_1_BOOT_ERROR_RECORD_TABLE_HEADER *Bert,
IN VOID *ErrorData
);
#endif // _BERT_H_

Просмотреть файл

@ -0,0 +1,87 @@
## @file -- HwErrorBert.inf
#
# Driver that looks through flash for variables with gEfiHardwareErrorVariableGuid
# and publishes them to BERT ACPI table.
#
# Copyright (c) Microsoft Corporation
# All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
##
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = HwErrorBert
FILE_GUID = 328AC7D1-6D2B-4F6B-8A2D-EA6302A62F60
MODULE_TYPE = DXE_DRIVER
VERSION_STRING = 1.0
ENTRY_POINT = HwErrorBertEntry
#
# The following information is for reference only and not required by the
# build tools.
#
# VALID_ARCHITECTURES = AARCH64 X64
#
[Sources]
HwErrorBert.c
BertHelper.c
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
MsWheaPkg/MsWheaPkg.dec
[LibraryClasses]
BaseLib
UefiLib
DebugLib
HobLib
MemoryAllocationLib
PcdLib
PrintLib
UefiBootServicesTableLib
UefiDriverEntryPoint
UefiRuntimeServicesTableLib
BaseMemoryLib
CheckHwErrRecHeaderLib
[Protocols]
gEfiAcpiTableProtocolGuid ## CONSUMES
[Pcd]
gMsWheaPkgTokenSpaceGuid.PcdVariableHardwareErrorRecordAttributeSupported
gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultOemId
gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultOemTableId
gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultOemRevision
gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultCreatorId
gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiDefaultCreatorRevision
[Guids]
gEfiHardwareErrorVariableGuid ## CONSUMES
gMsWheaReportServiceGuid ## SOMETIMES_CONSUMES
gEfiFirmwareErrorSectionGuid ## SOMETIMES_CONSUMES
gEfiEventNotificationTypeBootGuid ## SOMETIMES_CONSUMES
gEfiEventReadyToBootGuid ## CONSUMES ## Event
gEfiEventExitBootServicesGuid ## CONSUMES
[Depex]
TRUE

Просмотреть файл

@ -0,0 +1,369 @@
/** @file -- HwErrorBert.c
At EBS, driver will generate BERT table with all HwErrRec from flash storage.
At ReadyToBoot, driver will delete HwErrRec from flash.
Copyright (c), Microsoft Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**/
#include <Guid/Cper.h>
#include <Library/UefiLib.h>
#include <Library/DebugLib.h>
#include <Library/PcdLib.h>
#include <Library/ReportStatusCodeLib.h>
#include <Library/UefiDriverEntryPoint.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/CheckHwErrRecHeaderLib.h>
#include <Library/PrintLib.h>
#include <IndustryStandard/Acpi.h>
#include <Uefi.h>
#include "BertHelper.h"
STATIC EFI_EVENT mExitBootServicesEvent = NULL;
STATIC EFI_EVENT mReadyToBootEvent = NULL;
UINT16 mVarNameListCount = 0;
CHAR16 *mVarNameList = NULL;
/**
Create and publish Boot Error Runtime Table.
Gather variables in mVarNameList and add them
to the BootErrorRegion of the BERT table.
**/
VOID
EFIAPI
SetupBert() {
UINTN Size = 0;
CHAR16 *NamePtr = NULL;
VOID *Buffer = NULL;
EFI_STATUS Status;
BERT_CONTEXT Context;
if (!mVarNameList || mVarNameListCount == 0) {
DEBUG((DEBUG_WARN, "%a: leaving because list of entries to catalogue was empty.\n", __FUNCTION__));
return;
}
DEBUG((DEBUG_VERBOSE, "%a - %x CPER entries to publish to BERT\n", __FUNCTION__, mVarNameListCount));
// Create & publish BERT Header
BertHeaderCreator(&Context, BOOT_ERROR_REGION_SIZE);
BertErrorBlockInitial(Context.Block, EFI_ACPI_6_2_ERROR_SEVERITY_CORRECTED);
Status = BertSetAcpiTable(&Context);
if (EFI_ERROR(Status)) {
DEBUG((DEBUG_ERROR, "Publishing BERT ACPI table failed\n"));
return;
}
// Iterate through the list of variable names
for (UINTN Index = 0; Index < mVarNameListCount; Index ++) {
Size = 0;
Buffer = NULL;
NamePtr = &mVarNameList[Index * EFI_HW_ERR_REC_VAR_NAME_LEN];
DEBUG((DEBUG_VERBOSE, "%a - Publishing %s\n", __FUNCTION__, NamePtr));
//
// Call, get variable size; call again, get variable
//
Status = gRT->GetVariable(NamePtr,
&gEfiHardwareErrorVariableGuid,
NULL,
&Size,
NULL);
if (Status != EFI_BUFFER_TOO_SMALL) {
DEBUG((DEBUG_ERROR, "%a - %s 0 size GetVariable returned %r\n", __FUNCTION__, NamePtr, Status));
ASSERT(FALSE);
continue;
}
Buffer = AllocateZeroPool(Size);
if (Buffer == NULL) {
DEBUG((DEBUG_VERBOSE, "%a - out of memory", __FUNCTION__));
FreePool(Buffer);
return;
}
Status = gRT->GetVariable(NamePtr,
&gEfiHardwareErrorVariableGuid,
NULL,
&Size,
Buffer);
if (!EFI_ERROR (Status) && ValidateCperHeader((EFI_COMMON_ERROR_RECORD_HEADER *)Buffer, Size)) {
// We got a CPER, time to add it to BERT!
if (!BertAddAllCperSections(Context.BertHeader, Buffer)) {
DEBUG((DEBUG_ERROR, "Ran out of space in BERT boot error region\n"));
FreePool(Buffer);
return;
}
} else {
DEBUG((DEBUG_ERROR, "%a: Variable failed or CPER was deemed unsafe - %r\n", __FUNCTION__, Status));
FreePool(Buffer);
return;
}
if (Buffer) {
FreePool(Buffer);
}
}
}
/**
Go through variables on flash and find
variables with GUID gEfiHardwareErrorVariableGuid.
**/
VOID
EFIAPI
GenerateVariableList() {
CHAR16 *Name = NULL;
UINTN NameSize;
UINTN NewNameSize;
EFI_GUID Guid;
EFI_STATUS Status = EFI_SUCCESS;
DEBUG((DEBUG_VERBOSE, "%a enter\n", __FUNCTION__));
NameSize = sizeof(CHAR16);
Name = AllocateZeroPool(NameSize);
// Go through all the variables on flash
while (TRUE) {
// Get ready to recieve the next name
NewNameSize = NameSize;
Status = gRT->GetNextVariableName(&NewNameSize, Name, &Guid);
// Make room for the next name if necessary
if (Status == EFI_BUFFER_TOO_SMALL) {
Name = ReallocatePool(NameSize, NewNameSize, Name);
if (Name == NULL) {
DEBUG((DEBUG_ERROR, "%a - ReallocatePool failed, out of memory\n", __FUNCTION__));
Status = EFI_OUT_OF_RESOURCES;
goto cleanup;
}
Status = gRT->GetNextVariableName(&NewNameSize, Name, &Guid);
NameSize = NewNameSize;
}
if (Status == EFI_NOT_FOUND) {
// All done!
break;
}
ASSERT_EFI_ERROR(Status);
// If the GUID doesn't match, we don't care about it!
if (EFI_ERROR(Status) || !CompareGuid(&Guid, &gEfiHardwareErrorVariableGuid)) {
continue;
}
// Add this variable to the array
DEBUG((DEBUG_VERBOSE, "%a - found %s\n", __FUNCTION__, Name));
mVarNameList = ReallocatePool( mVarNameListCount * EFI_HW_ERR_REC_VAR_NAME_LEN * sizeof(CHAR16),
(mVarNameListCount + 1) * EFI_HW_ERR_REC_VAR_NAME_LEN * sizeof(CHAR16),
mVarNameList);
if (mVarNameList == NULL) {
DEBUG((DEBUG_ERROR, "%a - %d\n", __FUNCTION__, __LINE__));
Status = EFI_OUT_OF_RESOURCES;
goto cleanup;
}
StrCpyS(&mVarNameList[mVarNameListCount * EFI_HW_ERR_REC_VAR_NAME_LEN], StrLen(Name) + 1, Name);
mVarNameListCount++;
}
// We succeded! Let's not say otherwise.
Status = EFI_SUCCESS;
cleanup:
if (Name) {
FreePool(Name);
}
if (EFI_ERROR(Status)) {
// Shouldn't be happening... but we can cleanup anyways
ASSERT(FALSE);
mVarNameListCount = 0;
mVarNameList = NULL;
}
DEBUG((DEBUG_INFO, "%a found %x variables for the BERT table - %r\n", __FUNCTION__, mVarNameListCount, Status));
}
/**
Go through the variables on our list and delete them from flash.
**/
VOID
EFIAPI
ClearVariables() {
UINT32 Index = 0;
EFI_STATUS Status = EFI_SUCCESS;
CHAR16 *NamePtr = NULL;
DEBUG((DEBUG_VERBOSE, "%a enter: number of elements to clear = %x\n", __FUNCTION__, mVarNameListCount));
if (mVarNameList == NULL) {
DEBUG((DEBUG_ERROR, "%a mVarNameList is null!\n", __FUNCTION__));
return;
}
for (Index = 0; Index < mVarNameListCount; Index ++) {
NamePtr = &mVarNameList[Index * EFI_HW_ERR_REC_VAR_NAME_LEN];
Status = gRT->SetVariable(NamePtr, &gEfiHardwareErrorVariableGuid, 0, 0, NULL);
// Can't really do much if this fails
if (EFI_ERROR(Status)) {
DEBUG((DEBUG_ERROR, "Clearing variable %s failed with %r \n", NamePtr, Status));
ASSERT_EFI_ERROR(Status);
}
DEBUG((DEBUG_VERBOSE, "%a - Removed %s\n", __FUNCTION__, NamePtr));
}
}
/**
Exit Boot Services Callback Handler
@param[in] Event Event that signalled the callback.
@param[in] Context Pointer to an optional event contxt.
@retval None.
**/
VOID
EFIAPI
ExitBootServicesHandlerCallback(
IN EFI_EVENT Event,
IN VOID *Context
) {
EFI_STATUS Status;
if(mExitBootServicesEvent != NULL) {
Status = gBS->CloseEvent(mExitBootServicesEvent);
ASSERT_EFI_ERROR (Status);
}
// Delete all variables in the array because they have been published
ClearVariables();
if (mVarNameList) {
FreePool(mVarNameList);
}
return;
}
/**
@param[in] Event Event that signalled the callback.
@param[in] Context Pointer to an optional event contxt.
@retval None.
**/
VOID
EFIAPI
ReadyToBootHandlerCallback(
IN EFI_EVENT Event,
IN VOID *Context
) {
EFI_STATUS Status;
if(mReadyToBootEvent != NULL) {
Status = gBS->CloseEvent(mReadyToBootEvent);
ASSERT_EFI_ERROR (Status);
}
// Go through the variables in flash and note that have the right GUID
GenerateVariableList();
// Go through the variables in our list and publish them to the BERT table
SetupBert();
return;
}
/**
Entry to
@param[in] ImageHandle The image handle.
@param[in] SystemTable The system table.
@retval Status From internal routine or boot object, should not fail
**/
EFI_STATUS
EFIAPI
HwErrorBertEntry (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
DEBUG((DEBUG_VERBOSE, "%a\n", __FUNCTION__));
// Register for the exit boot event to publish BERT table.
Status = gBS->CreateEventEx(EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
ExitBootServicesHandlerCallback,
NULL,
&gEfiEventExitBootServicesGuid,
&mExitBootServicesEvent);
if (EFI_ERROR(Status)) {
DEBUG((DEBUG_ERROR, "%a - Error creating mExitBootServicesEvent\n", __FUNCTION__));
goto cleanup;
}
// Register for the ready to boot event to clear variables!
// This is separate from publishing BERT table in case we never boot to OS.
// In that case, we would be able to try to publish the variables again.
Status = gBS->CreateEventEx(EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
ReadyToBootHandlerCallback,
NULL,
&gEfiEventReadyToBootGuid,
&mReadyToBootEvent);
if (EFI_ERROR(Status)) {
DEBUG((DEBUG_ERROR, "%a - Error creating mReadyToBootEvent\n", __FUNCTION__));
goto cleanup;
}
cleanup:
// If anything goes wrong, we will need to close the events.
ASSERT_EFI_ERROR(Status);
if (EFI_ERROR(Status)) {
if(mReadyToBootEvent != NULL) {
Status = gBS->CloseEvent(mReadyToBootEvent);
}
if(mExitBootServicesEvent != NULL) {
Status = gBS->CloseEvent(mExitBootServicesEvent);
}
}
return Status;
}

Просмотреть файл

@ -136,6 +136,7 @@
}
[Components.X64]
MsWheaPkg/HwErrBert/HwErrBert.inf
MsWheaPkg/MsWheaReport/Dxe/MsWheaReportDxe.inf
MsWheaPkg/Test/UnitTests/Library/LibraryClass/CheckHwErrRecHeaderTestsApp.inf