#include "cper.hpp" #include "libcper/Cper.h" #include "common/utils.hpp" #include #include #include #include PHOSPHOR_LOG2_USING; namespace pldm { namespace oem_ampere { // Returns one if two EFI GUIDs are equal, zero otherwise. int guid_equal(EFI_GUID* a, EFI_GUID* b) { // Check top base 3 components. if (a->Data1 != b->Data1 || a->Data2 != b->Data2 || a->Data3 != b->Data3) { return 0; } // Check Data4 array for equality. for (int i = 0; i < 8; i++) { if (a->Data4[i] != b->Data4[i]) { return 0; } } return 1; } static void decodeSecAmpere(void* section, EFI_AMPERE_ERROR_DATA* ampSpecHdr) { std::memcpy(ampSpecHdr, section, sizeof(EFI_AMPERE_ERROR_DATA)); } static void decodeSecArm(void* section, EFI_AMPERE_ERROR_DATA* ampSpecHdr) { int len; EFI_ARM_ERROR_RECORD* proc; EFI_ARM_ERROR_INFORMATION_ENTRY* errInfo; EFI_ARM_CONTEXT_INFORMATION_HEADER* ctxInfo; proc = reinterpret_cast(section); errInfo = reinterpret_cast(proc + 1); len = proc->SectionLength - (sizeof(EFI_ARM_ERROR_RECORD) + proc->ErrInfoNum * (sizeof(EFI_ARM_ERROR_INFORMATION_ENTRY))); if (len < 0) { lg2::error("Section length is too small, section_length {LENGTH}", "LENGTH", static_cast(proc->SectionLength)); return; } ctxInfo = reinterpret_cast( errInfo + proc->ErrInfoNum); for ([[maybe_unused]] const auto& i : std::views::iota(0, static_cast(proc->ContextInfoNum))) { int size = sizeof(EFI_ARM_CONTEXT_INFORMATION_HEADER) + ctxInfo->RegisterArraySize; len -= size; ctxInfo = reinterpret_cast( (long)ctxInfo + size); } if (len > 0) { /* Get Ampere Specific header data */ std::memcpy(ampSpecHdr, (void*)ctxInfo, sizeof(EFI_AMPERE_ERROR_DATA)); } } static void decodeSecPlatformMemory(void* section, EFI_AMPERE_ERROR_DATA* ampSpecHdr) { EFI_PLATFORM_MEMORY_ERROR_DATA* mem = reinterpret_cast(section); if (mem->ErrorType == MEM_ERROR_TYPE_PARITY) { /* IP Type from bit 0 to 11 of TypeId */ ampSpecHdr->TypeId = (ampSpecHdr->TypeId & 0xf800) + ERROR_TYPE_ID_MCU; ampSpecHdr->SubtypeId = SUBTYPE_ID_PARITY; } } static void decodeSecPcie(void* section, EFI_AMPERE_ERROR_DATA* ampSpecHdr) { EFI_PCIE_ERROR_DATA* pcieErr = reinterpret_cast(section); if (pcieErr->ValidFields & CPER_PCIE_VALID_PORT_TYPE) { if (pcieErr->PortType == CPER_PCIE_PORT_TYPE_ROOT_PORT) { ampSpecHdr->SubtypeId = ERROR_SUBTYPE_PCIE_AER_ROOT_PORT; } else { ampSpecHdr->SubtypeId = ERROR_SUBTYPE_PCIE_AER_DEVICE; } } } static void decodeCperSection(const uint8_t* data, long basePos, EFI_AMPERE_ERROR_DATA* ampSpecHdr, EFI_ERROR_SECTION_DESCRIPTOR* secDesc) { long pos = basePos + secDesc->SectionOffset; char* section = new char[secDesc->SectionLength]; std::memcpy(section, &data[pos], secDesc->SectionLength); pos += secDesc->SectionLength; EFI_GUID* ptr = reinterpret_cast(&secDesc->SectionType); if (guid_equal(ptr, &gEfiAmpereErrorSectionGuid)) { lg2::info("RAS Section Type : Ampere Specific"); decodeSecAmpere(section, ampSpecHdr); } else if (guid_equal(ptr, &gEfiArmProcessorErrorSectionGuid)) { lg2::info("RAS Section Type : ARM"); decodeSecArm(section, ampSpecHdr); } else if (guid_equal(ptr, &gEfiPlatformMemoryErrorSectionGuid)) { lg2::info("RAS Section Type : Memory"); decodeSecPlatformMemory(section, ampSpecHdr); } else if (guid_equal(ptr, &gEfiPcieErrorSectionGuid)) { lg2::info("RAS Section Type : PCIE"); decodeSecPcie(section, ampSpecHdr); } else { lg2::error("Section Type is not supported"); } delete[] section; } void decodeCperRecord(const uint8_t* data, size_t /* eventDataSize */, EFI_AMPERE_ERROR_DATA* ampSpecHdr) { EFI_COMMON_ERROR_RECORD_HEADER cperHeader; long pos = sizeof(CommonEventData); long basePos = sizeof(CommonEventData); std::memcpy(&cperHeader, &data[pos], sizeof(EFI_COMMON_ERROR_RECORD_HEADER)); pos += sizeof(EFI_COMMON_ERROR_RECORD_HEADER); EFI_ERROR_SECTION_DESCRIPTOR* secDesc = new EFI_ERROR_SECTION_DESCRIPTOR[cperHeader.SectionCount]; for ([[maybe_unused]] const auto& i : std::views::iota(0, static_cast(cperHeader.SectionCount))) { std::memcpy(&secDesc[i], &data[pos], sizeof(EFI_ERROR_SECTION_DESCRIPTOR)); pos += sizeof(EFI_ERROR_SECTION_DESCRIPTOR); } for ([[maybe_unused]] const auto& i : std::views::iota(0, static_cast(cperHeader.SectionCount))) { decodeCperSection(data, basePos, ampSpecHdr, &secDesc[i]); } delete[] secDesc; } void addCperSELLog(pldm_tid_t tid, uint16_t eventID, EFI_AMPERE_ERROR_DATA* p) { std::vector evtData; std::string message = "PLDM RAS SEL Event"; uint8_t recordType; uint8_t evtData1, evtData2, evtData3, evtData4, evtData5, evtData6; uint8_t socket; /* * OEM IPMI SEL Recode Format for RAS event: * evtData1: * Bit [7:4]: eventClass * 0xF: oemEvent for RAS * Bit [3:1]: Reserved * Bit 0: SocketID * 0x0: Socket 0 0x1: Socket 1 * evtData2: * Event ID, indicates RAS PLDM sensor ID. * evtData3: * Error Type ID high byte - Bit [15:8] * evtData4: * Error Type ID low byte - Bit [7:0] * evtData5: * Error Sub Type ID high byte * evtData6: * Error Sub Type ID low byte */ socket = (tid == 1) ? 0 : 1; recordType = 0xD0; evtData1 = SENSOR_TYPE_OEM | socket; evtData2 = eventID; evtData3 = p->TypeId >> 8; evtData4 = p->TypeId; evtData5 = p->SubtypeId >> 8; evtData6 = p->SubtypeId; /* * OEM data bytes * Ampere IANA: 3 bytes [0x3a 0xcd 0x00] * event data: 9 bytes [evtData1 evtData2 evtData3 * evtData4 evtData5 evtData6 * 0x00 0x00 0x00 ] * sel type: 1 byte [0xC0] */ evtData.push_back(0x3a); evtData.push_back(0xcd); evtData.push_back(0); evtData.push_back(evtData1); evtData.push_back(evtData2); evtData.push_back(evtData3); evtData.push_back(evtData4); evtData.push_back(evtData5); evtData.push_back(evtData6); evtData.push_back(0); evtData.push_back(0); evtData.push_back(0); auto& bus = pldm::utils::DBusHandler::getBus(); try { auto method = bus.new_method_call(logBusName, logPath, logIntf, "IpmiSelAddOem"); method.append(message, evtData, recordType); auto selReply = bus.call(method); if (selReply.is_method_error()) { lg2::error("addCperSELLog: add SEL log error"); } } catch (const std::exception& e) { lg2::error("call addCperSELLog error - {ERROR}", "ERROR", e); } } } // namespace oem_ampere } // namespace pldm