1 /**
2 * Describes functions for converting CXL protocol error CPER sections from binary and JSON format
3 * into an intermediate format.
4 *
5 * Author: Lawrence.Tang@arm.com
6 **/
7 #include <stdio.h>
8 #include <string.h>
9 #include <libcper/base64.h>
10 #include <libcper/Cper.h>
11 #include <libcper/cper-utils.h>
12 #include <libcper/sections/cper-section-cxl-protocol.h>
13 #include <libcper/log.h>
14 #include <string.h>
15
16 //Converts a single CXL protocol error CPER section into JSON IR.
cper_section_cxl_protocol_to_ir(const UINT8 * section,UINT32 size,char ** desc_string)17 json_object *cper_section_cxl_protocol_to_ir(const UINT8 *section, UINT32 size,
18 char **desc_string)
19 {
20 int outstr_len = 0;
21 *desc_string = malloc(SECTION_DESC_STRING_SIZE);
22 outstr_len = snprintf(*desc_string, SECTION_DESC_STRING_SIZE,
23 "A CXL Protocol Error occurred");
24 if (outstr_len < 0) {
25 cper_print_log(
26 "Error: Could not write to CXL protocol description string\n");
27 } else if (outstr_len > SECTION_DESC_STRING_SIZE) {
28 cper_print_log(
29 "Error: CXL protocol description string truncated\n");
30 }
31
32 if (size < sizeof(EFI_CXL_PROTOCOL_ERROR_DATA)) {
33 return NULL;
34 }
35
36 EFI_CXL_PROTOCOL_ERROR_DATA *cxl_protocol_error =
37 (EFI_CXL_PROTOCOL_ERROR_DATA *)section;
38
39 if (size < sizeof(EFI_CXL_PROTOCOL_ERROR_DATA) +
40 cxl_protocol_error->CxlDvsecLength +
41 cxl_protocol_error->CxlErrorLogLength) {
42 return NULL;
43 }
44
45 json_object *section_ir = json_object_new_object();
46 ValidationTypes ui64Type = {
47 UINT_64T, .value.ui64 = cxl_protocol_error->ValidBits
48 };
49
50 //Type of detecting agent.
51 if (isvalid_prop_to_ir(&ui64Type, 0)) {
52 json_object *agent_type = integer_to_readable_pair(
53 cxl_protocol_error->CxlAgentType, 2,
54 CXL_PROTOCOL_ERROR_AGENT_TYPES_KEYS,
55 CXL_PROTOCOL_ERROR_AGENT_TYPES_VALUES,
56 "Unknown (Reserved)");
57 json_object_object_add(section_ir, "agentType", agent_type);
58 }
59 if (isvalid_prop_to_ir(&ui64Type, 1)) {
60 //CXL agent address, depending on the agent type.
61 json_object *agent_address = NULL;
62 if (cxl_protocol_error->CxlAgentType ==
63 CXL_PROTOCOL_ERROR_DEVICE_AGENT) {
64 agent_address = json_object_new_object();
65 //Address is a CXL1.1 device agent.
66 json_object_object_add(
67 agent_address, "functionNumber",
68 json_object_new_uint64(
69 cxl_protocol_error->CxlAgentAddress
70 .DeviceAddress.FunctionNumber));
71 json_object_object_add(
72 agent_address, "deviceNumber",
73 json_object_new_uint64(
74 cxl_protocol_error->CxlAgentAddress
75 .DeviceAddress.DeviceNumber));
76 json_object_object_add(
77 agent_address, "busNumber",
78 json_object_new_uint64(
79 cxl_protocol_error->CxlAgentAddress
80 .DeviceAddress.BusNumber));
81 json_object_object_add(
82 agent_address, "segmentNumber",
83 json_object_new_uint64(
84 cxl_protocol_error->CxlAgentAddress
85 .DeviceAddress.SegmentNumber));
86 } else if (cxl_protocol_error->CxlAgentType ==
87 CXL_PROTOCOL_ERROR_HOST_DOWNSTREAM_PORT_AGENT) {
88 agent_address = json_object_new_object();
89 //Address is a CXL port RCRB base address.
90 json_object_object_add(
91 agent_address, "value",
92 json_object_new_uint64(
93 cxl_protocol_error->CxlAgentAddress
94 .PortRcrbBaseAddress));
95 }
96 if (agent_address != NULL) {
97 json_object_object_add(section_ir, "cxlAgentAddress",
98 agent_address);
99 }
100 }
101
102 json_object *device_id = json_object_new_object();
103 json_object_object_add(
104 device_id, "vendorID",
105 json_object_new_uint64(cxl_protocol_error->DeviceId.VendorId));
106
107 //Device ID.
108 if (isvalid_prop_to_ir(&ui64Type, 2)) {
109 json_object_object_add(
110 device_id, "deviceID",
111 json_object_new_uint64(
112 cxl_protocol_error->DeviceId.DeviceId));
113 json_object_object_add(
114 device_id, "subsystemVendorID",
115 json_object_new_uint64(
116 cxl_protocol_error->DeviceId.SubsystemVendorId));
117 json_object_object_add(
118 device_id, "subsystemDeviceID",
119 json_object_new_uint64(
120 cxl_protocol_error->DeviceId.SubsystemDeviceId));
121 json_object_object_add(
122 device_id, "classCode",
123 json_object_new_uint64(
124 cxl_protocol_error->DeviceId.ClassCode));
125 json_object_object_add(
126 device_id, "slotNumber",
127 json_object_new_uint64(
128 cxl_protocol_error->DeviceId.SlotNumber));
129 }
130 json_object_object_add(section_ir, "deviceID", device_id);
131
132 if (isvalid_prop_to_ir(&ui64Type, 3)) {
133 //Device serial & capability structure (if CXL 1.1 device).
134 if (cxl_protocol_error->CxlAgentType ==
135 CXL_PROTOCOL_ERROR_DEVICE_AGENT) {
136 json_object_object_add(
137 section_ir, "deviceSerial",
138 json_object_new_uint64(
139 cxl_protocol_error->DeviceSerial));
140 }
141 }
142
143 char *encoded;
144 int32_t encoded_len = 0;
145
146 //The PCIe capability structure provided here could either be PCIe 1.1 Capability Structure
147 //(36-byte, padded to 60 bytes) or PCIe 2.0 Capability Structure (60-byte). There does not seem
148 //to be a way to differentiate these, so this is left as a b64 dump.
149 if (isvalid_prop_to_ir(&ui64Type, 4)) {
150 encoded = base64_encode(
151 (UINT8 *)cxl_protocol_error->CapabilityStructure.PcieCap,
152 60, &encoded_len);
153 if (encoded == NULL) {
154 cper_print_log(
155 "Failed to allocate encode output buffer. \n");
156 json_object_put(section_ir);
157
158 return NULL;
159 }
160 json_object_object_add(section_ir, "capabilityStructure",
161 json_object_new_string_len(encoded,
162 encoded_len));
163 free(encoded);
164 }
165
166 const UINT8 *cur_pos = (const UINT8 *)(cxl_protocol_error + 1);
167
168 if (isvalid_prop_to_ir(&ui64Type, 5)) {
169 //CXL DVSEC & error log length.
170 json_object_object_add(
171 section_ir, "dvsecLength",
172 json_object_new_int(
173 cxl_protocol_error->CxlDvsecLength));
174 //CXL DVSEC
175 //For CXL 1.1 devices, this is the "CXL DVSEC For Flex Bus Device" structure as in CXL 1.1 spec.
176 //For CXL 1.1 host downstream ports, this is the "CXL DVSEC For Flex Bus Port" structure as in CXL 1.1 spec.
177 int32_t encoded_len = 0;
178
179 encoded = base64_encode(cur_pos,
180 cxl_protocol_error->CxlDvsecLength,
181 &encoded_len);
182 if (encoded == NULL) {
183 json_object_put(section_ir);
184 return NULL;
185 }
186 json_object_object_add(section_ir, "cxlDVSEC",
187 json_object_new_string_len(encoded,
188 encoded_len));
189
190 free(encoded);
191 }
192
193 cur_pos += cxl_protocol_error->CxlDvsecLength;
194
195 if (isvalid_prop_to_ir(&ui64Type, 6)) {
196 json_object_object_add(
197 section_ir, "errorLogLength",
198 json_object_new_int(
199 cxl_protocol_error->CxlErrorLogLength));
200
201 //CXL Error Log
202 //This is the "CXL RAS Capability Structure" as in CXL 1.1 spec.
203
204 encoded_len = 0;
205 encoded = base64_encode((UINT8 *)cur_pos,
206 cxl_protocol_error->CxlErrorLogLength,
207 &encoded_len);
208
209 if (encoded == NULL) {
210 cper_print_log(
211 "Failed to allocate encode output buffer. \n");
212 json_object_put(section_ir);
213 return NULL;
214 }
215 json_object_object_add(section_ir, "cxlErrorLog",
216 json_object_new_string_len(encoded,
217 encoded_len));
218 free(encoded);
219 }
220
221 return section_ir;
222 }
223
224 //Converts a single CXL protocol CPER-JSON section into CPER binary, outputting to the given stream.
ir_section_cxl_protocol_to_cper(json_object * section,FILE * out)225 void ir_section_cxl_protocol_to_cper(json_object *section, FILE *out)
226 {
227 EFI_CXL_PROTOCOL_ERROR_DATA *section_cper =
228 (EFI_CXL_PROTOCOL_ERROR_DATA *)calloc(
229 1, sizeof(EFI_CXL_PROTOCOL_ERROR_DATA));
230 struct json_object *obj = NULL;
231
232 //Validation bits.
233 ValidationTypes ui64Type = { UINT_64T, .value.ui64 = 0 };
234
235 //Detecting agent type.
236 if (json_object_object_get_ex(section, "agentType", &obj)) {
237 section_cper->CxlAgentType = readable_pair_to_integer(obj);
238 add_to_valid_bitfield(&ui64Type, 0);
239 }
240
241 //Based on the agent type, set the address.
242 if (json_object_object_get_ex(section, "cxlAgentAddress", &obj)) {
243 json_object *address = obj;
244 if (section_cper->CxlAgentType ==
245 CXL_PROTOCOL_ERROR_DEVICE_AGENT) {
246 //Address is split by function, device, bus & segment.
247 UINT64 function = json_object_get_uint64(
248 json_object_object_get(address,
249 "functionNumber"));
250 UINT64 device = json_object_get_uint64(
251 json_object_object_get(address,
252 "deviceNumber"));
253 UINT64 bus = json_object_get_uint64(
254 json_object_object_get(address, "busNumber"));
255 UINT64 segment = json_object_get_uint64(
256 json_object_object_get(address,
257 "segmentNumber"));
258 section_cper->CxlAgentAddress.DeviceAddress
259 .FunctionNumber = function;
260 section_cper->CxlAgentAddress.DeviceAddress
261 .DeviceNumber = device;
262 section_cper->CxlAgentAddress.DeviceAddress.BusNumber =
263 bus;
264 section_cper->CxlAgentAddress.DeviceAddress
265 .SegmentNumber = segment;
266 } else if (section_cper->CxlAgentType ==
267 CXL_PROTOCOL_ERROR_HOST_DOWNSTREAM_PORT_AGENT) {
268 //Plain RCRB base address.
269 section_cper->CxlAgentAddress.PortRcrbBaseAddress =
270 json_object_get_uint64(json_object_object_get(
271 address, "value"));
272 }
273 add_to_valid_bitfield(&ui64Type, 1);
274 }
275
276 //Device ID information.
277 if (json_object_object_get_ex(section, "deviceID", &obj)) {
278 json_object *device_id = obj;
279 section_cper->DeviceId.VendorId = json_object_get_uint64(
280 json_object_object_get(device_id, "vendorID"));
281 section_cper->DeviceId.DeviceId = json_object_get_uint64(
282 json_object_object_get(device_id, "deviceID"));
283 section_cper->DeviceId.SubsystemVendorId =
284 json_object_get_uint64(json_object_object_get(
285 device_id, "subsystemVendorID"));
286 section_cper->DeviceId.SubsystemDeviceId =
287 json_object_get_uint64(json_object_object_get(
288 device_id, "subsystemDeviceID"));
289 section_cper->DeviceId.ClassCode = json_object_get_uint64(
290 json_object_object_get(device_id, "classCode"));
291 section_cper->DeviceId.SlotNumber = json_object_get_uint64(
292 json_object_object_get(device_id, "slotNumber"));
293 add_to_valid_bitfield(&ui64Type, 2);
294 }
295
296 //If CXL 1.1 device, the serial number & PCI capability structure.
297 UINT8 *decoded;
298 if (section_cper->CxlAgentType == CXL_PROTOCOL_ERROR_DEVICE_AGENT) {
299 if (json_object_object_get_ex(section, "deviceSerial", &obj)) {
300 section_cper->DeviceSerial =
301 json_object_get_uint64(obj);
302 add_to_valid_bitfield(&ui64Type, 3);
303 }
304 if (json_object_object_get_ex(section, "capabilityStructure",
305 &obj)) {
306 json_object *encoded = obj;
307
308 int32_t decoded_len = 0;
309
310 decoded = base64_decode(
311 json_object_get_string(encoded),
312 json_object_get_string_len(encoded),
313 &decoded_len);
314
315 if (decoded == NULL) {
316 cper_print_log(
317 "Failed to allocate decode output buffer. \n");
318 } else {
319 memcpy(section_cper->CapabilityStructure.PcieCap,
320 decoded, decoded_len);
321 free(decoded);
322 add_to_valid_bitfield(&ui64Type, 4);
323 }
324 }
325 }
326
327 //DVSEC length & error log length.
328 section_cper->CxlDvsecLength = (UINT16)json_object_get_int(
329 json_object_object_get(section, "dvsecLength"));
330 section_cper->CxlErrorLogLength = (UINT16)json_object_get_int(
331 json_object_object_get(section, "errorLogLength"));
332
333 json_object *encodedsrc = NULL;
334 json_object *encodederr = NULL;
335
336 //DVSEC out: write valid bits
337 if (json_object_object_get_ex(section, "cxlDVSEC", &obj)) {
338 add_to_valid_bitfield(&ui64Type, 5);
339 encodedsrc = obj;
340 }
341
342 //Error log: write valid bits
343 if (json_object_object_get_ex(section, "cxlErrorLog", &obj)) {
344 add_to_valid_bitfield(&ui64Type, 6);
345 encodederr = obj;
346 }
347 section_cper->ValidBits = ui64Type.value.ui64;
348
349 //Write header to stream.
350 fwrite(section_cper, sizeof(EFI_CXL_PROTOCOL_ERROR_DATA), 1, out);
351 fflush(out);
352
353 //DVSEC out to stream.
354 int32_t decoded_len = 0;
355 if (encodedsrc != NULL) {
356 decoded = base64_decode(json_object_get_string(encodedsrc),
357 json_object_get_string_len(encodedsrc),
358 &decoded_len);
359 if (decoded == NULL) {
360 cper_print_log(
361 "Failed to allocate decode output buffer. \n");
362 } else {
363 fwrite(decoded, decoded_len, 1, out);
364 fflush(out);
365 free(decoded);
366 }
367 }
368
369 //Error log out to stream.
370 decoded_len = 0;
371 if (encodederr != NULL) {
372 decoded = base64_decode(json_object_get_string(encodederr),
373 json_object_get_string_len(encodederr),
374 &decoded_len);
375 if (decoded == NULL) {
376 cper_print_log(
377 "Failed to allocate decode output buffer. \n");
378 } else {
379 fwrite(decoded, decoded_len, 1, out);
380 fflush(out);
381 free(decoded);
382 }
383 }
384
385 free(section_cper);
386 }
387