xref: /openbmc/pldm/oem/ampere/event/cper.cpp (revision 72c8aa06)
1 #include "cper.hpp"
2 
3 #include "libcper/Cper.h"
4 
5 #include "common/utils.hpp"
6 
7 #include <phosphor-logging/lg2.hpp>
8 
9 #include <cstring>
10 #include <ranges>
11 #include <vector>
12 
13 PHOSPHOR_LOG2_USING;
14 
15 namespace pldm
16 {
17 namespace oem_ampere
18 {
19 
20 // Returns one if two EFI GUIDs are equal, zero otherwise.
guid_equal(EFI_GUID * a,EFI_GUID * b)21 int guid_equal(EFI_GUID* a, EFI_GUID* b)
22 {
23     // Check top base 3 components.
24     if (a->Data1 != b->Data1 || a->Data2 != b->Data2 || a->Data3 != b->Data3)
25     {
26         return 0;
27     }
28 
29     // Check Data4 array for equality.
30     for (int i = 0; i < 8; i++)
31     {
32         if (a->Data4[i] != b->Data4[i])
33         {
34             return 0;
35         }
36     }
37 
38     return 1;
39 }
40 
decodeSecAmpere(void * section,EFI_AMPERE_ERROR_DATA * ampSpecHdr)41 static void decodeSecAmpere(void* section, EFI_AMPERE_ERROR_DATA* ampSpecHdr)
42 {
43     std::memcpy(ampSpecHdr, section, sizeof(EFI_AMPERE_ERROR_DATA));
44 }
45 
decodeSecArm(void * section,EFI_AMPERE_ERROR_DATA * ampSpecHdr)46 static void decodeSecArm(void* section, EFI_AMPERE_ERROR_DATA* ampSpecHdr)
47 {
48     int len;
49     EFI_ARM_ERROR_RECORD* proc;
50     EFI_ARM_ERROR_INFORMATION_ENTRY* errInfo;
51     EFI_ARM_CONTEXT_INFORMATION_HEADER* ctxInfo;
52 
53     proc = reinterpret_cast<EFI_ARM_ERROR_RECORD*>(section);
54     errInfo = reinterpret_cast<EFI_ARM_ERROR_INFORMATION_ENTRY*>(proc + 1);
55 
56     len = proc->SectionLength -
57           (sizeof(EFI_ARM_ERROR_RECORD) +
58            proc->ErrInfoNum * (sizeof(EFI_ARM_ERROR_INFORMATION_ENTRY)));
59     if (len < 0)
60     {
61         lg2::error("Section length is too small, section_length {LENGTH}",
62                    "LENGTH", static_cast<int>(proc->SectionLength));
63         return;
64     }
65 
66     ctxInfo = reinterpret_cast<EFI_ARM_CONTEXT_INFORMATION_HEADER*>(
67         errInfo + proc->ErrInfoNum);
68 
69     for ([[maybe_unused]] const auto& i :
70          std::views::iota(0, static_cast<int>(proc->ContextInfoNum)))
71     {
72         int size = sizeof(EFI_ARM_CONTEXT_INFORMATION_HEADER) +
73                    ctxInfo->RegisterArraySize;
74         len -= size;
75         ctxInfo = reinterpret_cast<EFI_ARM_CONTEXT_INFORMATION_HEADER*>(
76             (long)ctxInfo + size);
77     }
78 
79     if (len > 0)
80     {
81         /* Get Ampere Specific header data */
82         std::memcpy(ampSpecHdr, (void*)ctxInfo, sizeof(EFI_AMPERE_ERROR_DATA));
83     }
84 }
85 
decodeSecPlatformMemory(void * section,EFI_AMPERE_ERROR_DATA * ampSpecHdr)86 static void decodeSecPlatformMemory(void* section,
87                                     EFI_AMPERE_ERROR_DATA* ampSpecHdr)
88 {
89     EFI_PLATFORM_MEMORY_ERROR_DATA* mem =
90         reinterpret_cast<EFI_PLATFORM_MEMORY_ERROR_DATA*>(section);
91     if (mem->ErrorType == MEM_ERROR_TYPE_PARITY)
92     {
93         /* IP Type from bit 0 to 11 of TypeId */
94         ampSpecHdr->TypeId = (ampSpecHdr->TypeId & 0xf800) + ERROR_TYPE_ID_MCU;
95         ampSpecHdr->SubtypeId = SUBTYPE_ID_PARITY;
96     }
97 }
98 
decodeSecPcie(void * section,EFI_AMPERE_ERROR_DATA * ampSpecHdr)99 static void decodeSecPcie(void* section, EFI_AMPERE_ERROR_DATA* ampSpecHdr)
100 {
101     EFI_PCIE_ERROR_DATA* pcieErr =
102         reinterpret_cast<EFI_PCIE_ERROR_DATA*>(section);
103     if (pcieErr->ValidFields & CPER_PCIE_VALID_PORT_TYPE)
104     {
105         if (pcieErr->PortType == CPER_PCIE_PORT_TYPE_ROOT_PORT)
106         {
107             ampSpecHdr->SubtypeId = ERROR_SUBTYPE_PCIE_AER_ROOT_PORT;
108         }
109         else
110         {
111             ampSpecHdr->SubtypeId = ERROR_SUBTYPE_PCIE_AER_DEVICE;
112         }
113     }
114 }
115 
decodeCperSection(const uint8_t * data,long basePos,EFI_AMPERE_ERROR_DATA * ampSpecHdr,EFI_ERROR_SECTION_DESCRIPTOR * secDesc)116 static void decodeCperSection(const uint8_t* data, long basePos,
117                               EFI_AMPERE_ERROR_DATA* ampSpecHdr,
118                               EFI_ERROR_SECTION_DESCRIPTOR* secDesc)
119 {
120     long pos = basePos + secDesc->SectionOffset;
121     char* section = new char[secDesc->SectionLength];
122     std::memcpy(section, &data[pos], secDesc->SectionLength);
123     pos += secDesc->SectionLength;
124     EFI_GUID* ptr = reinterpret_cast<EFI_GUID*>(&secDesc->SectionType);
125     if (guid_equal(ptr, &gEfiAmpereErrorSectionGuid))
126     {
127         lg2::info("RAS Section Type : Ampere Specific");
128         decodeSecAmpere(section, ampSpecHdr);
129     }
130     else if (guid_equal(ptr, &gEfiArmProcessorErrorSectionGuid))
131     {
132         lg2::info("RAS Section Type : ARM");
133         decodeSecArm(section, ampSpecHdr);
134     }
135     else if (guid_equal(ptr, &gEfiPlatformMemoryErrorSectionGuid))
136     {
137         lg2::info("RAS Section Type : Memory");
138         decodeSecPlatformMemory(section, ampSpecHdr);
139     }
140     else if (guid_equal(ptr, &gEfiPcieErrorSectionGuid))
141     {
142         lg2::info("RAS Section Type : PCIE");
143         decodeSecPcie(section, ampSpecHdr);
144     }
145     else
146     {
147         lg2::error("Section Type is not supported");
148     }
149 
150     delete[] section;
151 }
152 
decodeCperRecord(const uint8_t * data,size_t,EFI_AMPERE_ERROR_DATA * ampSpecHdr)153 void decodeCperRecord(const uint8_t* data, size_t /* eventDataSize */,
154                       EFI_AMPERE_ERROR_DATA* ampSpecHdr)
155 {
156     EFI_COMMON_ERROR_RECORD_HEADER cperHeader;
157     long pos = sizeof(CommonEventData);
158     long basePos = sizeof(CommonEventData);
159 
160     std::memcpy(&cperHeader, &data[pos],
161                 sizeof(EFI_COMMON_ERROR_RECORD_HEADER));
162     pos += sizeof(EFI_COMMON_ERROR_RECORD_HEADER);
163 
164     EFI_ERROR_SECTION_DESCRIPTOR* secDesc =
165         new EFI_ERROR_SECTION_DESCRIPTOR[cperHeader.SectionCount];
166     for ([[maybe_unused]] const auto& i :
167          std::views::iota(0, static_cast<int>(cperHeader.SectionCount)))
168     {
169         std::memcpy(&secDesc[i], &data[pos],
170                     sizeof(EFI_ERROR_SECTION_DESCRIPTOR));
171         pos += sizeof(EFI_ERROR_SECTION_DESCRIPTOR);
172     }
173 
174     for ([[maybe_unused]] const auto& i :
175          std::views::iota(0, static_cast<int>(cperHeader.SectionCount)))
176     {
177         decodeCperSection(data, basePos, ampSpecHdr, &secDesc[i]);
178     }
179 
180     delete[] secDesc;
181 }
182 
addCperSELLog(pldm_tid_t tid,uint16_t eventID,EFI_AMPERE_ERROR_DATA * p)183 void addCperSELLog(pldm_tid_t tid, uint16_t eventID, EFI_AMPERE_ERROR_DATA* p)
184 {
185     std::vector<uint8_t> evtData;
186     std::string message = "PLDM RAS SEL Event";
187     uint8_t recordType;
188     uint8_t evtData1, evtData2, evtData3, evtData4, evtData5, evtData6;
189     uint8_t socket;
190 
191     /*
192      * OEM IPMI SEL Recode Format for RAS event:
193      * evtData1:
194      *    Bit [7:4]: eventClass
195      *        0xF: oemEvent for RAS
196      *    Bit [3:1]: Reserved
197      *    Bit 0: SocketID
198      *        0x0: Socket 0 0x1: Socket 1
199      * evtData2:
200      *    Event ID, indicates RAS PLDM sensor ID.
201      * evtData3:
202      *     Error Type ID high byte - Bit [15:8]
203      * evtData4:
204      *     Error Type ID low byte - Bit [7:0]
205      * evtData5:
206      *     Error Sub Type ID high byte
207      * evtData6:
208      *     Error Sub Type ID low byte
209      */
210     socket = (tid == 1) ? 0 : 1;
211     recordType = 0xD0;
212     evtData1 = SENSOR_TYPE_OEM | socket;
213     evtData2 = eventID;
214     evtData3 = p->TypeId >> 8;
215     evtData4 = p->TypeId;
216     evtData5 = p->SubtypeId >> 8;
217     evtData6 = p->SubtypeId;
218     /*
219      * OEM data bytes
220      *    Ampere IANA: 3 bytes [0x3a 0xcd 0x00]
221      *    event data: 9 bytes [evtData1 evtData2 evtData3
222      *                         evtData4 evtData5 evtData6
223      *                         0x00     0x00     0x00 ]
224      *    sel type: 1 byte [0xC0]
225      */
226     evtData.push_back(0x3a);
227     evtData.push_back(0xcd);
228     evtData.push_back(0);
229     evtData.push_back(evtData1);
230     evtData.push_back(evtData2);
231     evtData.push_back(evtData3);
232     evtData.push_back(evtData4);
233     evtData.push_back(evtData5);
234     evtData.push_back(evtData6);
235     evtData.push_back(0);
236     evtData.push_back(0);
237     evtData.push_back(0);
238     auto& bus = pldm::utils::DBusHandler::getBus();
239     try
240     {
241         auto method =
242             bus.new_method_call(logBusName, logPath, logIntf, "IpmiSelAddOem");
243         method.append(message, evtData, recordType);
244 
245         auto selReply = bus.call(method);
246         if (selReply.is_method_error())
247         {
248             lg2::error("addCperSELLog: add SEL log error");
249         }
250     }
251     catch (const std::exception& e)
252     {
253         lg2::error("call addCperSELLog error - {ERROR}", "ERROR", e);
254     }
255 }
256 
257 } // namespace oem_ampere
258 } // namespace pldm
259