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