/** @file This code install AIP protocol to report MRC health data to expose through ACPI PHAT for Telemetry. @copyright INTEL CONFIDENTIAL Copyright 2019 - 2020 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 an 'Intel Peripheral Driver' and is uniquely identified as "Intel Reference Module" and is licensed for Intel CPUs and chipsets under the terms of your license agreement with Intel or your vendor. This file may be modified by the user, subject to additional terms of the license agreement. @par Specification Reference: **/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /// /// Ensure proper structure formats /// #pragma pack(push, 1) /// /// BIOS Data ACPI structure /// typedef struct { EFI_ACPI_DESCRIPTION_HEADER Header; EFI_ACPI_3_0_GENERIC_ADDRESS_STRUCTURE BdatGas; } EFI_BDAT_ACPI_DESCRIPTION_TABLE; #pragma pack(pop) typedef struct { EFI_HANDLE Handle; EFI_ADAPTER_INFORMATION_PROTOCOL AdapterInfo; EFI_DEVICE_PATH_PROTOCOL *MrcDevicePath; } MRC_HEALTH_DEV; typedef struct { EFI_AIP_HEALTH_HEADER Header; UINT8 Revision; BDAT_STRUCTURE BDAT; } EFI_ADAPTER_INFO_MRC_HEALTH_STATE; #define EFI_BDAT_TABLE_SIGNATURE SIGNATURE_32 ('B', 'D', 'A', 'T') /// /// structure Revision (as defined in Telemetry Gen 2.0 spec.) /// #define MRC_AIP_HEADER_REVISION 0x01 #define MRC_AIP_DATA_REVISION 0x01 /** This function gets the MRC health data. @param[in] This A pointer to the EFI_ADAPTER_INFORMATION_PROTOCOL instance. @param[in] InformationType A pointer to gAdapterInfoMrcGuid that defines the contents of InformationBlock. The caller must use the InformationType to specify the infromation it needs to retrieve from this service and to determine how to parse the InformationBlock. The driver should not attempt to free InformationType. @param[out] InformationBlock A pointer to the number of GUIDs present in InfoTypesBuffer. @param[out] InformationBlockSize The driver return the size of the InformationBlock. @retval EFI_INVALID_PARAMETER @retval EFI_OUT_OF_RESOURCES @retval EFI_UNSUPPORTED @retval EFI_SUCCESS **/ EFI_STATUS EFIAPI MrcHealthDataAipGetInfo ( IN EFI_ADAPTER_INFORMATION_PROTOCOL *This, IN EFI_GUID *InformationType, OUT VOID **InformationBlock, OUT UINTN *InformationBlockSize ) { EFI_STATUS Status; EFI_ACPI_TABLE_PROTOCOL *AcpiTable; UINTN Handle; UINTN BlockSize; EFI_ADAPTER_INFO_MRC_HEALTH_STATE *HealthDataBlockPtr; EFI_ACPI_DESCRIPTION_HEADER *Table; VOID *BdatBuffer; EFI_BDAT_ACPI_DESCRIPTION_TABLE *BdatAcpiTable; UINTN Index; UINT8 IsHealthy; MEMORY_PLATFORM_DATA_HOB *MemPlatformDataHob; BOOLEAN IsBdatFound; if ((This == NULL) || (InformationBlock == NULL) || (InformationBlockSize == NULL)) { return EFI_INVALID_PARAMETER; } if (!CompareGuid (InformationType, &gAdapterInfoMrcGuid)) { return EFI_UNSUPPORTED; } *InformationBlockSize = 0; *InformationBlock = NULL; IsHealthy = TelemetryUnknown; IsBdatFound = FALSE; BlockSize = sizeof (EFI_ADAPTER_INFO_MRC_HEALTH_STATE); HealthDataBlockPtr = (EFI_ADAPTER_INFO_MRC_HEALTH_STATE *) AllocateZeroPool (BlockSize); if (HealthDataBlockPtr == NULL) { DEBUG ((DEBUG_ERROR, "MrcHealthDataAipGetInfo : Out of resource to allocate for EFI_ADAPTER_INFO_MRC_HEALTH_STATE.\n")); return EFI_OUT_OF_RESOURCES; } // // Getting health Information ... // // // #1. Try to locate ACPI BDAT which is supposed to be installed after MRC. // If BDAT is found, report it. AcpiTable = NULL; Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL, (VOID **) &AcpiTable); if (EFI_ERROR (Status) || (AcpiTable == NULL)) { DEBUG ((DEBUG_ERROR, "MrcHealthDataAipGetInfo : locate gEfiAcpiTableProtocolGuid failure \n")); } Table = NULL; Handle = 0; Status = LocateAcpiTableBySignature ( EFI_BDAT_TABLE_SIGNATURE, &Table, &Handle ); if (EFI_ERROR (Status) || (Table == NULL)) { DEBUG ((DEBUG_ERROR, "MrcHealthDataAipGetInfo : BDAT is not found \n")); } else { IsBdatFound = TRUE; BdatAcpiTable = (EFI_BDAT_ACPI_DESCRIPTION_TABLE *) Table; BdatBuffer = (VOID *) BdatAcpiTable->BdatGas.Address; DEBUG ((DEBUG_INFO, "MrcHealthDataAipGetInfo : BDAT found @ Address %x \n", BdatBuffer)); CopyMem (&(HealthDataBlockPtr->BDAT), BdatBuffer, sizeof (BDAT_STRUCTURE)); } // // Storing MRC Health Data ... // HealthDataBlockPtr->Revision = MRC_AIP_DATA_REVISION; MemPlatformDataHob = GetFirstGuidHob (&gSiMemoryPlatformDataGuid); ASSERT (MemPlatformDataHob != NULL); if (MemPlatformDataHob != NULL) { if (MemPlatformDataHob->Data.MrcBasicMemoryTestPass == FALSE) { IsHealthy = TelemetryAdvisory; } else { IsHealthy = TelemetryHealthy; } } // // Create the data header // HealthDataBlockPtr->Header.Length = sizeof (EFI_ADAPTER_INFO_MRC_HEALTH_STATE); HealthDataBlockPtr->Header.Revision = MRC_AIP_HEADER_REVISION; HealthDataBlockPtr->Header.Healthy = IsHealthy; CopyGuid (&HealthDataBlockPtr->Header.Signature, &gAdapterInfoMrcGuid); // // Print MRC health data block // DEBUG ((DEBUG_INFO, "=== EFI_ADAPTER_INFO_MRC_HEALTH_STATE ==\n")); DEBUG ((DEBUG_INFO, " EFI_AIP_HEALTH_HEADER \n")); DEBUG ((DEBUG_INFO, " Length = 0x%04x \n", HealthDataBlockPtr->Header.Length)); DEBUG ((DEBUG_INFO, " Revision = 0x%02x \n", HealthDataBlockPtr->Header.Revision)); DEBUG ((DEBUG_INFO, " Healthy = 0x%02x \n", HealthDataBlockPtr->Header.Healthy)); DEBUG ((DEBUG_INFO, " Signature = %g \n", HealthDataBlockPtr->Header.Signature)); DEBUG ((DEBUG_INFO, " Revision = 0x%02x \n", HealthDataBlockPtr->Revision)); if (IsBdatFound) { DEBUG ((DEBUG_INFO, " BDAT = 0x%lx \n", HealthDataBlockPtr->BDAT)); DEBUG ((DEBUG_INFO, "==================================\n")); DEBUG ((DEBUG_INFO, " BDAT_STRUCTURE data\n")); DEBUG ((DEBUG_INFO, " BDAT_HEADER_STRUCTURE \n")); DEBUG ((DEBUG_INFO, " BiosDataSignature = ")); for (Index = 0; Index < sizeof (HealthDataBlockPtr->BDAT.Header.BiosDataSignature); Index++) { DEBUG ((DEBUG_INFO, " 0x%02x", HealthDataBlockPtr->BDAT.Header.BiosDataSignature[Index])); } DEBUG ((DEBUG_INFO, "\n")); DEBUG ((DEBUG_INFO, " BiosDataStructureSize = 0x%08x\n", HealthDataBlockPtr->BDAT.Header.BiosDataStructSize)); DEBUG ((DEBUG_INFO, " Crc16 = 0x%04x\n", HealthDataBlockPtr->BDAT.Header.Crc16)); DEBUG ((DEBUG_INFO, " Reserved = 0x%04x\n", HealthDataBlockPtr->BDAT.Header.Reserved)); DEBUG ((DEBUG_INFO, " Version.Rev.Primary = 0x%04x\n", HealthDataBlockPtr->BDAT.Header.Version.Rev.Primary)); DEBUG ((DEBUG_INFO, " Version.Rev.Secondary = 0x%04x\n", HealthDataBlockPtr->BDAT.Header.Version.Rev.Secondary)); DEBUG ((DEBUG_INFO, " Version.Data32 = 0x%08x\n", HealthDataBlockPtr->BDAT.Header.Version.Data32)); DEBUG ((DEBUG_INFO, " OemOffset = 0x%08x\n", HealthDataBlockPtr->BDAT.Header.OemOffset)); DEBUG ((DEBUG_INFO, " Reserved1 = 0x%08x\n", HealthDataBlockPtr->BDAT.Header.Reserved1)); DEBUG ((DEBUG_INFO, " Reserved2 = 0x%08x\n", HealthDataBlockPtr->BDAT.Header.Reserved2)); DEBUG ((DEBUG_INFO, " BDAT_SCHEMA_LIST_STRUCTURE \n")); DEBUG ((DEBUG_INFO, " SchemaListLength = 0x%04x\n", HealthDataBlockPtr->BDAT.Schemas.SchemaListLength)); DEBUG ((DEBUG_INFO, " Reserved1 = 0x%04x\n", HealthDataBlockPtr->BDAT.Schemas.Reserved1)); DEBUG ((DEBUG_INFO, " Time.Year = 0x%04x\n", HealthDataBlockPtr->BDAT.Schemas.Time.Year)); DEBUG ((DEBUG_INFO, " Time.Month = 0x%02x\n", HealthDataBlockPtr->BDAT.Schemas.Time.Month)); DEBUG ((DEBUG_INFO, " Time.Day = 0x%02x\n", HealthDataBlockPtr->BDAT.Schemas.Time.Day)); DEBUG ((DEBUG_INFO, " Time.Hour = 0x%02x\n", HealthDataBlockPtr->BDAT.Schemas.Time.Hour)); DEBUG ((DEBUG_INFO, " Time.Minute = 0x%02x\n", HealthDataBlockPtr->BDAT.Schemas.Time.Minute)); DEBUG ((DEBUG_INFO, " Time.Second = 0x%02x\n", HealthDataBlockPtr->BDAT.Schemas.Time.Second)); DEBUG ((DEBUG_INFO, " Time.Reserved2 = 0x%02x\n", HealthDataBlockPtr->BDAT.Schemas.Time.Reserved2)); } *InformationBlock = HealthDataBlockPtr; *InformationBlockSize = sizeof (EFI_ADAPTER_INFO_MRC_HEALTH_STATE); return EFI_SUCCESS; } /** This function gets a list of Information types for this instance of the protocol. @param[in] *This A pointer to the EFI_ADAPTER_INFORMATION_PROTOCOL instance. @param[out] **InfoTypesBuffer A pointer to the array of InformationType GUID is supported by This. This buffer is allocated by this service, and it is the responsibility of the caller to free it after using it. @param[out] *InfoTypesBufferCount A pointer to the number of GUIDs present in InfoTypesBuffer. @retval EFI_INVALID_PARAMETER @retval EFI_OUT_OF_RESOURCES @retval EFI_SUCCESS **/ EFI_STATUS EFIAPI MrcHealthDataGetSupportedTypes ( IN EFI_ADAPTER_INFORMATION_PROTOCOL *This, OUT EFI_GUID **InfoTypesBuffer, OUT UINTN *InfoTypesBufferCount ) { if ((This == NULL) || (InfoTypesBuffer == NULL) || (InfoTypesBufferCount == NULL)) { return EFI_INVALID_PARAMETER; } *InfoTypesBuffer = AllocateCopyPool (sizeof (gAdapterInfoMrcGuid), &gAdapterInfoMrcGuid); if (*InfoTypesBuffer == NULL) { return EFI_OUT_OF_RESOURCES; } *InfoTypesBufferCount = 1; return EFI_SUCCESS; } /** This function sets the MRC health data. @param[in] *This A pointer to the EFI_ADAPTER_INFORMATION_PROTOCOL instance. @param[in] EFI_GUID *InformationType A pointer to gAdapterInfoMrcGuid that defines the contents of InformationBlock. The caller must use the InformationType to specify the infromation it needs to retrieve from this service and to determine how to parse the InformationBlock. The driver should not attempt to free InformationType. @param[in] VOID **InformationBlock A poointer to the number of GUIDs present in InfoTypesBuffer. @retval EFI_WRITE_PROTECTED **/ EFI_STATUS EFIAPI MrcHealthDataAipSetInfo ( IN EFI_ADAPTER_INFORMATION_PROTOCOL *This, IN EFI_GUID *InformationType, IN VOID *InformationBlock, IN UINTN InformationBlockSize ) { if ((This == NULL) || (InformationBlock == NULL)) { return EFI_INVALID_PARAMETER; } if (!CompareGuid (InformationType, &gAdapterInfoMrcGuid)) { return EFI_UNSUPPORTED; } return EFI_WRITE_PROTECTED;; } /** This function installs EFI_ADAPTER_INFORMATION_PROTOCOL for MRC Health Data. The data is consumed by Telemetry data collector. @retval EFI_SUCCESS The MRC Health data is successfully published in AIP protocol. @retval EFI_OUT_OF_RESOURCES Out of resources to allocate EFI_ADAPTER_INFORMATION_PROTOCOL. **/ EFI_STATUS EFIAPI InitializeMrcHealthData ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; MRC_HEALTH_DEV *MrcHealthPrivate; VENDOR_DEVICE_PATH VendorDeviceNode; EFI_DEVICE_PATH_PROTOCOL *DevicePath; Status = EFI_SUCCESS; DevicePath = NULL; DEBUG ((DEBUG_INFO, "InitializeMrcHealthData Begins\n")); MrcHealthPrivate = (MRC_HEALTH_DEV *) AllocateZeroPool (sizeof (MRC_HEALTH_DEV)); if (MrcHealthPrivate == NULL) { DEBUG ((EFI_D_ERROR, "EFI_OUT_OF_RESOURCES to Allocate MRC_HEALTH_DEV.\n")); return EFI_OUT_OF_RESOURCES; } MrcHealthPrivate->Handle = NULL; MrcHealthPrivate->AdapterInfo.GetInformation = MrcHealthDataAipGetInfo; MrcHealthPrivate->AdapterInfo.SetInformation = MrcHealthDataAipSetInfo; MrcHealthPrivate->AdapterInfo.GetSupportedTypes = MrcHealthDataGetSupportedTypes; ZeroMem (&VendorDeviceNode, sizeof (VENDOR_DEVICE_PATH)); VendorDeviceNode.Header.Type = HARDWARE_DEVICE_PATH; VendorDeviceNode.Header.SubType = HW_VENDOR_DP; CopyGuid (&VendorDeviceNode.Guid, &gAdapterInfoMrcGuid); SetDevicePathNodeLength (&VendorDeviceNode.Header, sizeof (VENDOR_DEVICE_PATH)); DevicePath = AppendDevicePathNode (NULL, (EFI_DEVICE_PATH_PROTOCOL*)&VendorDeviceNode); if (DevicePath == NULL) { Status = EFI_OUT_OF_RESOURCES; goto ERROR; } MrcHealthPrivate->MrcDevicePath = DevicePath; Status = gBS->InstallMultipleProtocolInterfaces ( &MrcHealthPrivate->Handle, &gEfiAdapterInformationProtocolGuid, &MrcHealthPrivate->AdapterInfo, &gEfiDevicePathProtocolGuid, MrcHealthPrivate->MrcDevicePath, NULL ); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "InitializeMrcHealthData: faild to install AIP protocol, %r.\n", Status)); goto ERROR; } DEBUG ((DEBUG_ERROR, "InitializeMrcHealthData Ends\n")); return Status; ERROR: // // On error, clean the Aip service context data, and free the memory allocated. // FreePool (MrcHealthPrivate); if (DevicePath != NULL) { FreePool (DevicePath); } return Status; }