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 "libbase64.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.
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 	//Device serial & capability structure (if CXL 1.1 device).
96 	if (cxl_protocol_error->CxlAgentType ==
97 	    CXL_PROTOCOL_ERROR_DEVICE_AGENT) {
98 		json_object_object_add(
99 			section_ir, "deviceSerial",
100 			json_object_new_uint64(
101 				cxl_protocol_error->DeviceSerial));
102 
103 		//The PCIe capability structure provided here could either be PCIe 1.1 Capability Structure
104 		//(36-byte, padded to 60 bytes) or PCIe 2.0 Capability Structure (60-byte). There does not seem
105 		//to be a way to differentiate these, so this is left as a b64 dump.
106 		char *encoded = malloc(2 * 60);
107 		size_t encoded_len = 0;
108 		if (!encoded) {
109 			printf("Failed to allocate encode output buffer. \n");
110 		} else {
111 			base64_encode((const char *)cxl_protocol_error
112 					      ->CapabilityStructure.PcieCap,
113 				      60, encoded, &encoded_len, 0);
114 			json_object_object_add(section_ir,
115 					       "capabilityStructure",
116 					       json_object_new_string_len(
117 						       encoded, encoded_len));
118 			free(encoded);
119 		}
120 	}
121 
122 	//CXL DVSEC & error log length.
123 	json_object_object_add(
124 		section_ir, "dvsecLength",
125 		json_object_new_int(cxl_protocol_error->CxlDvsecLength));
126 	json_object_object_add(
127 		section_ir, "errorLogLength",
128 		json_object_new_int(cxl_protocol_error->CxlErrorLogLength));
129 
130 	//CXL DVSEC
131 	//For CXL 1.1 devices, this is the "CXL DVSEC For Flex Bus Device" structure as in CXL 1.1 spec.
132 	//For CXL 1.1 host downstream ports, this is the "CXL DVSEC For Flex Bus Port" structure as in CXL 1.1 spec.
133 	const char *cur_pos = (const char *)(cxl_protocol_error + 1);
134 	char *encoded = malloc(2 * cxl_protocol_error->CxlDvsecLength);
135 	size_t encoded_len = 0;
136 	if (!encoded) {
137 		printf("Failed to allocate encode output buffer. \n");
138 	} else {
139 		base64_encode(cur_pos, cxl_protocol_error->CxlDvsecLength,
140 			      encoded, &encoded_len, 0);
141 		json_object_object_add(section_ir, "cxlDVSEC",
142 				       json_object_new_string_len(encoded,
143 								  encoded_len));
144 
145 		free(encoded);
146 	}
147 	cur_pos += cxl_protocol_error->CxlDvsecLength;
148 
149 	//CXL Error Log
150 	//This is the "CXL RAS Capability Structure" as in CXL 1.1 spec.
151 	encoded = malloc(2 * cxl_protocol_error->CxlErrorLogLength);
152 	encoded_len = 0;
153 	if (!encoded) {
154 		printf("Failed to allocate encode output buffer. \n");
155 	} else {
156 		base64_encode(cur_pos, cxl_protocol_error->CxlErrorLogLength,
157 			      encoded, &encoded_len, 0);
158 		json_object_object_add(section_ir, "cxlErrorLog",
159 				       json_object_new_string_len(encoded,
160 								  encoded_len));
161 		free(encoded);
162 	}
163 	return section_ir;
164 }
165 
166 //Converts a single CXL protocol CPER-JSON section into CPER binary, outputting to the given stream.
167 void ir_section_cxl_protocol_to_cper(json_object *section, FILE *out)
168 {
169 	EFI_CXL_PROTOCOL_ERROR_DATA *section_cper =
170 		(EFI_CXL_PROTOCOL_ERROR_DATA *)calloc(
171 			1, sizeof(EFI_CXL_PROTOCOL_ERROR_DATA));
172 
173 	//Validation bits.
174 	section_cper->ValidBits = ir_to_bitfield(
175 		json_object_object_get(section, "validationBits"), 7,
176 		CXL_PROTOCOL_ERROR_VALID_BITFIELD_NAMES);
177 
178 	//Detecting agent type.
179 	section_cper->CxlAgentType = readable_pair_to_integer(
180 		json_object_object_get(section, "agentType"));
181 
182 	//Based on the agent type, set the address.
183 	json_object *address =
184 		json_object_object_get(section, "cxlAgentAddress");
185 	if (section_cper->CxlAgentType == CXL_PROTOCOL_ERROR_DEVICE_AGENT) {
186 		//Address is split by function, device, bus & segment.
187 		UINT64 function = json_object_get_uint64(
188 			json_object_object_get(address, "functionNumber"));
189 		UINT64 device = json_object_get_uint64(
190 			json_object_object_get(address, "deviceNumber"));
191 		UINT64 bus = json_object_get_uint64(
192 			json_object_object_get(address, "busNumber"));
193 		UINT64 segment = json_object_get_uint64(
194 			json_object_object_get(address, "segmentNumber"));
195 		section_cper->CxlAgentAddress.DeviceAddress.FunctionNumber =
196 			function;
197 		section_cper->CxlAgentAddress.DeviceAddress.DeviceNumber =
198 			device;
199 		section_cper->CxlAgentAddress.DeviceAddress.BusNumber = bus;
200 		section_cper->CxlAgentAddress.DeviceAddress.SegmentNumber =
201 			segment;
202 	} else if (section_cper->CxlAgentType ==
203 		   CXL_PROTOCOL_ERROR_HOST_DOWNSTREAM_PORT_AGENT) {
204 		//Plain RCRB base address.
205 		section_cper->CxlAgentAddress.PortRcrbBaseAddress =
206 			json_object_get_uint64(
207 				json_object_object_get(address, "value"));
208 	}
209 
210 	//Device ID information.
211 	json_object *device_id = json_object_object_get(section, "deviceID");
212 	section_cper->DeviceId.VendorId = json_object_get_uint64(
213 		json_object_object_get(device_id, "vendorID"));
214 	section_cper->DeviceId.DeviceId = json_object_get_uint64(
215 		json_object_object_get(device_id, "deviceID"));
216 	section_cper->DeviceId.SubsystemVendorId = json_object_get_uint64(
217 		json_object_object_get(device_id, "subsystemVendorID"));
218 	section_cper->DeviceId.SubsystemDeviceId = json_object_get_uint64(
219 		json_object_object_get(device_id, "subsystemDeviceID"));
220 	section_cper->DeviceId.ClassCode = json_object_get_uint64(
221 		json_object_object_get(device_id, "classCode"));
222 	section_cper->DeviceId.SlotNumber = json_object_get_uint64(
223 		json_object_object_get(device_id, "slotNumber"));
224 
225 	//If CXL 1.1 device, the serial number & PCI capability structure.
226 	if (section_cper->CxlAgentType == CXL_PROTOCOL_ERROR_DEVICE_AGENT) {
227 		section_cper->DeviceSerial = json_object_get_uint64(
228 			json_object_object_get(section, "deviceSerial"));
229 
230 		json_object *encoded =
231 			json_object_object_get(section, "capabilityStructure");
232 		char *decoded = malloc(json_object_get_string_len(encoded));
233 		size_t decoded_len = 0;
234 		if (!decoded) {
235 			printf("Failed to allocate decode output buffer. \n");
236 		} else {
237 			base64_decode(json_object_get_string(encoded),
238 				      json_object_get_string_len(encoded),
239 				      decoded, &decoded_len, 0);
240 			memcpy(section_cper->CapabilityStructure.PcieCap,
241 			       decoded, decoded_len);
242 			free(decoded);
243 		}
244 	}
245 
246 	//DVSEC length & error log length.
247 	section_cper->CxlDvsecLength = (UINT16)json_object_get_int(
248 		json_object_object_get(section, "dvsecLength"));
249 	section_cper->CxlErrorLogLength = (UINT16)json_object_get_int(
250 		json_object_object_get(section, "errorLogLength"));
251 
252 	//Write header to stream.
253 	fwrite(section_cper, sizeof(EFI_CXL_PROTOCOL_ERROR_DATA), 1, out);
254 	fflush(out);
255 
256 	//DVSEC out to stream.
257 	json_object *encoded = json_object_object_get(section, "cxlDVSEC");
258 	char *decoded = malloc(json_object_get_string_len(encoded));
259 	size_t decoded_len = 0;
260 	if (!decoded) {
261 		printf("Failed to allocate decode output buffer. \n");
262 	} else {
263 		base64_decode(json_object_get_string(encoded),
264 			      json_object_get_string_len(encoded), decoded,
265 			      &decoded_len, 0);
266 		fwrite(decoded, decoded_len, 1, out);
267 		fflush(out);
268 		free(decoded);
269 	}
270 
271 	//Error log out to stream.
272 	encoded = json_object_object_get(section, "cxlErrorLog");
273 	decoded = malloc(json_object_get_string_len(encoded));
274 	decoded_len = 0;
275 	if (!decoded) {
276 		printf("Failed to allocate decode output buffer. \n");
277 	} else {
278 		base64_decode(json_object_get_string(encoded),
279 			      json_object_get_string_len(encoded), decoded,
280 			      &decoded_len, 0);
281 		fwrite(decoded, decoded_len, 1, out);
282 		fflush(out);
283 		free(decoded);
284 	}
285 
286 	free(section_cper);
287 }
288