1 /** 2 * Describes high level functions for converting an entire CPER log, and functions for parsing 3 * CPER headers and section descriptions into an intermediate JSON format. 4 * 5 * Author: Lawrence.Tang@arm.com 6 **/ 7 8 #include <stdio.h> 9 #include <string.h> 10 #include <json.h> 11 #include "base64.h" 12 #include "edk/Cper.h" 13 #include "cper-parse.h" 14 #include "cper-parse-str.h" 15 #include "cper-utils.h" 16 #include "sections/cper-section.h" 17 18 //Private pre-definitions. 19 json_object *cper_header_to_ir(EFI_COMMON_ERROR_RECORD_HEADER *header); 20 json_object * 21 cper_section_descriptor_to_ir(EFI_ERROR_SECTION_DESCRIPTOR *section_descriptor); 22 json_object *cper_section_to_ir(FILE *handle, long base_pos, 23 EFI_ERROR_SECTION_DESCRIPTOR *descriptor); 24 25 //Reads a CPER log file at the given file location, and returns an intermediate 26 //JSON representation of this CPER record. 27 json_object *cper_to_ir(FILE *cper_file) 28 { 29 //Read the current file pointer location as the base of the record. 30 long base_pos = ftell(cper_file); 31 32 //Ensure this is really a CPER log. 33 EFI_COMMON_ERROR_RECORD_HEADER header; 34 if (fread(&header, sizeof(EFI_COMMON_ERROR_RECORD_HEADER), 1, 35 cper_file) != 1) { 36 printf("Invalid CPER file: Invalid length (log too short).\n"); 37 return NULL; 38 } 39 40 //Check if the header contains the magic bytes ("CPER"). 41 if (header.SignatureStart != EFI_ERROR_RECORD_SIGNATURE_START) { 42 printf("Invalid CPER file: Invalid header (incorrect signature).\n"); 43 return NULL; 44 } 45 46 //Create the header JSON object from the read bytes. 47 json_object *header_ir = cper_header_to_ir(&header); 48 49 //Read the appropriate number of section descriptors & sections, and convert them into IR format. 50 json_object *section_descriptors_ir = json_object_new_array(); 51 json_object *sections_ir = json_object_new_array(); 52 for (int i = 0; i < header.SectionCount; i++) { 53 //Create the section descriptor. 54 EFI_ERROR_SECTION_DESCRIPTOR section_descriptor; 55 if (fread(§ion_descriptor, 56 sizeof(EFI_ERROR_SECTION_DESCRIPTOR), 1, 57 cper_file) != 1) { 58 printf("Invalid number of section headers: Header states %d sections, could not read section %d.\n", 59 header.SectionCount, i + 1); 60 // Free json objects 61 json_object_put(sections_ir); 62 json_object_put(section_descriptors_ir); 63 json_object_put(header_ir); 64 return NULL; 65 } 66 json_object_array_add( 67 section_descriptors_ir, 68 cper_section_descriptor_to_ir(§ion_descriptor)); 69 70 //Read the section itself. 71 json_object_array_add(sections_ir, 72 cper_section_to_ir(cper_file, base_pos, 73 §ion_descriptor)); 74 } 75 76 //Add the header, section descriptors, and sections to a parent object. 77 json_object *parent = json_object_new_object(); 78 json_object_object_add(parent, "header", header_ir); 79 json_object_object_add(parent, "sectionDescriptors", 80 section_descriptors_ir); 81 json_object_object_add(parent, "sections", sections_ir); 82 83 return parent; 84 } 85 86 char *cper_to_str_ir(FILE *cper_file) 87 { 88 json_object *jobj = cper_to_ir(cper_file); 89 char *str = jobj ? strdup(json_object_to_json_string(jobj)) : NULL; 90 91 json_object_put(jobj); 92 return str; 93 } 94 95 char *cperbuf_to_str_ir(const unsigned char *cper, size_t size) 96 { 97 FILE *cper_file = fmemopen((void *)cper, size, "r"); 98 99 return cper_file ? cper_to_str_ir(cper_file) : NULL; 100 } 101 102 //Converts a parsed CPER record header into intermediate JSON object format. 103 json_object *cper_header_to_ir(EFI_COMMON_ERROR_RECORD_HEADER *header) 104 { 105 json_object *header_ir = json_object_new_object(); 106 107 //Revision/version information. 108 json_object_object_add(header_ir, "revision", 109 revision_to_ir(header->Revision)); 110 111 //Section count. 112 json_object_object_add(header_ir, "sectionCount", 113 json_object_new_int(header->SectionCount)); 114 115 //Error severity (with interpreted string version). 116 json_object *error_severity = json_object_new_object(); 117 json_object_object_add(error_severity, "code", 118 json_object_new_uint64(header->ErrorSeverity)); 119 json_object_object_add(error_severity, "name", 120 json_object_new_string(severity_to_string( 121 header->ErrorSeverity))); 122 json_object_object_add(header_ir, "severity", error_severity); 123 124 //The validation bits for each section. 125 json_object *validation_bits = bitfield_to_ir( 126 header->ValidationBits, 3, CPER_HEADER_VALID_BITFIELD_NAMES); 127 json_object_object_add(header_ir, "validationBits", validation_bits); 128 129 //Total length of the record (including headers) in bytes. 130 json_object_object_add(header_ir, "recordLength", 131 json_object_new_uint64(header->RecordLength)); 132 133 //If a timestamp exists according to validation bits, then add it. 134 if (header->ValidationBits & 0x2) { 135 char timestamp_string[TIMESTAMP_LENGTH]; 136 timestamp_to_string(timestamp_string, &header->TimeStamp); 137 138 json_object_object_add( 139 header_ir, "timestamp", 140 json_object_new_string(timestamp_string)); 141 json_object_object_add( 142 header_ir, "timestampIsPrecise", 143 json_object_new_boolean(header->TimeStamp.Flag)); 144 } 145 146 //If a platform ID exists according to the validation bits, then add it. 147 if (header->ValidationBits & 0x1) { 148 char platform_string[GUID_STRING_LENGTH]; 149 guid_to_string(platform_string, &header->PlatformID); 150 json_object_object_add(header_ir, "platformID", 151 json_object_new_string(platform_string)); 152 } 153 154 //If a partition ID exists according to the validation bits, then add it. 155 if (header->ValidationBits & 0x4) { 156 char partition_string[GUID_STRING_LENGTH]; 157 guid_to_string(partition_string, &header->PartitionID); 158 json_object_object_add( 159 header_ir, "partitionID", 160 json_object_new_string(partition_string)); 161 } 162 163 //Creator ID of the header. 164 char creator_string[GUID_STRING_LENGTH]; 165 guid_to_string(creator_string, &header->CreatorID); 166 json_object_object_add(header_ir, "creatorID", 167 json_object_new_string(creator_string)); 168 169 //Notification type for the header. Some defined types are available. 170 json_object *notification_type = json_object_new_object(); 171 char notification_type_string[GUID_STRING_LENGTH]; 172 guid_to_string(notification_type_string, &header->NotificationType); 173 json_object_object_add( 174 notification_type, "guid", 175 json_object_new_string(notification_type_string)); 176 177 //Add the human readable notification type if possible. 178 char *notification_type_readable = "Unknown"; 179 if (guid_equal(&header->NotificationType, 180 &gEfiEventNotificationTypeCmcGuid)) { 181 notification_type_readable = "CMC"; 182 } else if (guid_equal(&header->NotificationType, 183 &gEfiEventNotificationTypeCpeGuid)) { 184 notification_type_readable = "CPE"; 185 } else if (guid_equal(&header->NotificationType, 186 &gEfiEventNotificationTypeMceGuid)) { 187 notification_type_readable = "MCE"; 188 } else if (guid_equal(&header->NotificationType, 189 &gEfiEventNotificationTypePcieGuid)) { 190 notification_type_readable = "PCIe"; 191 } else if (guid_equal(&header->NotificationType, 192 &gEfiEventNotificationTypeInitGuid)) { 193 notification_type_readable = "INIT"; 194 } else if (guid_equal(&header->NotificationType, 195 &gEfiEventNotificationTypeNmiGuid)) { 196 notification_type_readable = "NMI"; 197 } else if (guid_equal(&header->NotificationType, 198 &gEfiEventNotificationTypeBootGuid)) { 199 notification_type_readable = "Boot"; 200 } else if (guid_equal(&header->NotificationType, 201 &gEfiEventNotificationTypeDmarGuid)) { 202 notification_type_readable = "DMAr"; 203 } else if (guid_equal(&header->NotificationType, 204 &gEfiEventNotificationTypeSeaGuid)) { 205 notification_type_readable = "SEA"; 206 } else if (guid_equal(&header->NotificationType, 207 &gEfiEventNotificationTypeSeiGuid)) { 208 notification_type_readable = "SEI"; 209 } else if (guid_equal(&header->NotificationType, 210 &gEfiEventNotificationTypePeiGuid)) { 211 notification_type_readable = "PEI"; 212 } else if (guid_equal(&header->NotificationType, 213 &gEfiEventNotificationTypeCxlGuid)) { 214 notification_type_readable = "CXL Component"; 215 } 216 json_object_object_add( 217 notification_type, "type", 218 json_object_new_string(notification_type_readable)); 219 json_object_object_add(header_ir, "notificationType", 220 notification_type); 221 222 //The record ID for this record, unique on a given system. 223 json_object_object_add(header_ir, "recordID", 224 json_object_new_uint64(header->RecordID)); 225 226 //Flag for the record, and a human readable form. 227 json_object *flags = integer_to_readable_pair( 228 header->Flags, 229 sizeof(CPER_HEADER_FLAG_TYPES_KEYS) / sizeof(int), 230 CPER_HEADER_FLAG_TYPES_KEYS, CPER_HEADER_FLAG_TYPES_VALUES, 231 "Unknown"); 232 json_object_object_add(header_ir, "flags", flags); 233 234 //Persistence information. Outside the scope of specification, so just a uint32 here. 235 json_object_object_add(header_ir, "persistenceInfo", 236 json_object_new_uint64(header->PersistenceInfo)); 237 return header_ir; 238 } 239 240 //Converts the given EFI section descriptor into JSON IR format. 241 json_object * 242 cper_section_descriptor_to_ir(EFI_ERROR_SECTION_DESCRIPTOR *section_descriptor) 243 { 244 json_object *section_descriptor_ir = json_object_new_object(); 245 246 //The offset of the section from the base of the record header, length. 247 json_object_object_add( 248 section_descriptor_ir, "sectionOffset", 249 json_object_new_uint64(section_descriptor->SectionOffset)); 250 json_object_object_add( 251 section_descriptor_ir, "sectionLength", 252 json_object_new_uint64(section_descriptor->SectionLength)); 253 254 //Revision. 255 json_object_object_add(section_descriptor_ir, "revision", 256 revision_to_ir(section_descriptor->Revision)); 257 258 //Validation bits. 259 json_object *validation_bits = 260 bitfield_to_ir(section_descriptor->SecValidMask, 2, 261 CPER_SECTION_DESCRIPTOR_VALID_BITFIELD_NAMES); 262 json_object_object_add(section_descriptor_ir, "validationBits", 263 validation_bits); 264 265 //Flag bits. 266 json_object *flags = 267 bitfield_to_ir(section_descriptor->SectionFlags, 8, 268 CPER_SECTION_DESCRIPTOR_FLAGS_BITFIELD_NAMES); 269 json_object_object_add(section_descriptor_ir, "flags", flags); 270 271 //Section type (GUID). 272 json_object *section_type = json_object_new_object(); 273 char section_type_string[GUID_STRING_LENGTH]; 274 guid_to_string(section_type_string, §ion_descriptor->SectionType); 275 json_object_object_add(section_type, "data", 276 json_object_new_string(section_type_string)); 277 278 //Readable section type, if possible. 279 const char *section_type_readable = "Unknown"; 280 for (size_t i = 0; i < section_definitions_len; i++) { 281 if (guid_equal(section_definitions[i].Guid, 282 §ion_descriptor->SectionType)) { 283 section_type_readable = 284 section_definitions[i].ReadableName; 285 break; 286 } 287 } 288 289 json_object_object_add(section_type, "type", 290 json_object_new_string(section_type_readable)); 291 json_object_object_add(section_descriptor_ir, "sectionType", 292 section_type); 293 294 //If validation bits indicate it exists, add FRU ID. 295 if (section_descriptor->SecValidMask & 0x1) { 296 char fru_id_string[GUID_STRING_LENGTH]; 297 guid_to_string(fru_id_string, §ion_descriptor->FruId); 298 json_object_object_add(section_descriptor_ir, "fruID", 299 json_object_new_string(fru_id_string)); 300 } 301 302 //If validation bits indicate it exists, add FRU text. 303 if ((section_descriptor->SecValidMask & 0x2) >> 1) { 304 json_object_object_add( 305 section_descriptor_ir, "fruText", 306 json_object_new_string(section_descriptor->FruString)); 307 } 308 309 //Section severity. 310 json_object *section_severity = json_object_new_object(); 311 json_object_object_add( 312 section_severity, "code", 313 json_object_new_uint64(section_descriptor->Severity)); 314 json_object_object_add(section_severity, "name", 315 json_object_new_string(severity_to_string( 316 section_descriptor->Severity))); 317 json_object_object_add(section_descriptor_ir, "severity", 318 section_severity); 319 320 return section_descriptor_ir; 321 } 322 323 //Converts the section described by a single given section descriptor. 324 json_object *cper_section_to_ir(FILE *handle, long base_pos, 325 EFI_ERROR_SECTION_DESCRIPTOR *descriptor) 326 { 327 //Save our current position in the stream. 328 long position = ftell(handle); 329 330 //Read section as described by the section descriptor. 331 fseek(handle, base_pos + descriptor->SectionOffset, SEEK_SET); 332 void *section = malloc(descriptor->SectionLength); 333 if (fread(section, descriptor->SectionLength, 1, handle) != 1) { 334 printf("Section read failed: Could not read %u bytes from global offset %d.\n", 335 descriptor->SectionLength, descriptor->SectionOffset); 336 free(section); 337 return NULL; 338 } 339 340 //Seek back to our original position. 341 fseek(handle, position, SEEK_SET); 342 343 //Parse section to IR based on GUID. 344 json_object *result = NULL; 345 int section_converted = 0; 346 for (size_t i = 0; i < section_definitions_len; i++) { 347 if (guid_equal(section_definitions[i].Guid, 348 &descriptor->SectionType) && 349 section_definitions[i].ToIR != NULL) { 350 result = section_definitions[i].ToIR(section); 351 section_converted = 1; 352 break; 353 } 354 } 355 356 //Was it an unknown GUID/failed read? 357 if (!section_converted) { 358 //Output the data as formatted base64. 359 result = json_object_new_object(); 360 361 int32_t encoded_len = 0; 362 char *encoded = base64_encode( 363 section, descriptor->SectionLength, &encoded_len); 364 if (encoded == NULL) { 365 printf("Failed to allocate encode output buffer. \n"); 366 } else { 367 json_object_object_add(result, "data", 368 json_object_new_string_len( 369 encoded, encoded_len)); 370 free(encoded); 371 } 372 } 373 //Free section memory, return result. 374 free(section); 375 return result; 376 } 377 378 //Converts a single CPER section, without a header but with a section descriptor, to JSON. 379 json_object *cper_single_section_to_ir(FILE *cper_section_file) 380 { 381 json_object *ir = json_object_new_object(); 382 383 //Read the current file pointer location as base record position. 384 long base_pos = ftell(cper_section_file); 385 386 //Read the section descriptor out. 387 EFI_ERROR_SECTION_DESCRIPTOR section_descriptor; 388 if (fread(§ion_descriptor, sizeof(EFI_ERROR_SECTION_DESCRIPTOR), 1, 389 cper_section_file) != 1) { 390 printf("Failed to read section descriptor for CPER single section (fread() returned an unexpected value).\n"); 391 return NULL; 392 } 393 394 //Convert the section descriptor to IR. 395 json_object *section_descriptor_ir = 396 cper_section_descriptor_to_ir(§ion_descriptor); 397 json_object_object_add(ir, "sectionDescriptor", section_descriptor_ir); 398 399 //Parse the single section. 400 json_object *section_ir = cper_section_to_ir( 401 cper_section_file, base_pos, §ion_descriptor); 402 json_object_object_add(ir, "section", section_ir); 403 404 return ir; 405 } 406 407 char *cper_single_section_to_str_ir(FILE *cper_section_file) 408 { 409 json_object *jobj = cper_single_section_to_ir(cper_section_file); 410 char *str = jobj ? strdup(json_object_to_json_string(jobj)) : NULL; 411 412 json_object_put(jobj); 413 return str; 414 } 415 416 char *cperbuf_single_section_to_str_ir(const unsigned char *cper_section, 417 size_t size) 418 { 419 FILE *cper_section_file = fmemopen((void *)cper_section, size, "r"); 420 421 return cper_section_file ? 422 cper_single_section_to_str_ir(cper_section_file) : 423 NULL; 424 } 425