702 lines
25 KiB
C
702 lines
25 KiB
C
/** @file
|
|
This DXE driver supports the CrashLog Feature.
|
|
|
|
@copyright
|
|
INTEL CONFIDENTIAL
|
|
Copyright 2016 - 2021 Intel Corporation.
|
|
|
|
The source code contained or described herein and all documents related to the
|
|
source code ("Material") are owned by Intel Corporation or its suppliers or
|
|
licensors. Title to the Material remains with Intel Corporation or its suppliers
|
|
and licensors. The Material may contain trade secrets and proprietary and
|
|
confidential information of Intel Corporation and its suppliers and licensors,
|
|
and is protected by worldwide copyright and trade secret laws and treaty
|
|
provisions. No part of the Material may be used, copied, reproduced, modified,
|
|
published, uploaded, posted, transmitted, distributed, or disclosed in any way
|
|
without Intel's prior express written permission.
|
|
|
|
No license under any patent, copyright, trade secret or other intellectual
|
|
property right is granted to or conferred upon you by disclosure or delivery
|
|
of the Materials, either expressly, by implication, inducement, estoppel or
|
|
otherwise. Any license under such intellectual property rights must be
|
|
express and approved by Intel in writing.
|
|
|
|
Unless otherwise agreed by Intel in writing, you may not remove or alter
|
|
this notice or any other notice embedded in Materials by Intel or
|
|
Intel's suppliers or licensors in any way.
|
|
|
|
This file contains a 'Sample Driver' and is licensed as such under the terms
|
|
of your license agreement with Intel or your vendor. This file may be modified
|
|
by the user, subject to the additional terms of the license agreement.
|
|
|
|
@par Specification Reference:
|
|
**/
|
|
|
|
#include "CrashLogDxe.h"
|
|
|
|
///
|
|
/// Boot Error Record Table (BERT) instance
|
|
///
|
|
STATIC EFI_ACPI_6_1_BOOT_ERROR_RECORD_TABLE_HEADER *mBert;
|
|
|
|
STATIC EFI_ACPI_TABLE_PROTOCOL *mAcpiTbl = NULL;
|
|
STATIC BOOLEAN mPmcCrashLogSupport = FALSE;
|
|
STATIC BOOLEAN mPmcCrashLogPresent = FALSE;
|
|
STATIC BOOLEAN mCpuCrashLogSupport = FALSE;
|
|
STATIC BOOLEAN mCpuCrashLogPresent = FALSE;
|
|
STATIC UINT32 mMainLogDataBuffer = 0;
|
|
STATIC UINT32 mTelemetryDataBuffer = 0;
|
|
STATIC UINT32 mTraceDataBuffer = 0;
|
|
STATIC UINT32 mPmcCrashLogSize = 0;
|
|
STATIC UINT32 mCpuCrashLogSize = 0;
|
|
STATIC UINT32 mMainLogSize = 0;
|
|
STATIC UINT32 mTelemetrySize = 0;
|
|
STATIC UINT32 mTraceSize = 0;
|
|
|
|
EFI_GUID gCrashLog_RecordId_Guid = CRASHLOG_RECORDID_GUID;
|
|
EFI_GUID gTelemetry_RecordId_Guid = TELEMETRY_RECORDID_GUID;
|
|
EFI_GUID gTrace_RecordId_Guid = TRACE_RECORDID_GUID;
|
|
|
|
/**
|
|
|
|
Update Platform & OEM ID's in Whea ACPI tables
|
|
|
|
@param[in] TableHeader BERT Table Header Pointer
|
|
|
|
**/
|
|
VOID
|
|
UpdateAcpiTableIds (
|
|
EFI_ACPI_DESCRIPTION_HEADER *TableHeader
|
|
)
|
|
{
|
|
//
|
|
// Update the OEMID, OEM revision, OEM TABLE ID, Creator ID and Creator revision
|
|
//
|
|
*(UINT32 *)(TableHeader->OemId) = 'I' + ('N' << 8) + ('T' << 16) + ('E' << 24);
|
|
*(UINT16 *)(TableHeader->OemId + 4) = 'L' + (' ' << 8);
|
|
|
|
TableHeader->OemTableId = EFI_ACPI_OEM_TABLE_ID;
|
|
TableHeader->OemRevision = EFI_WHEA_OEM_REVISION;
|
|
TableHeader->CreatorId = EFI_ACPI_CREATOR_ID;
|
|
TableHeader->CreatorRevision = EFI_ACPI_CREATOR_REVISION;
|
|
}
|
|
|
|
/**
|
|
Get PMC Crash log
|
|
|
|
@param[in] Destination Memory address of GEBE entry
|
|
@param[in] PmcCrashLogHob Crash log data HOB
|
|
|
|
@retval EFI_SUCCESS The function completes successfully
|
|
|
|
**/
|
|
EFI_STATUS
|
|
GetPmcCrashLog (
|
|
IN UINT32 *Destination,
|
|
IN CRASHLOG_HOB *PmcCrashLogHob
|
|
)
|
|
{
|
|
UINT32 *PmcCrashLogAddr;
|
|
UINT32 PmcCrashLogSize = 0;
|
|
|
|
DEBUG ((DEBUG_INFO, "BERT Pmc CrashLog Destination = 0x%x \n", Destination));
|
|
DEBUG ((DEBUG_INFO, "Value at PmcCrashLogAddress 0x%x = 0x%x \n", PmcCrashLogHob->AllocateAddress, MmioRead32 (PmcCrashLogHob->AllocateAddress)));
|
|
|
|
DEBUG ((DEBUG_INFO, "Pmc Crash Data Collection\n"));
|
|
|
|
PmcCrashLogAddr = (UINT32 *) PmcCrashLogHob->AllocateAddress;
|
|
while (PmcCrashLogSize < (PmcCrashLogHob->Size)) {
|
|
*Destination = *PmcCrashLogAddr;
|
|
if (PmcCrashLogSize < CRASHLOG_SIZE_DEBUG_PURPOSE) { // Dumping only few bytes to help debug
|
|
DEBUG ((DEBUG_INFO, "PmcCrashData = 0x%x\n", *Destination));
|
|
}
|
|
Destination++;
|
|
PmcCrashLogAddr++;
|
|
PmcCrashLogSize += 4;
|
|
}
|
|
|
|
DEBUG ((DEBUG_INFO, "Copied Pmc CrasLog Size = 0x%x\n", PmcCrashLogSize));
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Get Cpu Crash log
|
|
|
|
@retval EFI_SUCCESS The function completes successfully
|
|
|
|
**/
|
|
EFI_STATUS
|
|
GetCpuCrashLog (
|
|
IN UINT32 *MainLogDestination,
|
|
IN UINT32 *TelemetryDestination,
|
|
IN UINT32 *TraceDestination
|
|
)
|
|
{
|
|
UINT32 *CrashLogAddr = NULL;
|
|
UINT32 CrashLogSize = 0;
|
|
|
|
if (mCpuCrashLogSize > 0) {
|
|
DEBUG ((DEBUG_INFO, "Cpu CrashLog is present.\n"));
|
|
} else {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
if ((MainLogDestination == NULL) || (TelemetryDestination == NULL) || (TraceDestination == NULL)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
DEBUG ((DEBUG_INFO, "Cpu Crash Data Collection\n"));
|
|
|
|
CrashLogAddr = (UINT32 *) (UINTN) mMainLogDataBuffer;
|
|
DEBUG ((DEBUG_INFO, "Value at MainCrashLogAddress 0x%x = 0x%x \n ", (UINTN)CrashLogAddr, (UINT32) *CrashLogAddr));
|
|
while (CrashLogSize < (mMainLogSize)) {
|
|
*MainLogDestination = *CrashLogAddr;
|
|
if (CrashLogSize < CRASHLOG_SIZE_DEBUG_PURPOSE) { // Dumping only few bytes to help debug
|
|
DEBUG ((DEBUG_INFO, "Main CrashData = 0x%x\n", *MainLogDestination));
|
|
}
|
|
MainLogDestination++;
|
|
CrashLogAddr++;
|
|
CrashLogSize+=4;
|
|
}
|
|
DEBUG ((DEBUG_INFO, "Copied Main CrasLog Size = 0x%x\n", CrashLogSize));
|
|
|
|
CrashLogSize = 0;
|
|
CrashLogAddr = (UINT32 *) (UINTN) mTelemetryDataBuffer;
|
|
DEBUG ((DEBUG_INFO, "Value at TelemetryCrashLogAddress 0x%x = 0x%x \n ", (UINTN)CrashLogAddr, (UINT32) *CrashLogAddr));
|
|
while (CrashLogSize < (mTelemetrySize)) {
|
|
*TelemetryDestination = *CrashLogAddr;
|
|
if (CrashLogSize < CRASHLOG_SIZE_DEBUG_PURPOSE) { // Dumping only few bytes to help debug
|
|
DEBUG ((DEBUG_INFO, "Telemetry CrashData = 0x%x\n", *TelemetryDestination));
|
|
}
|
|
TelemetryDestination++;
|
|
CrashLogAddr++;
|
|
CrashLogSize+=4;
|
|
}
|
|
DEBUG ((DEBUG_INFO, "Copied Telemetry CrasLog Size = 0x%x\n", CrashLogSize));
|
|
|
|
CrashLogSize = 0;
|
|
CrashLogAddr = (UINT32 *) (UINTN) mTraceDataBuffer;
|
|
DEBUG ((DEBUG_INFO, "Value at TraceCrashLogAddress 0x%x = 0x%x \n ", (UINTN)CrashLogAddr, (UINT32) *CrashLogAddr));
|
|
while (CrashLogSize < (mTraceSize)) {
|
|
*TraceDestination = *CrashLogAddr;
|
|
if (CrashLogSize < CRASHLOG_SIZE_DEBUG_PURPOSE) { // Dumping only few bytes to help debug
|
|
DEBUG ((DEBUG_INFO, "Trace CrashData = 0x%x\n", *TraceDestination));
|
|
}
|
|
TraceDestination++;
|
|
CrashLogAddr++;
|
|
CrashLogSize+=4;
|
|
}
|
|
DEBUG ((DEBUG_INFO, "Copied Trace CrasLog Size = 0x%x\n", CrashLogSize));
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
|
|
Add a new Firmware CrashLog Entry to existing Error Status Block.
|
|
|
|
@param[in] ErrStsBlk BERT GENERIC_ERROR_STATUS instance.
|
|
@param[in out] FirmwareGenericErrorAddr Pointer to this FirmwareGenericError entry address, updated on return.
|
|
@param[in out] FirmwareCrashLogPayloadAddr Pointer to Firmware CrashLog Entry payload address, updated on return.
|
|
@param[in] EntrySize Firmware CrashLog Entry payload size.
|
|
@param[in] EntrySource Firmware CrashLog Entry source.
|
|
@param[in] EntryVersion Firmware CrashLog Entry version.
|
|
|
|
**/
|
|
|
|
VOID
|
|
AddFirmwareCrashLogEntry (
|
|
IN EFI_ACPI_6_1_GENERIC_ERROR_STATUS_STRUCTURE *ErrStsBlk,
|
|
IN OUT FIRMWARE_GENERIC_ERROR **FirmwareGenericErrorAddr,
|
|
IN OUT UINT8 **FirmwareCrashLogPayloadAddr,
|
|
IN UINT32 EntrySize,
|
|
IN EFI_GUID RecordIdGuid
|
|
)
|
|
{
|
|
FIRMWARE_GENERIC_ERROR *FirmwareGenericError = NULL;
|
|
|
|
FirmwareGenericError = (FIRMWARE_GENERIC_ERROR*)((UINT8*)(void*)ErrStsBlk + ErrStsBlk->DataLength + sizeof (EFI_ACPI_6_1_GENERIC_ERROR_STATUS_STRUCTURE));
|
|
ZeroMem (FirmwareGenericError, sizeof (FIRMWARE_GENERIC_ERROR));
|
|
|
|
CopyMem (&FirmwareGenericError->GenericErrorDataEntry.SectionType, &gEfiFirmwareErrorSectionGuid, sizeof (EFI_GUID));
|
|
FirmwareGenericError->GenericErrorDataEntry.ErrorSeverity = EFI_ACPI_6_1_ERROR_SEVERITY_FATAL;
|
|
FirmwareGenericError->GenericErrorDataEntry.Revision = EFI_ACPI_6_1_GENERIC_ERROR_DATA_ENTRY_REVISION;
|
|
FirmwareGenericError->GenericErrorDataEntry.ValidationBits = 0;
|
|
FirmwareGenericError->GenericErrorDataEntry.ErrorDataLength = EntrySize + sizeof (EFI_FIRMWARE_ERROR_DATA);
|
|
FirmwareGenericError->EfiFirmwareErrorData.ErrorType = 2; //FW CrashLog Error Record
|
|
FirmwareGenericError->EfiFirmwareErrorData.Revision = EFI_2_7_FIRMWARE_ERROR_RECORD_REVISION;
|
|
FirmwareGenericError->EfiFirmwareErrorData.RecordIdGuid = RecordIdGuid;
|
|
|
|
ErrStsBlk->DataLength += FirmwareGenericError->GenericErrorDataEntry.ErrorDataLength + sizeof (EFI_ACPI_6_1_GENERIC_ERROR_DATA_ENTRY_STRUCTURE);
|
|
ErrStsBlk->BlockStatus.ErrorDataEntryCount++;
|
|
|
|
*FirmwareGenericErrorAddr = FirmwareGenericError;
|
|
*FirmwareCrashLogPayloadAddr = (UINT8*)(void*) FirmwareGenericError + sizeof (FIRMWARE_GENERIC_ERROR);
|
|
}
|
|
|
|
/**
|
|
Get PMC, PUNIT, CPU Crash Log, and initialize the APEI BERT GENERIC_ERROR_STATUS structure
|
|
|
|
@param[in] ErrStsBlk BERT GENERIC_ERROR_STATUS instance.
|
|
|
|
@retval EFI_SUCCESS The function completes successfully
|
|
**/
|
|
EFI_STATUS
|
|
GenFwBootErrorlog (
|
|
IN EFI_ACPI_6_1_GENERIC_ERROR_STATUS_STRUCTURE *ErrStsBlk
|
|
)
|
|
{
|
|
EFI_STATUS Status = EFI_SUCCESS;
|
|
FIRMWARE_GENERIC_ERROR *MainFirmwareGenericError = NULL;
|
|
FIRMWARE_GENERIC_ERROR *TelemetryFirmwareGenericError = NULL;
|
|
FIRMWARE_GENERIC_ERROR *TraceFirmwareGenericError = NULL;
|
|
FIRMWARE_GENERIC_ERROR *PmcFirmwareGenericError = NULL;
|
|
UINT8 *PmcDest;
|
|
UINT8 *MainDest = NULL;
|
|
UINT8 *TelemetryDest = NULL;
|
|
UINT8 *TraceDest = NULL;
|
|
EFI_HOB_GUID_TYPE *CrashLogDataBufferHob;
|
|
CRASHLOG_HOB *PmcCrashLogHob;
|
|
UINT32 HobDataSize;
|
|
UINT32 Index;
|
|
|
|
if (ErrStsBlk == NULL) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
ErrStsBlk->RawDataOffset = 0;
|
|
ErrStsBlk->RawDataLength = 0;
|
|
ErrStsBlk->ErrorSeverity = EFI_ACPI_6_1_ERROR_SEVERITY_FATAL;
|
|
ErrStsBlk->BlockStatus.ErrorDataEntryCount = 0;
|
|
ErrStsBlk->DataLength = 0;
|
|
|
|
//
|
|
// Getting Cpu Crashlog and adding entry
|
|
//
|
|
if (mCpuCrashLogSupport && mCpuCrashLogPresent) {
|
|
//
|
|
// MainLog crashlog data is presented then create GEBE for MainLog , Telemetry and Trace
|
|
//
|
|
if (mMainLogSize) {
|
|
AddFirmwareCrashLogEntry (ErrStsBlk, &MainFirmwareGenericError, &MainDest, mMainLogSize, gCrashLog_RecordId_Guid);
|
|
DEBUG ((DEBUG_INFO, "ErrStsBlk = 0x%x, MainFirmwareGenericError = 0x%x, MainDest = 0x%x, mMainLogSize = 0x%x\n", ErrStsBlk, MainFirmwareGenericError, MainDest, mMainLogSize));
|
|
|
|
if (mTelemetrySize) {
|
|
AddFirmwareCrashLogEntry (ErrStsBlk, &TelemetryFirmwareGenericError, &TelemetryDest, mTelemetrySize, gTelemetry_RecordId_Guid);
|
|
DEBUG ((DEBUG_INFO, "ErrStsBlk = 0x%x, TelemetryFirmwareGenericError = 0x%x, TelemetryDest = 0x%x, mTelemetrySize = 0x%x\n", ErrStsBlk, TelemetryFirmwareGenericError, TelemetryDest, mTelemetrySize));
|
|
}
|
|
|
|
if (mTraceSize) {
|
|
AddFirmwareCrashLogEntry (ErrStsBlk, &TraceFirmwareGenericError, &TraceDest, mTraceSize, gTrace_RecordId_Guid);
|
|
DEBUG ((DEBUG_INFO, "ErrStsBlk = 0x%x, TraceFirmwareGenericError = 0x%x, TraceDest = 0x%x, mTraceSize = 0x%x\n", ErrStsBlk, TraceFirmwareGenericError, TraceDest, mTraceSize));
|
|
}
|
|
}
|
|
|
|
Status = GetCpuCrashLog ((void*) MainDest, (void*) TelemetryDest, (void*) TraceDest);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "CPU CrashLog data are not collected\n"));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Getting Pmc Crashlog and adding entry
|
|
//
|
|
if (mPmcCrashLogSupport && mPmcCrashLogPresent ) {
|
|
CrashLogDataBufferHob = GetFirstGuidHob (&gPmcCrashLogDataBufferHobGuid);
|
|
PmcCrashLogHob = (CRASHLOG_HOB *) GET_GUID_HOB_DATA (CrashLogDataBufferHob);
|
|
HobDataSize = GET_GUID_HOB_DATA_SIZE (CrashLogDataBufferHob) / sizeof (CRASHLOG_HOB);
|
|
|
|
for (Index = 0; Index < HobDataSize; Index++) {
|
|
//
|
|
// Pmc crashlog data is presented then create GEBE for PmcCrashLog
|
|
//
|
|
AddFirmwareCrashLogEntry (ErrStsBlk, &PmcFirmwareGenericError, &PmcDest, PmcCrashLogHob[Index].Size, gCrashLog_RecordId_Guid);
|
|
DEBUG ((DEBUG_INFO, "ErrStsBlk = 0x%x, PmcFirmwareGenericError = 0x%x, PmcDest = 0x%x, PmcCrashLogHob[%d].Size = 0x%x\n", ErrStsBlk, PmcFirmwareGenericError, PmcDest, Index, PmcCrashLogHob[Index].Size));
|
|
Status = GetPmcCrashLog ((void*) PmcDest, &(PmcCrashLogHob[Index]));
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "PMC CrashLog data are not collected\n"));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mPmcCrashLogSupport == FALSE && mCpuCrashLogSupport == FALSE) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
ErrStsBlk->BlockStatus.MultipleUncorrectableErrors = 1;
|
|
ErrStsBlk->BlockStatus.UncorrectableErrorValid = 1;
|
|
|
|
mBert->BootErrorRegionLength = ErrStsBlk->DataLength + sizeof (EFI_ACPI_6_1_GENERIC_ERROR_STATUS_STRUCTURE);
|
|
|
|
DEBUG ((DEBUG_INFO, " ErrStsBlk->DataLength = 0x%x\n", ErrStsBlk->DataLength));
|
|
DEBUG ((DEBUG_INFO, " ErrorStatusBlock Size = 0x%x\n", mBert->BootErrorRegionLength));
|
|
if (PmcFirmwareGenericError != NULL) {
|
|
DEBUG((DEBUG_INFO, " PmcFirmwareGenericError->GenericErrorDataEntry.ErrorDataLength = 0x%x\n", PmcFirmwareGenericError->GenericErrorDataEntry.ErrorDataLength));
|
|
}
|
|
DEBUG ((DEBUG_INFO, " PmcCrashLogSize = 0x%x\n", mPmcCrashLogSize));
|
|
DEBUG ((DEBUG_INFO, " CpuCrashLogSize = 0x%x\n", mCpuCrashLogSize));
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Log firmware boot error log in APEI BERT.
|
|
|
|
@param[in] BootErrorTable APEI BERT address.
|
|
|
|
@retval EFI_SUCCESS The function completes successfully
|
|
**/
|
|
EFI_STATUS
|
|
LogFwBootErrorlog (
|
|
IN EFI_ACPI_6_1_BOOT_ERROR_RECORD_TABLE_HEADER *BootErrorTable
|
|
)
|
|
{
|
|
EFI_ACPI_6_1_GENERIC_ERROR_STATUS_STRUCTURE *ErrStsBlk;
|
|
|
|
if (BootErrorTable == NULL) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
ErrStsBlk = (EFI_ACPI_6_1_GENERIC_ERROR_STATUS_STRUCTURE *) (UINTN) BootErrorTable->BootErrorRegion;
|
|
if (ErrStsBlk->DataLength >= BootErrorTable->BootErrorRegionLength) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
return GenFwBootErrorlog (ErrStsBlk);
|
|
}
|
|
|
|
/**
|
|
Install ACPI table for APEI BERT
|
|
|
|
@param[in] Bert APEI BERT Instance.
|
|
|
|
@retval EFI_SUCCESS The function completes successfully
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
InstallApeiBertTables (
|
|
EFI_ACPI_6_1_BOOT_ERROR_RECORD_TABLE_HEADER *Bert
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN TableKey;
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
if (Bert == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
|
|
if (mAcpiTbl) {
|
|
DEBUG ((DEBUG_INFO, "APEI Support. Install BERT Table \n"));
|
|
TableKey = 0;
|
|
Status = mAcpiTbl->InstallAcpiTable (
|
|
mAcpiTbl,
|
|
Bert,
|
|
Bert->Header.Length,
|
|
&TableKey
|
|
);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Get Crash Log size.
|
|
|
|
@param[in] CrashLogDataBufferHob Crash log data HOB
|
|
|
|
@retval PmcCrashLogSize Total size of all crash log data.
|
|
**/
|
|
UINT32
|
|
GetPmcCrashLogSize (
|
|
IN EFI_HOB_GUID_TYPE *CrashLogDataBufferHob
|
|
)
|
|
{
|
|
UINT32 Index;
|
|
UINT32 HobDataSize;
|
|
UINT32 PmcCrashLogSize;
|
|
CRASHLOG_HOB *PmcCrashLogHob;
|
|
|
|
PmcCrashLogSize = 0;
|
|
|
|
while (CrashLogDataBufferHob != NULL) {
|
|
PmcCrashLogHob = (CRASHLOG_HOB *) GET_GUID_HOB_DATA (CrashLogDataBufferHob);
|
|
HobDataSize = GET_GUID_HOB_DATA_SIZE (CrashLogDataBufferHob) / sizeof (CRASHLOG_HOB);
|
|
for (Index = 0; Index < HobDataSize; Index++) {
|
|
PmcCrashLogSize += PmcCrashLogHob[Index].Size;
|
|
}
|
|
CrashLogDataBufferHob = GET_NEXT_HOB (CrashLogDataBufferHob);
|
|
CrashLogDataBufferHob = GetNextGuidHob (&gPmcCrashLogDataBufferHobGuid, CrashLogDataBufferHob);
|
|
}
|
|
|
|
return PmcCrashLogSize;
|
|
}
|
|
|
|
/**
|
|
Determine Crash Log support or not.
|
|
|
|
@param[in] DiscoveryBuffer Discovery buffer read from PMC
|
|
**/
|
|
BOOLEAN
|
|
GetPmcCrashLogSupport (
|
|
IN PMC_IPC_DISCOVERY_BUF *DiscoveryBuffer
|
|
)
|
|
{
|
|
if (DiscoveryBuffer->Bits.Avail != 1) {
|
|
return FALSE;
|
|
}
|
|
|
|
switch (DiscoveryBuffer->Bits64.Mech) {
|
|
case CRASHLOG_MECH_LEGACY:
|
|
if (DiscoveryBuffer->Bits.Dis == 1) {
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case CRASHLOG_MECH_DESCRIPTOR_TABLE:
|
|
if (DiscoveryBuffer->Bits64.CrashDisSts) {
|
|
return FALSE;
|
|
}
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
Discover CrashLog
|
|
|
|
@retval TRUE if CPU or PMC crash log is discovered.
|
|
@retval FALSE if none of CPU and PMC crash logs are discovered.
|
|
|
|
**/
|
|
BOOLEAN
|
|
DiscoverCrashLog (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
PMC_IPC_DISCOVERY_BUF DiscoveryBuffer;
|
|
TEL_CRASHLOG_DEVSC_CAP CrashLogDevscCap;
|
|
BOOLEAN CrashLogSupport;
|
|
|
|
Status = EFI_SUCCESS;
|
|
CrashLogSupport = FALSE;
|
|
ZeroMem (&DiscoveryBuffer, sizeof (PMC_IPC_DISCOVERY_BUF));
|
|
|
|
//
|
|
// PCH CrashLog Discovery
|
|
//
|
|
Status = PmcCrashLogDiscovery (&DiscoveryBuffer);
|
|
|
|
if (EFI_ERROR (Status) || (GetPmcCrashLogSupport (&DiscoveryBuffer) == FALSE)) {
|
|
DEBUG ((DEBUG_INFO, "PCH CrashLog feature not supported\n"));
|
|
} else {
|
|
mPmcCrashLogSupport = TRUE;
|
|
CrashLogSupport = TRUE;
|
|
}
|
|
|
|
//
|
|
// CPU CrashLog Discovery
|
|
//
|
|
Status = GetCpuCrashLogCapability (&CrashLogDevscCap);
|
|
|
|
if (EFI_ERROR (Status) ) {
|
|
DEBUG ((DEBUG_INFO, "CPU CrashLog feature not supported\n"));
|
|
} else {
|
|
mCpuCrashLogSupport = TRUE;
|
|
CrashLogSupport = TRUE;
|
|
}
|
|
|
|
return CrashLogSupport;
|
|
}
|
|
|
|
/**
|
|
Entry point of the CrashLog support driver.
|
|
|
|
@param[in] ImageHandle EFI_HANDLE: A handle for the image that is initializing this driver
|
|
@param[in] SystemTable EFI_SYSTEM_TABLE: A pointer to the EFI system table
|
|
|
|
@retval EFI_SUCCESS: Driver initialized successfully
|
|
EFI_LOAD_ERROR: Failed to Initialize or to Load the driver
|
|
EFI_OUT_OF_RESOURCES: Could not allocate needed resources
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
InstallCrashLogSupport (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
SETUP_DATA SetupData;
|
|
UINTN VariableSize;
|
|
UINT32 VariableAttributes;
|
|
BOOLEAN CreateBertTableOrNot;
|
|
EFI_HOB_GUID_TYPE *CrashLogDataBufferHob;
|
|
UINT32 BootErrorRegionSize;
|
|
CRASHLOG_HOB *PmcCrashLogHob;
|
|
CPU_CRASHLOG_HOB *CpuCrashLogHob;
|
|
UINT32 CrashLogCount = 0;
|
|
|
|
CreateBertTableOrNot = FALSE;
|
|
VariableSize = sizeof (SETUP_DATA);
|
|
|
|
DEBUG ((DEBUG_INFO, "CrashLog Entry Point\n"));
|
|
Status = gRT->GetVariable (
|
|
L"Setup",
|
|
&gSetupVariableGuid,
|
|
&VariableAttributes,
|
|
&VariableSize,
|
|
&SetupData
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
if (!SetupData.EnableCrashLog) {
|
|
//
|
|
// The PCH CrashLog Disable.
|
|
//
|
|
DEBUG ((DEBUG_INFO, "Disable PCH CrashLog Feature\n"));
|
|
Status = PmcCrashLogDisable ();
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Check for the CrashLog feature, if it is available or not.
|
|
//
|
|
CreateBertTableOrNot = DiscoverCrashLog ();
|
|
if (!CreateBertTableOrNot) {
|
|
DEBUG ((DEBUG_ERROR, "DiscoverCrashLog: CrashLog is not supported \n"));
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
if (mPmcCrashLogSupport) {
|
|
if (SetupData.CrashLogOnAllReset) {
|
|
DEBUG ((DEBUG_INFO, "Crash Log on All Reset \n"));
|
|
//
|
|
// Collect crash data on every boot
|
|
//
|
|
Status = PmcCrashLogOnAllReset ();
|
|
DEBUG ((DEBUG_INFO, "PMC IPC to collect Crash Log on every reset successful\n"));
|
|
}
|
|
}
|
|
|
|
DEBUG ((DEBUG_INFO, "BERT Init\n"));
|
|
|
|
CrashLogDataBufferHob = GetFirstGuidHob (&gPmcCrashLogDataBufferHobGuid);
|
|
if (CrashLogDataBufferHob != NULL) {
|
|
PmcCrashLogHob = (CRASHLOG_HOB *) GET_GUID_HOB_DATA (CrashLogDataBufferHob);
|
|
mPmcCrashLogSize = GetPmcCrashLogSize (CrashLogDataBufferHob);
|
|
if ((PmcCrashLogHob != NULL) && (mPmcCrashLogSize > 0)){
|
|
DEBUG ((DEBUG_ERROR, "PMC CrashLog data is present\n"));
|
|
DEBUG ((DEBUG_INFO, "PmcCrashLogDataBuffer = 0x%x\n", PmcCrashLogHob));
|
|
DEBUG ((DEBUG_INFO, "PMC CrashLog Size = 0x%x\n", mPmcCrashLogSize));
|
|
mPmcCrashLogPresent = TRUE;
|
|
CrashLogCount += GET_GUID_HOB_DATA_SIZE (CrashLogDataBufferHob) / sizeof (CRASHLOG_HOB);
|
|
}
|
|
} else {
|
|
DEBUG ((DEBUG_ERROR, "PMC CrashLog data is not present \n"));
|
|
mPmcCrashLogPresent = FALSE;
|
|
}
|
|
|
|
CrashLogDataBufferHob = GetFirstGuidHob (&gCpuCrashLogDataBufferHobGuid);
|
|
if (CrashLogDataBufferHob != NULL) {
|
|
CpuCrashLogHob = (CPU_CRASHLOG_HOB *) GET_GUID_HOB_DATA (CrashLogDataBufferHob);
|
|
mMainLogDataBuffer = (UINT32) (CpuCrashLogHob->Main.AllocateAddress);
|
|
mTelemetryDataBuffer = (UINT32) (CpuCrashLogHob->Telemetry.AllocateAddress);
|
|
mTraceDataBuffer = (UINT32) (CpuCrashLogHob->Trace.AllocateAddress);
|
|
mMainLogSize = CpuCrashLogHob->Main.Size;
|
|
mTelemetrySize = CpuCrashLogHob->Telemetry.Size;
|
|
mTraceSize = CpuCrashLogHob->Trace.Size;
|
|
mCpuCrashLogSize = mMainLogSize + mTelemetrySize + mTraceSize;
|
|
DEBUG ((DEBUG_INFO, "MainLogDataBuffer = 0x%x\n", mMainLogDataBuffer));
|
|
DEBUG ((DEBUG_INFO, "MainLogSize = 0x%x\n", mMainLogSize));
|
|
DEBUG ((DEBUG_INFO, "TelemetryDataBuffer = 0x%x\n", mTelemetryDataBuffer));
|
|
DEBUG ((DEBUG_INFO, "TelemetrySize = 0x%x\n", mTelemetrySize));
|
|
DEBUG ((DEBUG_INFO, "TraceDataBuffer = 0x%x\n", mTraceDataBuffer));
|
|
DEBUG ((DEBUG_INFO, "TraceSize = 0x%x\n", mTraceSize));
|
|
DEBUG ((DEBUG_INFO, "CpuCrashLogSize = 0x%x\n", mCpuCrashLogSize));
|
|
}
|
|
|
|
if (mMainLogSize) {
|
|
DEBUG ((DEBUG_ERROR, "Cpu CrashLog data is present\n"));
|
|
mCpuCrashLogPresent = TRUE;
|
|
CrashLogCount++;
|
|
if (mTelemetrySize) {
|
|
CrashLogCount++;
|
|
}
|
|
if (mTraceSize) {
|
|
CrashLogCount++;
|
|
}
|
|
} else {
|
|
DEBUG ((DEBUG_ERROR, "Cpu CrashLog data is not present\n"));
|
|
mCpuCrashLogPresent = FALSE;
|
|
}
|
|
|
|
if ((mCpuCrashLogPresent == FALSE) && (mPmcCrashLogPresent == FALSE)) {
|
|
DEBUG ((DEBUG_ERROR, "CrashLog is not present. Skip BERT creation \n"));
|
|
return EFI_NOT_READY;
|
|
}
|
|
|
|
//
|
|
// Allocate memory for BERT table
|
|
//
|
|
mBert = (EFI_ACPI_6_1_BOOT_ERROR_RECORD_TABLE_HEADER *) AllocateZeroPool (sizeof (EFI_ACPI_6_1_BOOT_ERROR_RECORD_TABLE_HEADER));
|
|
|
|
if (mBert == NULL) {
|
|
DEBUG ((DEBUG_ERROR, "Unable to Allocate Memory for BERT Record Creation\n"));
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Fill EFI_ACPI_DESCRIPTION_HEADER structure
|
|
//
|
|
mBert->Header.Signature = EFI_ACPI_6_1_BOOT_ERROR_RECORD_TABLE_SIGNATURE;
|
|
mBert->Header.Length = sizeof (EFI_ACPI_6_1_BOOT_ERROR_RECORD_TABLE_HEADER);
|
|
mBert->Header.Revision = EFI_ACPI_6_1_BOOT_ERROR_RECORD_TABLE_REVISION;
|
|
mBert->Header.Checksum = 0;
|
|
UpdateAcpiTableIds (&mBert->Header);
|
|
//
|
|
// Allocate memory space Error status blocks
|
|
// Allocate memort space for Errorlog Address range, Fill EFI_ACPI_6_1_BOOT_ERROR_RECORD_TABLE_HEADER(mBert) structure
|
|
//
|
|
BootErrorRegionSize = sizeof (EFI_ACPI_6_1_GENERIC_ERROR_STATUS_STRUCTURE) + sizeof (FIRMWARE_GENERIC_ERROR) * CrashLogCount + mPmcCrashLogSize + mCpuCrashLogSize;
|
|
mBert->BootErrorRegion = (UINT64) AllocateReservedZeroPool ((UINTN) BootErrorRegionSize);
|
|
|
|
DEBUG ((DEBUG_INFO, "mBert->BootErrorRegion = %x\n", (UINTN)mBert->BootErrorRegion));
|
|
DEBUG ((DEBUG_INFO, "mBert->BootErrorRegionSize = %x\n", BootErrorRegionSize));
|
|
|
|
if (mBert->BootErrorRegion == 0) {
|
|
DEBUG ((DEBUG_ERROR, "Unable to Allocate Memory for Boot Error Record Data\n"));
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
mBert->BootErrorRegionLength = BootErrorRegionSize;
|
|
|
|
Status = LogFwBootErrorlog (mBert);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_INFO, "No Pmc or Punit or Cpu error log found\n"));
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
mAcpiTbl = NULL;
|
|
Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL, (VOID **) &mAcpiTbl);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
Status = InstallApeiBertTables (mBert);
|
|
if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
|
|
DEBUG ((DEBUG_ERROR, "Not able to install ACPI table for APEI BERT \n"));
|
|
}
|
|
|
|
if (mBert != NULL) {
|
|
FreePool (mBert);
|
|
}
|
|
|
|
DEBUG ((DEBUG_INFO, "CrashLog Exit \n"));
|
|
return Status;
|
|
}
|