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