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