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