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