1 /** 2 * Describes utility functions for parsing CPER into JSON IR. 3 * 4 * Author: Lawrence.Tang@arm.com 5 **/ 6 7 #include <ctype.h> 8 #include <stdio.h> 9 #include <json.h> 10 #include <string.h> 11 #include <libcper/Cper.h> 12 #include <libcper/cper-utils.h> 13 #include <libcper/log.h> 14 15 //The available severity types for CPER. 16 const char *CPER_SEVERITY_TYPES[4] = { "Recoverable", "Fatal", "Corrected", 17 "Informational" }; 18 19 //Converts the given generic CPER error status to JSON IR. 20 json_object * 21 cper_generic_error_status_to_ir(EFI_GENERIC_ERROR_STATUS *error_status) 22 { 23 json_object *error_status_ir = json_object_new_object(); 24 25 //Error type. 26 json_object_object_add(error_status_ir, "errorType", 27 integer_to_readable_pair_with_desc( 28 error_status->Type, 18, 29 CPER_GENERIC_ERROR_TYPES_KEYS, 30 CPER_GENERIC_ERROR_TYPES_VALUES, 31 CPER_GENERIC_ERROR_TYPES_DESCRIPTIONS, 32 "Unknown (Reserved)")); 33 34 //Boolean bit fields. 35 json_object_object_add( 36 error_status_ir, "addressSignal", 37 json_object_new_boolean(error_status->AddressSignal)); 38 json_object_object_add( 39 error_status_ir, "controlSignal", 40 json_object_new_boolean(error_status->ControlSignal)); 41 json_object_object_add( 42 error_status_ir, "dataSignal", 43 json_object_new_boolean(error_status->DataSignal)); 44 json_object_object_add( 45 error_status_ir, "detectedByResponder", 46 json_object_new_boolean(error_status->DetectedByResponder)); 47 json_object_object_add( 48 error_status_ir, "detectedByRequester", 49 json_object_new_boolean(error_status->DetectedByRequester)); 50 json_object_object_add( 51 error_status_ir, "firstError", 52 json_object_new_boolean(error_status->FirstError)); 53 json_object_object_add( 54 error_status_ir, "overflowDroppedLogs", 55 json_object_new_boolean(error_status->OverflowNotLogged)); 56 57 return error_status_ir; 58 } 59 60 //Converts the given CPER-JSON generic error status into a CPER structure. 61 void ir_generic_error_status_to_cper( 62 json_object *error_status, EFI_GENERIC_ERROR_STATUS *error_status_cper) 63 { 64 error_status_cper->Type = readable_pair_to_integer( 65 json_object_object_get(error_status, "errorType")); 66 error_status_cper->AddressSignal = json_object_get_boolean( 67 json_object_object_get(error_status, "addressSignal")); 68 error_status_cper->ControlSignal = json_object_get_boolean( 69 json_object_object_get(error_status, "controlSignal")); 70 error_status_cper->DataSignal = json_object_get_boolean( 71 json_object_object_get(error_status, "dataSignal")); 72 error_status_cper->DetectedByResponder = json_object_get_boolean( 73 json_object_object_get(error_status, "detectedByResponder")); 74 error_status_cper->DetectedByRequester = json_object_get_boolean( 75 json_object_object_get(error_status, "detectedByRequester")); 76 error_status_cper->FirstError = json_object_get_boolean( 77 json_object_object_get(error_status, "firstError")); 78 error_status_cper->OverflowNotLogged = json_object_get_boolean( 79 json_object_object_get(error_status, "overflowDroppedLogs")); 80 } 81 82 //Converts a single uniform struct of UINT64s into intermediate JSON IR format, given names for each field in byte order. 83 json_object *uniform_struct64_to_ir(UINT64 *start, int len, const char *names[]) 84 { 85 json_object *result = json_object_new_object(); 86 87 UINT64 *cur = start; 88 for (int i = 0; i < len; i++) { 89 json_object_object_add(result, names[i], 90 json_object_new_uint64(*cur)); 91 cur++; 92 } 93 94 return result; 95 } 96 97 //Converts a single uniform struct of UINT32s into intermediate JSON IR format, given names for each field in byte order. 98 json_object *uniform_struct_to_ir(UINT32 *start, int len, const char *names[]) 99 { 100 json_object *result = json_object_new_object(); 101 102 UINT32 *cur = start; 103 for (int i = 0; i < len; i++) { 104 UINT32 value; 105 memcpy(&value, cur, sizeof(UINT32)); 106 json_object_object_add(result, names[i], 107 json_object_new_uint64(value)); 108 cur++; 109 } 110 111 return result; 112 } 113 114 //Converts a single object containing UINT32s into a uniform struct. 115 void ir_to_uniform_struct64(json_object *ir, UINT64 *start, int len, 116 const char *names[]) 117 { 118 UINT64 *cur = start; 119 for (int i = 0; i < len; i++) { 120 *cur = json_object_get_uint64( 121 json_object_object_get(ir, names[i])); 122 cur++; 123 } 124 } 125 126 //Converts a single object containing UINT32s into a uniform struct. 127 void ir_to_uniform_struct(json_object *ir, UINT32 *start, int len, 128 const char *names[]) 129 { 130 UINT32 *cur = start; 131 for (int i = 0; i < len; i++) { 132 *cur = (UINT32)json_object_get_uint64( 133 json_object_object_get(ir, names[i])); 134 cur++; 135 } 136 } 137 138 //Converts a single integer value to an object containing a value, and a readable name if possible. 139 json_object *integer_to_readable_pair(UINT64 value, int len, const int keys[], 140 const char *values[], 141 const char *default_value) 142 { 143 json_object *result = json_object_new_object(); 144 json_object_object_add(result, "value", json_object_new_uint64(value)); 145 146 //Search for human readable name, add. 147 const char *name = default_value; 148 for (int i = 0; i < len; i++) { 149 if ((UINT64)keys[i] == value) { 150 name = values[i]; 151 } 152 } 153 154 json_object_object_add(result, "name", json_object_new_string(name)); 155 return result; 156 } 157 158 //Converts a single integer value to an object containing a value, readable name and description if possible. 159 json_object *integer_to_readable_pair_with_desc(int value, int len, 160 const int keys[], 161 const char *values[], 162 const char *descriptions[], 163 const char *default_value) 164 { 165 json_object *result = json_object_new_object(); 166 json_object_object_add(result, "value", json_object_new_int(value)); 167 168 //Search for human readable name, add. 169 const char *name = default_value; 170 for (int i = 0; i < len; i++) { 171 if (keys[i] == value) { 172 name = values[i]; 173 json_object_object_add( 174 result, "description", 175 json_object_new_string(descriptions[i])); 176 } 177 } 178 179 json_object_object_add(result, "name", json_object_new_string(name)); 180 return result; 181 } 182 183 //Returns a single UINT64 value from the given readable pair object. 184 //Assumes the integer value is held in the "value" field. 185 UINT64 readable_pair_to_integer(json_object *pair) 186 { 187 return json_object_get_uint64(json_object_object_get(pair, "value")); 188 } 189 190 //Converts the given 64 bit bitfield to IR, assuming bit 0 starts on the left. 191 json_object *bitfield_to_ir(UINT64 bitfield, int num_fields, 192 const char *names[]) 193 { 194 json_object *result = json_object_new_object(); 195 for (int i = 0; i < num_fields; i++) { 196 json_object_object_add(result, names[i], 197 json_object_new_boolean((bitfield >> i) & 198 0x1)); 199 } 200 201 return result; 202 } 203 204 //Filters properties based on Validation Bits. 205 // Refer to CPER spec for vbit_idx to be passed here. 206 void add_to_valid_bitfield(ValidationTypes *val, int vbit_idx) 207 { 208 switch (val->size) { 209 case UINT_8T: 210 val->value.ui8 |= (0x01 << vbit_idx); 211 break; 212 case UINT_16T: 213 val->value.ui16 |= (0x01 << vbit_idx); 214 break; 215 case UINT_32T: 216 val->value.ui32 |= (0x01 << vbit_idx); 217 break; 218 case UINT_64T: 219 val->value.ui64 |= (0x01 << vbit_idx); 220 break; 221 default: 222 cper_print_log( 223 "IR to CPER: Unknown validation bits size passed, Enum IntType=%d", 224 val->size); 225 } 226 } 227 228 //Converts the given IR bitfield into a standard UINT64 bitfield, with fields beginning from bit 0. 229 UINT64 ir_to_bitfield(json_object *ir, int num_fields, const char *names[]) 230 { 231 UINT64 result = 0x0; 232 for (int i = 0; i < num_fields; i++) { 233 if (json_object_get_boolean( 234 json_object_object_get(ir, names[i]))) { 235 result |= (0x1 << i); 236 } 237 } 238 239 return result; 240 } 241 242 // Filters properties based on Validation Bits. 243 // Refer to CPER spec for vbit_idx to be passed here. 244 // Overload function for 16, 32, 64b 245 bool isvalid_prop_to_ir(ValidationTypes *val, int vbit_idx) 246 { 247 // If the option is enabled, output invalid properties 248 // as well as valid ones. 249 #ifdef OUTPUT_ALL_PROPERTIES 250 return true; 251 #endif //OUTPUT_ALL_PROPERTIES 252 UINT64 vbit_mask = 0x01 << vbit_idx; 253 switch (val->size) { 254 case UINT_16T: 255 return (vbit_mask & val->value.ui16); 256 257 case UINT_32T: 258 return (vbit_mask & val->value.ui32); 259 260 case UINT_64T: 261 return (vbit_mask & val->value.ui64); 262 263 default: 264 cper_print_log( 265 "CPER to IR:Unknown validation bits size passed. Enum IntType: %d", 266 val->size); 267 } 268 return 0; 269 } 270 271 void print_val(ValidationTypes *val) 272 { 273 switch (val->size) { 274 case UINT_8T: 275 cper_print_log("Validation bits: %x\n", val->value.ui8); 276 break; 277 case UINT_16T: 278 cper_print_log("Validation bits: %x\n", val->value.ui16); 279 break; 280 281 case UINT_32T: 282 cper_print_log("Validation bits: %x\n", val->value.ui32); 283 break; 284 285 case UINT_64T: 286 cper_print_log("Validation bits: %llx\n", val->value.ui64); 287 break; 288 289 default: 290 cper_print_log( 291 "CPER to IR:Unknown validation bits size passed. Enum IntType: %d", 292 val->size); 293 } 294 } 295 296 //Converts the given UINT64 array into a JSON IR array, given the length. 297 json_object *uint64_array_to_ir_array(UINT64 *array, int len) 298 { 299 json_object *array_ir = json_object_new_array(); 300 for (int i = 0; i < len; i++) { 301 json_object_array_add(array_ir, 302 json_object_new_uint64(array[i])); 303 } 304 return array_ir; 305 } 306 307 //Converts a single UINT16 revision number into JSON IR representation. 308 json_object *revision_to_ir(UINT16 revision) 309 { 310 json_object *revision_info = json_object_new_object(); 311 json_object_object_add(revision_info, "major", 312 json_object_new_int(revision >> 8)); 313 json_object_object_add(revision_info, "minor", 314 json_object_new_int(revision & 0xFF)); 315 return revision_info; 316 } 317 318 //Returns the appropriate string for the given integer severity. 319 const char *severity_to_string(UINT32 severity) 320 { 321 return severity < 4 ? CPER_SEVERITY_TYPES[severity] : "Unknown"; 322 } 323 324 //Converts a single EFI timestamp to string, at the given output. 325 //Output must be at least TIMESTAMP_LENGTH bytes long. 326 int timestamp_to_string(char *out, int out_len, EFI_ERROR_TIME_STAMP *timestamp) 327 { 328 //Cannot go to three digits. 329 int century = bcd_to_int(timestamp->Century) % 100; 330 if (century >= 100) { 331 return -1; 332 } 333 int year = bcd_to_int(timestamp->Year) % 100; 334 if (year >= 100) { 335 return -1; 336 } 337 int month = bcd_to_int(timestamp->Month); 338 if (month > 12) { 339 return -1; 340 } 341 int day = bcd_to_int(timestamp->Day); 342 if (day > 31) { 343 return -1; 344 } 345 int hours = bcd_to_int(timestamp->Hours); 346 if (hours > 24) { 347 return -1; 348 } 349 int minutes = bcd_to_int(timestamp->Minutes); 350 if (minutes > 60) { 351 return -1; 352 } 353 int seconds = bcd_to_int(timestamp->Seconds); 354 if (seconds >= 60) { 355 return -1; 356 } 357 int written = snprintf( 358 out, out_len, 359 "%02hhu%02hhu-%02hhu-%02hhuT%02hhu:%02hhu:%02hhu+00:00", 360 century, year, month, day, hours, minutes, seconds); 361 362 if (written < 0 || written >= out_len) { 363 cper_print_log("Timestamp buffer of insufficient size\n"); 364 return -1; 365 } 366 return 0; 367 } 368 369 //Converts a single timestamp string to an EFI timestamp. 370 void string_to_timestamp(EFI_ERROR_TIME_STAMP *out, const char *timestamp) 371 { 372 //Ignore invalid timestamps. 373 if (timestamp == NULL) { 374 return; 375 } 376 377 sscanf(timestamp, "%2hhu%2hhu-%hhu-%hhuT%hhu:%hhu:%hhu+00:00", 378 &out->Century, &out->Year, &out->Month, &out->Day, &out->Hours, 379 &out->Minutes, &out->Seconds); 380 381 //Convert back to BCD. 382 out->Century = int_to_bcd(out->Century); 383 out->Year = int_to_bcd(out->Year); 384 out->Month = int_to_bcd(out->Month); 385 out->Day = int_to_bcd(out->Day); 386 out->Hours = int_to_bcd(out->Hours); 387 out->Minutes = int_to_bcd(out->Minutes); 388 out->Seconds = int_to_bcd(out->Seconds); 389 } 390 391 //Helper function to convert an EDK EFI GUID into a string for intermediate use. 392 int guid_to_string(char *out, size_t out_len, EFI_GUID *guid) 393 { 394 size_t len = snprintf( 395 out, out_len, 396 "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", guid->Data1, 397 guid->Data2, guid->Data3, guid->Data4[0], guid->Data4[1], 398 guid->Data4[2], guid->Data4[3], guid->Data4[4], guid->Data4[5], 399 guid->Data4[6], guid->Data4[7]); 400 if (len != out_len) { 401 return -1; 402 } 403 return len; 404 } 405 406 //Helper function to convert a string into an EDK EFI GUID. 407 void string_to_guid(EFI_GUID *out, const char *guid) 408 { 409 //Ignore invalid GUIDs. 410 if (guid == NULL) { 411 return; 412 } 413 414 sscanf(guid, 415 "%08x-%04hx-%04hx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx", 416 &out->Data1, &out->Data2, &out->Data3, out->Data4, 417 out->Data4 + 1, out->Data4 + 2, out->Data4 + 3, out->Data4 + 4, 418 out->Data4 + 5, out->Data4 + 6, out->Data4 + 7); 419 } 420 421 //Returns one if two EFI GUIDs are equal, zero otherwise. 422 int guid_equal(EFI_GUID *a, EFI_GUID *b) 423 { 424 //Check top base 3 components. 425 if (a->Data1 != b->Data1 || a->Data2 != b->Data2 || 426 a->Data3 != b->Data3) { 427 return 0; 428 } 429 430 //Check Data4 array for equality. 431 for (int i = 0; i < 8; i++) { 432 if (a->Data4[i] != b->Data4[i]) { 433 return 0; 434 } 435 } 436 437 return 1; 438 } 439 440 int select_guid_from_list(EFI_GUID *guid, EFI_GUID *guid_list[], int len) 441 { 442 int i = 0; 443 for (; i < len; i++) { 444 if (guid_equal(guid, guid_list[i])) { 445 break; 446 } 447 } 448 // It's unlikely fuzzing can reliably come up with a correct guid, given how 449 // much entropy there is. If we're in fuzzing mode, and if we haven't found 450 // a match, try to force a match so we get some coverage. Note, we still 451 // want coverage of the section failed to convert code, so treat index == 452 // size as section failed to convert. 453 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION 454 if (i == len) { 455 i = guid->Data1 % (len + 1); 456 } 457 #endif 458 459 return i; 460 } 461 462 void add_untrusted_string(json_object *ir, const char *field_name, 463 const char *str, int len) 464 { 465 int fru_text_len = 0; 466 for (; fru_text_len < len; fru_text_len++) { 467 char c = str[fru_text_len]; 468 if (c == '\0') { 469 break; 470 } 471 if (!isprint(c)) { 472 fru_text_len = -1; 473 break; 474 } 475 } 476 if (fru_text_len >= 0) { 477 json_object_object_add( 478 ir, field_name, 479 json_object_new_string_len(str, fru_text_len)); 480 } 481 } 482 483 void add_guid(json_object *ir, const char *field_name, EFI_GUID *guid) 484 { 485 char platform_string[GUID_STRING_LENGTH + 1]; 486 if (!guid_to_string(platform_string, sizeof(platform_string), guid)) { 487 return; 488 } 489 json_object_object_add( 490 ir, field_name, 491 json_object_new_string_len(platform_string, 492 sizeof(platform_string) - 1)); 493 } 494 495 void add_int(json_object *register_ir, const char *field_name, int value) 496 { 497 json_object_object_add(register_ir, field_name, 498 json_object_new_uint64(value)); 499 } 500 501 void add_bool(json_object *register_ir, const char *field_name, UINT64 value) 502 { 503 json_object_object_add(register_ir, field_name, 504 json_object_new_boolean(value)); 505 } 506 507 void add_bool_enum(json_object *register_ir, const char *field_name, 508 const char *value_dict[2], UINT64 value_int) 509 { 510 const char *value = value_dict[0]; 511 if (value_int > 0) { 512 value = value_dict[1]; 513 } 514 json_object_object_add(register_ir, field_name, 515 json_object_new_string(value)); 516 } 517 518 void add_dict(json_object *register_ir, const char *field_name, UINT64 value, 519 const char *dict[], size_t dict_size) 520 { 521 json_object *field_ir = json_object_new_object(); 522 json_object_object_add(register_ir, field_name, field_ir); 523 json_object_object_add(field_ir, "raw", json_object_new_uint64(value)); 524 525 if (dict != NULL) { 526 if (value < dict_size) { 527 const char *name = dict[value]; 528 if (name != NULL) { 529 const char *value_name = name; 530 531 json_object_object_add( 532 field_ir, "value", 533 json_object_new_string(value_name)); 534 } 535 } 536 } 537 } 538