зеркало из https://github.com/microsoft/mu_plus.git
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:
Родитель
c12a65bdbe
Коммит
134dac1966
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче