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. 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 if (isvalid_prop_to_ir(&ui64Type, 1)) { 46 //CXL agent address, depending on the agent type. 47 json_object *agent_address = NULL; 48 if (cxl_protocol_error->CxlAgentType == 49 CXL_PROTOCOL_ERROR_DEVICE_AGENT) { 50 agent_address = json_object_new_object(); 51 //Address is a CXL1.1 device agent. 52 json_object_object_add( 53 agent_address, "functionNumber", 54 json_object_new_uint64( 55 cxl_protocol_error->CxlAgentAddress 56 .DeviceAddress.FunctionNumber)); 57 json_object_object_add( 58 agent_address, "deviceNumber", 59 json_object_new_uint64( 60 cxl_protocol_error->CxlAgentAddress 61 .DeviceAddress.DeviceNumber)); 62 json_object_object_add( 63 agent_address, "busNumber", 64 json_object_new_uint64( 65 cxl_protocol_error->CxlAgentAddress 66 .DeviceAddress.BusNumber)); 67 json_object_object_add( 68 agent_address, "segmentNumber", 69 json_object_new_uint64( 70 cxl_protocol_error->CxlAgentAddress 71 .DeviceAddress.SegmentNumber)); 72 } else if (cxl_protocol_error->CxlAgentType == 73 CXL_PROTOCOL_ERROR_HOST_DOWNSTREAM_PORT_AGENT) { 74 agent_address = json_object_new_object(); 75 //Address is a CXL port RCRB base address. 76 json_object_object_add( 77 agent_address, "value", 78 json_object_new_uint64( 79 cxl_protocol_error->CxlAgentAddress 80 .PortRcrbBaseAddress)); 81 } 82 if (agent_address != NULL) { 83 json_object_object_add(section_ir, "cxlAgentAddress", 84 agent_address); 85 } 86 } 87 88 json_object *device_id = json_object_new_object(); 89 json_object_object_add( 90 device_id, "vendorID", 91 json_object_new_uint64(cxl_protocol_error->DeviceId.VendorId)); 92 93 //Device ID. 94 if (isvalid_prop_to_ir(&ui64Type, 2)) { 95 json_object_object_add( 96 device_id, "deviceID", 97 json_object_new_uint64( 98 cxl_protocol_error->DeviceId.DeviceId)); 99 json_object_object_add( 100 device_id, "subsystemVendorID", 101 json_object_new_uint64( 102 cxl_protocol_error->DeviceId.SubsystemVendorId)); 103 json_object_object_add( 104 device_id, "subsystemDeviceID", 105 json_object_new_uint64( 106 cxl_protocol_error->DeviceId.SubsystemDeviceId)); 107 json_object_object_add( 108 device_id, "classCode", 109 json_object_new_uint64( 110 cxl_protocol_error->DeviceId.ClassCode)); 111 json_object_object_add( 112 device_id, "slotNumber", 113 json_object_new_uint64( 114 cxl_protocol_error->DeviceId.SlotNumber)); 115 } 116 json_object_object_add(section_ir, "deviceID", device_id); 117 118 if (isvalid_prop_to_ir(&ui64Type, 3)) { 119 //Device serial & capability structure (if CXL 1.1 device). 120 if (cxl_protocol_error->CxlAgentType == 121 CXL_PROTOCOL_ERROR_DEVICE_AGENT) { 122 json_object_object_add( 123 section_ir, "deviceSerial", 124 json_object_new_uint64( 125 cxl_protocol_error->DeviceSerial)); 126 } 127 } 128 129 char *encoded; 130 int32_t encoded_len = 0; 131 132 //The PCIe capability structure provided here could either be PCIe 1.1 Capability Structure 133 //(36-byte, padded to 60 bytes) or PCIe 2.0 Capability Structure (60-byte). There does not seem 134 //to be a way to differentiate these, so this is left as a b64 dump. 135 if (isvalid_prop_to_ir(&ui64Type, 4)) { 136 encoded = base64_encode( 137 (UINT8 *)cxl_protocol_error->CapabilityStructure.PcieCap, 138 60, &encoded_len); 139 if (encoded == NULL) { 140 cper_print_log( 141 "Failed to allocate encode output buffer. \n"); 142 json_object_put(section_ir); 143 144 return NULL; 145 } 146 json_object_object_add(section_ir, "capabilityStructure", 147 json_object_new_string_len(encoded, 148 encoded_len)); 149 free(encoded); 150 } 151 152 const UINT8 *cur_pos = (const UINT8 *)(cxl_protocol_error + 1); 153 154 if (isvalid_prop_to_ir(&ui64Type, 5)) { 155 //CXL DVSEC & error log length. 156 json_object_object_add( 157 section_ir, "dvsecLength", 158 json_object_new_int( 159 cxl_protocol_error->CxlDvsecLength)); 160 //CXL DVSEC 161 //For CXL 1.1 devices, this is the "CXL DVSEC For Flex Bus Device" structure as in CXL 1.1 spec. 162 //For CXL 1.1 host downstream ports, this is the "CXL DVSEC For Flex Bus Port" structure as in CXL 1.1 spec. 163 int32_t encoded_len = 0; 164 165 encoded = base64_encode(cur_pos, 166 cxl_protocol_error->CxlDvsecLength, 167 &encoded_len); 168 if (encoded == NULL) { 169 json_object_put(section_ir); 170 return NULL; 171 } 172 json_object_object_add(section_ir, "cxlDVSEC", 173 json_object_new_string_len(encoded, 174 encoded_len)); 175 176 free(encoded); 177 } 178 179 cur_pos += cxl_protocol_error->CxlDvsecLength; 180 181 if (isvalid_prop_to_ir(&ui64Type, 6)) { 182 json_object_object_add( 183 section_ir, "errorLogLength", 184 json_object_new_int( 185 cxl_protocol_error->CxlErrorLogLength)); 186 187 //CXL Error Log 188 //This is the "CXL RAS Capability Structure" as in CXL 1.1 spec. 189 190 encoded_len = 0; 191 encoded = base64_encode((UINT8 *)cur_pos, 192 cxl_protocol_error->CxlErrorLogLength, 193 &encoded_len); 194 195 if (encoded == NULL) { 196 cper_print_log( 197 "Failed to allocate encode output buffer. \n"); 198 json_object_put(section_ir); 199 return NULL; 200 } 201 json_object_object_add(section_ir, "cxlErrorLog", 202 json_object_new_string_len(encoded, 203 encoded_len)); 204 free(encoded); 205 } 206 207 return section_ir; 208 } 209 210 //Converts a single CXL protocol CPER-JSON section into CPER binary, outputting to the given stream. 211 void ir_section_cxl_protocol_to_cper(json_object *section, FILE *out) 212 { 213 EFI_CXL_PROTOCOL_ERROR_DATA *section_cper = 214 (EFI_CXL_PROTOCOL_ERROR_DATA *)calloc( 215 1, sizeof(EFI_CXL_PROTOCOL_ERROR_DATA)); 216 struct json_object *obj = NULL; 217 218 //Validation bits. 219 ValidationTypes ui64Type = { UINT_64T, .value.ui64 = 0 }; 220 221 //Detecting agent type. 222 if (json_object_object_get_ex(section, "agentType", &obj)) { 223 section_cper->CxlAgentType = readable_pair_to_integer(obj); 224 add_to_valid_bitfield(&ui64Type, 0); 225 } 226 227 //Based on the agent type, set the address. 228 if (json_object_object_get_ex(section, "cxlAgentAddress", &obj)) { 229 json_object *address = obj; 230 if (section_cper->CxlAgentType == 231 CXL_PROTOCOL_ERROR_DEVICE_AGENT) { 232 //Address is split by function, device, bus & segment. 233 UINT64 function = json_object_get_uint64( 234 json_object_object_get(address, 235 "functionNumber")); 236 UINT64 device = json_object_get_uint64( 237 json_object_object_get(address, 238 "deviceNumber")); 239 UINT64 bus = json_object_get_uint64( 240 json_object_object_get(address, "busNumber")); 241 UINT64 segment = json_object_get_uint64( 242 json_object_object_get(address, 243 "segmentNumber")); 244 section_cper->CxlAgentAddress.DeviceAddress 245 .FunctionNumber = function; 246 section_cper->CxlAgentAddress.DeviceAddress 247 .DeviceNumber = device; 248 section_cper->CxlAgentAddress.DeviceAddress.BusNumber = 249 bus; 250 section_cper->CxlAgentAddress.DeviceAddress 251 .SegmentNumber = segment; 252 } else if (section_cper->CxlAgentType == 253 CXL_PROTOCOL_ERROR_HOST_DOWNSTREAM_PORT_AGENT) { 254 //Plain RCRB base address. 255 section_cper->CxlAgentAddress.PortRcrbBaseAddress = 256 json_object_get_uint64(json_object_object_get( 257 address, "value")); 258 } 259 add_to_valid_bitfield(&ui64Type, 1); 260 } 261 262 //Device ID information. 263 if (json_object_object_get_ex(section, "deviceID", &obj)) { 264 json_object *device_id = obj; 265 section_cper->DeviceId.VendorId = json_object_get_uint64( 266 json_object_object_get(device_id, "vendorID")); 267 section_cper->DeviceId.DeviceId = json_object_get_uint64( 268 json_object_object_get(device_id, "deviceID")); 269 section_cper->DeviceId.SubsystemVendorId = 270 json_object_get_uint64(json_object_object_get( 271 device_id, "subsystemVendorID")); 272 section_cper->DeviceId.SubsystemDeviceId = 273 json_object_get_uint64(json_object_object_get( 274 device_id, "subsystemDeviceID")); 275 section_cper->DeviceId.ClassCode = json_object_get_uint64( 276 json_object_object_get(device_id, "classCode")); 277 section_cper->DeviceId.SlotNumber = json_object_get_uint64( 278 json_object_object_get(device_id, "slotNumber")); 279 add_to_valid_bitfield(&ui64Type, 2); 280 } 281 282 //If CXL 1.1 device, the serial number & PCI capability structure. 283 UINT8 *decoded; 284 if (section_cper->CxlAgentType == CXL_PROTOCOL_ERROR_DEVICE_AGENT) { 285 if (json_object_object_get_ex(section, "deviceSerial", &obj)) { 286 section_cper->DeviceSerial = 287 json_object_get_uint64(obj); 288 add_to_valid_bitfield(&ui64Type, 3); 289 } 290 if (json_object_object_get_ex(section, "capabilityStructure", 291 &obj)) { 292 json_object *encoded = obj; 293 294 int32_t decoded_len = 0; 295 296 decoded = base64_decode( 297 json_object_get_string(encoded), 298 json_object_get_string_len(encoded), 299 &decoded_len); 300 301 if (decoded == NULL) { 302 cper_print_log( 303 "Failed to allocate decode output buffer. \n"); 304 } else { 305 memcpy(section_cper->CapabilityStructure.PcieCap, 306 decoded, decoded_len); 307 free(decoded); 308 add_to_valid_bitfield(&ui64Type, 4); 309 } 310 } 311 } 312 313 //DVSEC length & error log length. 314 section_cper->CxlDvsecLength = (UINT16)json_object_get_int( 315 json_object_object_get(section, "dvsecLength")); 316 section_cper->CxlErrorLogLength = (UINT16)json_object_get_int( 317 json_object_object_get(section, "errorLogLength")); 318 319 json_object *encodedsrc = NULL; 320 json_object *encodederr = NULL; 321 322 //DVSEC out: write valid bits 323 if (json_object_object_get_ex(section, "cxlDVSEC", &obj)) { 324 add_to_valid_bitfield(&ui64Type, 5); 325 encodedsrc = obj; 326 } 327 328 //Error log: write valid bits 329 if (json_object_object_get_ex(section, "cxlErrorLog", &obj)) { 330 add_to_valid_bitfield(&ui64Type, 6); 331 encodederr = obj; 332 } 333 section_cper->ValidBits = ui64Type.value.ui64; 334 335 //Write header to stream. 336 fwrite(section_cper, sizeof(EFI_CXL_PROTOCOL_ERROR_DATA), 1, out); 337 fflush(out); 338 339 //DVSEC out to stream. 340 int32_t decoded_len = 0; 341 if (encodedsrc != NULL) { 342 decoded = base64_decode(json_object_get_string(encodedsrc), 343 json_object_get_string_len(encodedsrc), 344 &decoded_len); 345 if (decoded == NULL) { 346 cper_print_log( 347 "Failed to allocate decode output buffer. \n"); 348 } else { 349 fwrite(decoded, decoded_len, 1, out); 350 fflush(out); 351 free(decoded); 352 } 353 } 354 355 //Error log out to stream. 356 decoded_len = 0; 357 if (encodederr != NULL) { 358 decoded = base64_decode(json_object_get_string(encodederr), 359 json_object_get_string_len(encodederr), 360 &decoded_len); 361 if (decoded == NULL) { 362 cper_print_log( 363 "Failed to allocate decode output buffer. \n"); 364 } else { 365 fwrite(decoded, decoded_len, 1, out); 366 fflush(out); 367 free(decoded); 368 } 369 } 370 371 free(section_cper); 372 } 373