Lawrence Tang | 1b0b00e | 2022-07-05 10:33:10 +0100 | [diff] [blame] | 1 | /** |
| 2 | * Describes utility functions for parsing CPER into JSON IR. |
| 3 | * |
| 4 | * Author: Lawrence.Tang@arm.com |
| 5 | **/ |
| 6 | |
| 7 | #include <stdio.h> |
| 8 | #include "json.h" |
| 9 | #include "edk/Cper.h" |
| 10 | #include "cper-utils.h" |
| 11 | |
| 12 | //The available severity types for CPER. |
| 13 | const char* CPER_SEVERITY_TYPES[4] = {"Recoverable", "Fatal", "Corrected", "Informational"}; |
| 14 | |
Lawrence Tang | a0865e3 | 2022-07-06 11:59:52 +0100 | [diff] [blame] | 15 | //Converts the given generic CPER error status to JSON IR. |
| 16 | json_object* cper_generic_error_status_to_ir(EFI_GENERIC_ERROR_STATUS* error_status) |
| 17 | { |
| 18 | json_object* error_status_ir = json_object_new_object(); |
| 19 | |
| 20 | //Error type. |
| 21 | json_object_object_add(error_status_ir, "errorType", integer_to_readable_pair_with_desc(error_status->Type, 18, |
| 22 | CPER_GENERIC_ERROR_TYPES_KEYS, |
| 23 | CPER_GENERIC_ERROR_TYPES_VALUES, |
| 24 | CPER_GENERIC_ERROR_TYPES_DESCRIPTIONS, |
| 25 | "Unknown (Reserved)")); |
| 26 | |
| 27 | //Boolean bit fields. |
| 28 | json_object_object_add(error_status_ir, "addressSignal", json_object_new_boolean(error_status->AddressSignal)); |
| 29 | json_object_object_add(error_status_ir, "controlSignal", json_object_new_boolean(error_status->ControlSignal)); |
| 30 | json_object_object_add(error_status_ir, "dataSignal", json_object_new_boolean(error_status->DataSignal)); |
| 31 | json_object_object_add(error_status_ir, "detectedByResponder", json_object_new_boolean(error_status->DetectedByResponder)); |
| 32 | json_object_object_add(error_status_ir, "detectedByRequester", json_object_new_boolean(error_status->DetectedByRequester)); |
| 33 | json_object_object_add(error_status_ir, "firstError", json_object_new_boolean(error_status->FirstError)); |
Lawrence Tang | 583cdee | 2022-07-11 14:31:11 +0100 | [diff] [blame] | 34 | json_object_object_add(error_status_ir, "overflowDroppedLogs", json_object_new_boolean(error_status->OverflowNotLogged)); |
Lawrence Tang | a0865e3 | 2022-07-06 11:59:52 +0100 | [diff] [blame] | 35 | |
| 36 | return error_status_ir; |
| 37 | } |
| 38 | |
Lawrence Tang | 3b7f45b | 2022-07-14 14:14:30 +0100 | [diff] [blame] | 39 | //Converts the given CPER-JSON generic error status into a CPER structure. |
| 40 | void ir_generic_error_status_to_cper(json_object* error_status, EFI_GENERIC_ERROR_STATUS* error_status_cper) |
| 41 | { |
| 42 | error_status_cper->Type = readable_pair_to_integer(json_object_object_get(error_status, "errorType")); |
| 43 | error_status_cper->AddressSignal = json_object_get_boolean(json_object_object_get(error_status, "addressSignal")); |
| 44 | error_status_cper->ControlSignal = json_object_get_boolean(json_object_object_get(error_status, "controlSignal")); |
| 45 | error_status_cper->DataSignal = json_object_get_boolean(json_object_object_get(error_status, "dataSignal")); |
| 46 | error_status_cper->DetectedByResponder = json_object_get_boolean(json_object_object_get(error_status, "detectedByResponder")); |
| 47 | error_status_cper->DetectedByRequester = json_object_get_boolean(json_object_object_get(error_status, "detectedByRequester")); |
| 48 | error_status_cper->FirstError = json_object_get_boolean(json_object_object_get(error_status, "firstError")); |
| 49 | error_status_cper->OverflowNotLogged = json_object_get_boolean(json_object_object_get(error_status, "overflowDroppedLogs")); |
| 50 | } |
| 51 | |
Lawrence Tang | 7f21db6 | 2022-07-06 11:09:39 +0100 | [diff] [blame] | 52 | //Converts a single uniform struct of UINT64s into intermediate JSON IR format, given names for each field in byte order. |
| 53 | json_object* uniform_struct64_to_ir(UINT64* start, int len, const char* names[]) |
| 54 | { |
| 55 | json_object* result = json_object_new_object(); |
| 56 | |
| 57 | UINT64* cur = start; |
| 58 | for (int i=0; i<len; i++) |
| 59 | { |
| 60 | json_object_object_add(result, names[i], json_object_new_uint64(*cur)); |
| 61 | cur++; |
| 62 | } |
| 63 | |
| 64 | return result; |
| 65 | } |
| 66 | |
| 67 | //Converts a single uniform struct of UINT32s into intermediate JSON IR format, given names for each field in byte order. |
| 68 | json_object* uniform_struct_to_ir(UINT32* start, int len, const char* names[]) |
| 69 | { |
| 70 | json_object* result = json_object_new_object(); |
| 71 | |
| 72 | UINT32* cur = start; |
| 73 | for (int i=0; i<len; i++) |
| 74 | { |
| 75 | json_object_object_add(result, names[i], json_object_new_uint64(*cur)); |
| 76 | cur++; |
| 77 | } |
| 78 | |
| 79 | return result; |
| 80 | } |
| 81 | |
Lawrence Tang | 71570a2 | 2022-07-14 11:45:28 +0100 | [diff] [blame] | 82 | //Converts a single object containing UINT32s into a uniform struct. |
| 83 | void ir_to_uniform_struct64(json_object* ir, UINT64* start, int len, const char* names[]) |
| 84 | { |
| 85 | UINT64* cur = start; |
| 86 | for (int i=0; i<len; i++) |
| 87 | { |
| 88 | *cur = json_object_get_uint64(json_object_object_get(ir, names[i])); |
| 89 | cur++; |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | //Converts a single object containing UINT32s into a uniform struct. |
| 94 | void ir_to_uniform_struct(json_object* ir, UINT32* start, int len, const char* names[]) |
| 95 | { |
| 96 | UINT32* cur = start; |
| 97 | for (int i=0; i<len; i++) |
| 98 | { |
| 99 | *cur = (UINT32)json_object_get_uint64(json_object_object_get(ir, names[i])); |
| 100 | cur++; |
| 101 | } |
| 102 | } |
| 103 | |
Lawrence Tang | 3c43f74 | 2022-07-05 11:37:17 +0100 | [diff] [blame] | 104 | //Converts a single integer value to an object containing a value, and a readable name if possible. |
Lawrence Tang | 3c87835 | 2022-07-08 14:04:50 +0100 | [diff] [blame] | 105 | json_object* integer_to_readable_pair(UINT64 value, int len, int keys[], const char* values[], const char* default_value) |
Lawrence Tang | 3c43f74 | 2022-07-05 11:37:17 +0100 | [diff] [blame] | 106 | { |
| 107 | json_object* result = json_object_new_object(); |
Lawrence Tang | 3c87835 | 2022-07-08 14:04:50 +0100 | [diff] [blame] | 108 | json_object_object_add(result, "value", json_object_new_uint64(value)); |
Lawrence Tang | 3c43f74 | 2022-07-05 11:37:17 +0100 | [diff] [blame] | 109 | |
| 110 | //Search for human readable name, add. |
Lawrence Tang | 794312c | 2022-07-05 14:46:10 +0100 | [diff] [blame] | 111 | const char* name = default_value; |
Lawrence Tang | 3c43f74 | 2022-07-05 11:37:17 +0100 | [diff] [blame] | 112 | for (int i=0; i<len; i++) |
| 113 | { |
| 114 | if (keys[i] == value) |
| 115 | name = values[i]; |
| 116 | } |
| 117 | |
| 118 | json_object_object_add(result, "name", json_object_new_string(name)); |
| 119 | return result; |
| 120 | } |
| 121 | |
Lawrence Tang | 7f21db6 | 2022-07-06 11:09:39 +0100 | [diff] [blame] | 122 | //Converts a single integer value to an object containing a value, readable name and description if possible. |
| 123 | json_object* integer_to_readable_pair_with_desc(int value, int len, int keys[], const char* values[], |
| 124 | const char* descriptions[], const char* default_value) |
| 125 | { |
| 126 | json_object* result = json_object_new_object(); |
| 127 | json_object_object_add(result, "value", json_object_new_int(value)); |
| 128 | |
| 129 | //Search for human readable name, add. |
| 130 | const char* name = default_value; |
| 131 | for (int i=0; i<len; i++) |
| 132 | { |
| 133 | if (keys[i] == value) |
| 134 | { |
| 135 | name = values[i]; |
| 136 | json_object_object_add(result, "description", json_object_new_string(descriptions[i])); |
| 137 | } |
| 138 | } |
| 139 | |
| 140 | json_object_object_add(result, "name", json_object_new_string(name)); |
| 141 | return result; |
| 142 | } |
| 143 | |
Lawrence Tang | b44314c | 2022-07-13 11:45:22 +0100 | [diff] [blame] | 144 | //Returns a single UINT64 value from the given readable pair object. |
| 145 | //Assumes the integer value is held in the "value" field. |
| 146 | UINT64 readable_pair_to_integer(json_object* pair) |
| 147 | { |
| 148 | return json_object_get_uint64(json_object_object_get(pair, "value")); |
| 149 | } |
| 150 | |
Lawrence Tang | 794312c | 2022-07-05 14:46:10 +0100 | [diff] [blame] | 151 | //Converts the given 64 bit bitfield to IR, assuming bit 0 starts on the left. |
Lawrence Tang | 3d0e4f2 | 2022-07-05 17:17:41 +0100 | [diff] [blame] | 152 | json_object* bitfield_to_ir(UINT64 bitfield, int num_fields, const char* names[]) |
Lawrence Tang | 794312c | 2022-07-05 14:46:10 +0100 | [diff] [blame] | 153 | { |
| 154 | json_object* result = json_object_new_object(); |
| 155 | for (int i=0; i<num_fields; i++) |
| 156 | { |
Lawrence Tang | 2800cd8 | 2022-07-05 16:08:20 +0100 | [diff] [blame] | 157 | json_object_object_add(result, names[i], json_object_new_boolean((bitfield >> i) & 0b1)); |
Lawrence Tang | 794312c | 2022-07-05 14:46:10 +0100 | [diff] [blame] | 158 | } |
| 159 | |
| 160 | return result; |
| 161 | } |
| 162 | |
Lawrence Tang | b44314c | 2022-07-13 11:45:22 +0100 | [diff] [blame] | 163 | //Converts the given IR bitfield into a standard UINT64 bitfield, with fields beginning from bit 0. |
| 164 | UINT64 ir_to_bitfield(json_object* ir, int num_fields, const char* names[]) |
| 165 | { |
| 166 | UINT64 result = 0x0; |
| 167 | for (int i=0; i<num_fields; i++) |
| 168 | { |
| 169 | if (json_object_get_boolean(json_object_object_get(ir, names[i]))) |
| 170 | result |= (0x1 << i); |
| 171 | } |
| 172 | |
| 173 | return result; |
| 174 | } |
| 175 | |
Lawrence Tang | e18aaee | 2022-07-07 09:01:30 +0100 | [diff] [blame] | 176 | //Converts the given UINT64 array into a JSON IR array, given the length. |
| 177 | json_object* uint64_array_to_ir_array(UINT64* array, int len) |
| 178 | { |
| 179 | json_object* array_ir = json_object_new_array(); |
| 180 | for (int i=0; i<len; i++) |
| 181 | json_object_array_add(array_ir, json_object_new_uint64(array[i])); |
| 182 | return array_ir; |
| 183 | } |
Lawrence Tang | 794312c | 2022-07-05 14:46:10 +0100 | [diff] [blame] | 184 | |
Lawrence Tang | 1b0b00e | 2022-07-05 10:33:10 +0100 | [diff] [blame] | 185 | //Converts a single UINT16 revision number into JSON IR representation. |
| 186 | json_object* revision_to_ir(UINT16 revision) |
| 187 | { |
| 188 | json_object* revision_info = json_object_new_object(); |
| 189 | json_object_object_add(revision_info, "major", json_object_new_int(revision >> 8)); |
| 190 | json_object_object_add(revision_info, "minor", json_object_new_int(revision & 0xFF)); |
| 191 | return revision_info; |
| 192 | } |
| 193 | |
| 194 | //Returns the appropriate string for the given integer severity. |
| 195 | const char* severity_to_string(UINT8 severity) |
| 196 | { |
| 197 | return severity < 4 ? CPER_SEVERITY_TYPES[severity] : "Unknown"; |
| 198 | } |
| 199 | |
Lawrence Tang | b44314c | 2022-07-13 11:45:22 +0100 | [diff] [blame] | 200 | //Converts a single EFI timestamp to string, at the given output. |
| 201 | //Output must be at least TIMESTAMP_LENGTH bytes long. |
| 202 | void timestamp_to_string(char* out, EFI_ERROR_TIME_STAMP* timestamp) |
| 203 | { |
| 204 | sprintf(out, "%02d%02d-%02d-%02dT%02d:%02d:%02d.000", |
| 205 | timestamp->Century, |
| 206 | timestamp->Year, |
| 207 | timestamp->Month, |
| 208 | timestamp->Day, |
| 209 | timestamp->Hours, |
| 210 | timestamp->Minutes, |
| 211 | timestamp->Seconds); |
| 212 | } |
| 213 | |
| 214 | //Converts a single timestamp string to an EFI timestamp. |
| 215 | void string_to_timestamp(EFI_ERROR_TIME_STAMP* out, const char* timestamp) |
| 216 | { |
Lawrence Tang | 0cb3379 | 2022-07-13 13:51:39 +0100 | [diff] [blame] | 217 | //Ignore invalid timestamps. |
| 218 | if (timestamp == NULL) |
| 219 | return; |
| 220 | |
| 221 | sscanf(timestamp, "%02hhd%02hhd-%02hhd-%02hhdT%02hhd:%02hhd:%02hhd.000", |
Lawrence Tang | b44314c | 2022-07-13 11:45:22 +0100 | [diff] [blame] | 222 | &out->Century, |
| 223 | &out->Year, |
| 224 | &out->Month, |
| 225 | &out->Day, |
| 226 | &out->Hours, |
| 227 | &out->Minutes, |
| 228 | &out->Seconds); |
| 229 | } |
| 230 | |
Lawrence Tang | 1b0b00e | 2022-07-05 10:33:10 +0100 | [diff] [blame] | 231 | //Helper function to convert an EDK EFI GUID into a string for intermediate use. |
| 232 | void guid_to_string(char* out, EFI_GUID* guid) |
| 233 | { |
| 234 | sprintf(out, "%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x", |
| 235 | guid->Data1, |
| 236 | guid->Data2, |
| 237 | guid->Data3, |
| 238 | guid->Data4[0], |
| 239 | guid->Data4[1], |
| 240 | guid->Data4[2], |
| 241 | guid->Data4[3], |
| 242 | guid->Data4[4], |
| 243 | guid->Data4[5], |
| 244 | guid->Data4[6], |
| 245 | guid->Data4[7]); |
| 246 | } |
| 247 | |
Lawrence Tang | b44314c | 2022-07-13 11:45:22 +0100 | [diff] [blame] | 248 | //Helper function to convert a string into an EDK EFI GUID. |
| 249 | void string_to_guid(EFI_GUID* out, const char* guid) |
| 250 | { |
Lawrence Tang | 0cb3379 | 2022-07-13 13:51:39 +0100 | [diff] [blame] | 251 | //Ignore invalid GUIDs. |
| 252 | if (guid == NULL) |
| 253 | return; |
| 254 | |
| 255 | sscanf(guid, "%08x-%04hx-%04hx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx", |
Lawrence Tang | b44314c | 2022-07-13 11:45:22 +0100 | [diff] [blame] | 256 | &out->Data1, |
| 257 | &out->Data2, |
| 258 | &out->Data3, |
| 259 | out->Data4, |
| 260 | out->Data4 + 1, |
| 261 | out->Data4 + 2, |
| 262 | out->Data4 + 3, |
| 263 | out->Data4 + 4, |
| 264 | out->Data4 + 5, |
| 265 | out->Data4 + 6, |
| 266 | out->Data4 + 7); |
| 267 | } |
| 268 | |
Lawrence Tang | 1b0b00e | 2022-07-05 10:33:10 +0100 | [diff] [blame] | 269 | //Returns one if two EFI GUIDs are equal, zero otherwise. |
| 270 | int guid_equal(EFI_GUID* a, EFI_GUID* b) |
| 271 | { |
| 272 | //Check top base 3 components. |
| 273 | if (a->Data1 != b->Data1 |
| 274 | || a->Data2 != b->Data2 |
| 275 | || a->Data3 != b->Data3) |
| 276 | { |
| 277 | return 0; |
| 278 | } |
| 279 | |
| 280 | //Check Data4 array for equality. |
| 281 | for (int i=0; i<8; i++) |
| 282 | { |
| 283 | if (a->Data4[i] != b->Data4[i]) |
| 284 | return 0; |
| 285 | } |
| 286 | |
| 287 | return 1; |
Lawrence Tang | 4dbe3d7 | 2022-07-06 13:51:01 +0100 | [diff] [blame] | 288 | } |
| 289 | |
| 290 | //Converts the given BCD byte to a standard integer. |
| 291 | int bcd_to_int(UINT8 bcd) |
| 292 | { |
| 293 | return ((bcd & 0xF0) >> 4) * 10 + (bcd & 0x0F); |
Lawrence Tang | 3b7f45b | 2022-07-14 14:14:30 +0100 | [diff] [blame] | 294 | } |
| 295 | |
| 296 | //Converts the given integer to a single byte BCD. |
| 297 | UINT8 int_to_bcd(int value) |
| 298 | { |
| 299 | UINT8 result = 0; |
| 300 | int shift = 0; |
| 301 | while (value > 0) { |
| 302 | result |= (value % 10) << (shift++ << 2); |
| 303 | value /= 10; |
| 304 | } |
| 305 | |
| 306 | return result; |
Lawrence Tang | 1b0b00e | 2022-07-05 10:33:10 +0100 | [diff] [blame] | 307 | } |