xref: /openbmc/libcper/sections/cper-section-cxl-protocol.c (revision d6b62637dc5203821c87aeb0145fb8c7f9d607c7)
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