1 /** 2 * Describes functions for converting NVIDIA CPER sections from binary and JSON format 3 * into an intermediate format. 4 **/ 5 6 #include <stdio.h> 7 #include <stddef.h> 8 #include <string.h> 9 #include <json.h> 10 #include <libcper/Cper.h> 11 #include <libcper/cper-utils.h> 12 #include <libcper/sections/cper-section-nvidia.h> 13 #include <libcper/log.h> 14 #include <string.h> 15 16 void parse_cmet_info(EFI_NVIDIA_REGISTER_DATA *regPtr, UINT8 NumberRegs, 17 size_t size, json_object *section_ir) 18 { 19 json_object *regarr = json_object_new_array(); 20 for (int i = 0; i < NumberRegs; i++, regPtr++) { 21 json_object *reg = NULL; 22 if (sizeof(EFI_NVIDIA_ERROR_DATA) + 23 (i + 1) * sizeof(EFI_NVIDIA_REGISTER_DATA) <= 24 size) { 25 reg = json_object_new_object(); 26 add_int_hex_64(reg, "ChannelAddress", regPtr->Address); 27 add_int(reg, "ErrorCount", regPtr->CmetInfo.ErrorCount); 28 add_bool(reg, "ChannelEnabled", 29 regPtr->CmetInfo.ChannelEnabled); 30 add_bool(reg, "ChannelIsSpare", 31 regPtr->CmetInfo.ChannelIsSpare); 32 add_dict(reg, "DisabledReason", 33 regPtr->CmetInfo.DisabledReason, 34 channel_disable_reason_dict, 35 channel_disable_reason_dict_size); 36 } else { 37 reg = json_object_new_null(); 38 } 39 40 json_object_array_add(regarr, reg); 41 } 42 43 json_object_object_add(section_ir, "CMETInfo", regarr); 44 } 45 46 void parse_fwerror(EFI_NVIDIA_REGISTER_DATA *regPtr, UINT8 NumberRegs, 47 size_t size, json_object *section_ir) 48 { 49 (void)NumberRegs; 50 json_object *fwinfo; 51 if (sizeof(EFI_NVIDIA_ERROR_DATA) + sizeof(EFI_NVIDIA_FWERROR) > size) { 52 fwinfo = json_object_new_null(); 53 } else { 54 fwinfo = json_object_new_object(); 55 EFI_NVIDIA_FWERROR *fwerror = (EFI_NVIDIA_FWERROR *)regPtr; 56 add_untrusted_string(fwinfo, "initiating_firmware", 57 fwerror->initiating_firmware, 58 sizeof(fwerror->initiating_firmware)); 59 add_int_hex_64(fwinfo, "task_checkpoint", 60 fwerror->task_checkpoint); 61 add_int_hex_64(fwinfo, "mb1_error_code", 62 fwerror->mb1_error_code); 63 add_untrusted_string(fwinfo, "mb1_version_string", 64 fwerror->mb1_version_string, 65 sizeof(fwerror->mb1_version_string)); 66 add_int_hex_64(fwinfo, "bad_pages_retired_mask", 67 fwerror->bad_pages_retired_mask); 68 add_int_hex_64(fwinfo, "training_or_alias_check_retired_mask", 69 fwerror->training_or_alias_check_retired_mask); 70 } 71 72 json_object_object_add(section_ir, "FWErrorInfo", fwinfo); 73 } 74 75 void parse_registers(EFI_NVIDIA_REGISTER_DATA *regPtr, UINT8 NumberRegs, 76 size_t size, json_object *section_ir) 77 { 78 // Registers (Address Value pairs). 79 json_object *regarr = json_object_new_array(); 80 for (int i = 0; i < NumberRegs; i++, regPtr++) { 81 json_object *reg = NULL; 82 if (sizeof(EFI_NVIDIA_ERROR_DATA) + 83 (i + 1) * sizeof(EFI_NVIDIA_REGISTER_DATA) <= 84 size) { 85 reg = json_object_new_object(); 86 json_object_object_add( 87 reg, "address", 88 json_object_new_uint64(regPtr->Address)); 89 json_object_object_add( 90 reg, "value", 91 json_object_new_uint64(regPtr->Value)); 92 } else { 93 reg = json_object_new_null(); 94 } 95 96 json_object_array_add(regarr, reg); 97 } 98 json_object_object_add(section_ir, "registers", regarr); 99 } 100 101 typedef struct { 102 const char *ip_signature; 103 void (*callback)(EFI_NVIDIA_REGISTER_DATA *, UINT8, size_t, 104 json_object *); 105 } NV_SECTION_CALLBACKS; 106 107 NV_SECTION_CALLBACKS section_handlers[] = { 108 { "CMET-INFO\0", &parse_cmet_info }, 109 { "FWERROR\0", &parse_fwerror }, 110 { "", &parse_registers }, 111 }; 112 113 int get_index(const char *signature) 114 { 115 if (signature == NULL) { 116 cper_print_log("Error: NULL signature\n"); 117 return -1; 118 } 119 int index = -1; 120 // we're comparing i with section_handlers type 121 size_t i = 0; 122 for (i = 0; i < sizeof(section_handlers) / sizeof(section_handlers[0]); 123 i++) { 124 const char *ip_signature = section_handlers[i].ip_signature; 125 if (strncmp(signature, ip_signature, strlen(ip_signature)) == 126 0) { 127 // i is small so we won't overflow 128 return (int)i; 129 } 130 } 131 // if no match was found, and fuzzing is enabled, pick one to get coverage 132 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION 133 index = (unsigned char)signature[0] % 134 (sizeof(section_handlers) / sizeof(section_handlers[0])); 135 #endif 136 return index; 137 } 138 139 //Converts a single NVIDIA CPER section into JSON IR. 140 json_object *cper_section_nvidia_to_ir(const UINT8 *section, UINT32 size, 141 char **desc_string) 142 { 143 *desc_string = NULL; 144 if (size < sizeof(EFI_NVIDIA_ERROR_DATA)) { 145 cper_print_log("Error: NVIDIA section too small\n"); 146 return NULL; 147 } 148 149 *desc_string = calloc(1, SECTION_DESC_STRING_SIZE); 150 if (*desc_string == NULL) { 151 cper_print_log( 152 "Error: Failed to allocate NVIDIA desc string\n"); 153 return NULL; 154 } 155 156 char *property_desc = calloc(1, EFI_ERROR_DESCRIPTION_STRING_LEN); 157 if (property_desc == NULL) { 158 free(*desc_string); 159 *desc_string = NULL; 160 cper_print_log( 161 "Error: Failed to allocate NVIDIA property desc\n"); 162 return NULL; 163 } 164 165 EFI_NVIDIA_ERROR_DATA *nvidia_error = (EFI_NVIDIA_ERROR_DATA *)section; 166 167 json_object *section_ir = json_object_new_object(); 168 169 const char *signature = nvidia_error->Signature; 170 add_untrusted_string(section_ir, "signature", signature, 171 strlen(signature)); 172 173 json_object *severity = json_object_new_object(); 174 json_object_object_add(severity, "code", 175 json_object_new_uint64(nvidia_error->Severity)); 176 const char *severity_name = severity_to_string(nvidia_error->Severity); 177 json_object_object_add(severity, "name", 178 json_object_new_string(severity_name)); 179 int outstr_len = 0; 180 outstr_len = snprintf(*desc_string, SECTION_DESC_STRING_SIZE, 181 "A %s %s NVIDIA Error occurred", severity_name, 182 signature); 183 if (outstr_len < 0) { 184 cper_print_log( 185 "Error: Could not write to description string\n"); 186 } else if (outstr_len > SECTION_DESC_STRING_SIZE) { 187 cper_print_log("Error: Description string truncated: %s\n", 188 *desc_string); 189 } 190 json_object_object_add(section_ir, "severity", severity); 191 192 json_object_object_add(section_ir, "errorType", 193 json_object_new_int(nvidia_error->ErrorType)); 194 json_object_object_add( 195 section_ir, "errorInstance", 196 json_object_new_int(nvidia_error->ErrorInstance)); 197 json_object_object_add(section_ir, "socket", 198 json_object_new_int(nvidia_error->Socket)); 199 200 outstr_len = snprintf(property_desc, EFI_ERROR_DESCRIPTION_STRING_LEN, 201 " on CPU %d", nvidia_error->Socket); 202 if (outstr_len < 0) { 203 cper_print_log("Error: Could not write to property string\n"); 204 } else if (outstr_len > EFI_ERROR_DESCRIPTION_STRING_LEN) { 205 cper_print_log("Error: Property string truncated: %s\n", 206 property_desc); 207 } 208 209 int property_desc_len = strlen(property_desc); 210 strncat(*desc_string, property_desc, 211 SECTION_DESC_STRING_SIZE - strlen(*desc_string) - 1); 212 // We still want to get as much info as possible, just warn about truncation 213 if (property_desc_len + strlen(*desc_string) >= 214 SECTION_DESC_STRING_SIZE) { 215 cper_print_log("Error: Description string truncated: %s\n", 216 *desc_string); 217 } 218 free(property_desc); 219 220 json_object_object_add(section_ir, "registerCount", 221 json_object_new_int(nvidia_error->NumberRegs)); 222 json_object_object_add( 223 section_ir, "instanceBase", 224 json_object_new_uint64(nvidia_error->InstanceBase)); 225 int index = get_index(nvidia_error->Signature); 226 if (index == -1) { 227 cper_print_log("Error: Unknown NVIDIA section signature: %s\n", 228 nvidia_error->Signature); 229 return NULL; 230 } 231 section_handlers[index].callback(&nvidia_error->Register[0], 232 nvidia_error->NumberRegs, size, 233 section_ir); 234 235 return section_ir; 236 } 237 238 //Converts a single NVIDIA CPER-JSON section into CPER binary, outputting to the given stream. 239 void ir_section_nvidia_to_cper(json_object *section, FILE *out) 240 { 241 json_object *regarr = json_object_object_get(section, "registers"); 242 int numRegs = json_object_array_length(regarr); 243 244 size_t section_sz = offsetof(EFI_NVIDIA_ERROR_DATA, Register) + 245 numRegs * sizeof(EFI_NVIDIA_REGISTER_DATA); 246 EFI_NVIDIA_ERROR_DATA *section_cper = 247 (EFI_NVIDIA_ERROR_DATA *)calloc(1, section_sz); 248 249 //Signature. 250 strncpy(section_cper->Signature, 251 json_object_get_string( 252 json_object_object_get(section, "signature")), 253 sizeof(section_cper->Signature) - 1); 254 section_cper->Signature[sizeof(section_cper->Signature) - 1] = '\0'; 255 256 //Fields. 257 section_cper->ErrorType = json_object_get_int( 258 json_object_object_get(section, "errorType")); 259 section_cper->ErrorInstance = json_object_get_int( 260 json_object_object_get(section, "errorInstance")); 261 json_object *severity = json_object_object_get(section, "severity"); 262 section_cper->Severity = (UINT8)json_object_get_uint64( 263 json_object_object_get(severity, "code")); 264 section_cper->Socket = 265 json_object_get_int(json_object_object_get(section, "socket")); 266 section_cper->NumberRegs = json_object_get_int( 267 json_object_object_get(section, "registerCount")); 268 section_cper->InstanceBase = json_object_get_uint64( 269 json_object_object_get(section, "instanceBase")); 270 271 // Registers (Address Value pairs). 272 EFI_NVIDIA_REGISTER_DATA *regPtr = section_cper->Register; 273 for (int i = 0; i < numRegs; i++, regPtr++) { 274 json_object *reg = json_object_array_get_idx(regarr, i); 275 regPtr->Address = json_object_get_uint64( 276 json_object_object_get(reg, "address")); 277 regPtr->Value = json_object_get_uint64( 278 json_object_object_get(reg, "value")); 279 } 280 281 //Write to stream, free resources. 282 fwrite(section_cper, section_sz, 1, out); 283 fflush(out); 284 free(section_cper); 285 } 286