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