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