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
parse_cmet_info(EFI_NVIDIA_REGISTER_DATA * regPtr,UINT8 NumberRegs,size_t size,json_object * section_ir)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
parse_fwerror(EFI_NVIDIA_REGISTER_DATA * regPtr,UINT8 NumberRegs,size_t size,json_object * section_ir)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
parse_registers(EFI_NVIDIA_REGISTER_DATA * regPtr,UINT8 NumberRegs,size_t size,json_object * section_ir)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
get_index(const char * signature)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.
cper_section_nvidia_to_ir(const UINT8 * section,UINT32 size,char ** desc_string)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 add_untrusted_string(section_ir, "signature", nvidia_error->Signature,
170 sizeof(nvidia_error->Signature));
171
172 json_object *severity = json_object_new_object();
173 json_object_object_add(severity, "code",
174 json_object_new_uint64(nvidia_error->Severity));
175 const char *severity_name = severity_to_string(nvidia_error->Severity);
176 json_object_object_add(severity, "name",
177 json_object_new_string(severity_name));
178 int outstr_len = 0;
179 char *signature = nvidia_error->Signature;
180 int sig_len = cper_printable_string_length(
181 nvidia_error->Signature, sizeof(nvidia_error->Signature));
182 if (sig_len <= 0) {
183 signature = "";
184 sig_len = 0;
185 }
186
187 outstr_len = snprintf(*desc_string, SECTION_DESC_STRING_SIZE,
188 "A %s %.*s NVIDIA Error occurred", severity_name,
189 sig_len, signature);
190 if (outstr_len < 0) {
191 cper_print_log(
192 "Error: Could not write to description string\n");
193 } else if (outstr_len > SECTION_DESC_STRING_SIZE) {
194 cper_print_log("Error: Description string truncated: %s\n",
195 *desc_string);
196 }
197 json_object_object_add(section_ir, "severity", severity);
198
199 json_object_object_add(section_ir, "errorType",
200 json_object_new_int(nvidia_error->ErrorType));
201 json_object_object_add(
202 section_ir, "errorInstance",
203 json_object_new_int(nvidia_error->ErrorInstance));
204 json_object_object_add(section_ir, "socket",
205 json_object_new_int(nvidia_error->Socket));
206
207 outstr_len = snprintf(property_desc, EFI_ERROR_DESCRIPTION_STRING_LEN,
208 " on CPU %d", nvidia_error->Socket);
209 if (outstr_len < 0) {
210 cper_print_log("Error: Could not write to property string\n");
211 } else if (outstr_len > EFI_ERROR_DESCRIPTION_STRING_LEN) {
212 cper_print_log("Error: Property string truncated: %s\n",
213 property_desc);
214 }
215
216 int property_desc_len = strlen(property_desc);
217 strncat(*desc_string, property_desc,
218 SECTION_DESC_STRING_SIZE - strlen(*desc_string) - 1);
219 // We still want to get as much info as possible, just warn about truncation
220 if (property_desc_len + strlen(*desc_string) >=
221 SECTION_DESC_STRING_SIZE) {
222 cper_print_log("Error: Description string truncated: %s\n",
223 *desc_string);
224 }
225 free(property_desc);
226
227 json_object_object_add(section_ir, "registerCount",
228 json_object_new_int(nvidia_error->NumberRegs));
229 json_object_object_add(
230 section_ir, "instanceBase",
231 json_object_new_uint64(nvidia_error->InstanceBase));
232 int index = get_index(nvidia_error->Signature);
233 if (index == -1) {
234 cper_print_log("Error: Unknown NVIDIA section signature: %s\n",
235 nvidia_error->Signature);
236 return NULL;
237 }
238 section_handlers[index].callback(&nvidia_error->Register[0],
239 nvidia_error->NumberRegs, size,
240 section_ir);
241
242 return section_ir;
243 }
244
245 //Converts a single NVIDIA CPER-JSON section into CPER binary, outputting to the given stream.
ir_section_nvidia_to_cper(json_object * section,FILE * out)246 void ir_section_nvidia_to_cper(json_object *section, FILE *out)
247 {
248 json_object *regarr = json_object_object_get(section, "registers");
249 int numRegs = json_object_array_length(regarr);
250
251 size_t section_sz = offsetof(EFI_NVIDIA_ERROR_DATA, Register) +
252 numRegs * sizeof(EFI_NVIDIA_REGISTER_DATA);
253 EFI_NVIDIA_ERROR_DATA *section_cper =
254 (EFI_NVIDIA_ERROR_DATA *)calloc(1, section_sz);
255
256 //Signature.
257 strncpy(section_cper->Signature,
258 json_object_get_string(
259 json_object_object_get(section, "signature")),
260 sizeof(section_cper->Signature) - 1);
261 section_cper->Signature[sizeof(section_cper->Signature) - 1] = '\0';
262
263 //Fields.
264 section_cper->ErrorType = json_object_get_int(
265 json_object_object_get(section, "errorType"));
266 section_cper->ErrorInstance = json_object_get_int(
267 json_object_object_get(section, "errorInstance"));
268 json_object *severity = json_object_object_get(section, "severity");
269 section_cper->Severity = (UINT8)json_object_get_uint64(
270 json_object_object_get(severity, "code"));
271 section_cper->Socket =
272 json_object_get_int(json_object_object_get(section, "socket"));
273 section_cper->NumberRegs = json_object_get_int(
274 json_object_object_get(section, "registerCount"));
275 section_cper->InstanceBase = json_object_get_uint64(
276 json_object_object_get(section, "instanceBase"));
277
278 // Registers (Address Value pairs).
279 EFI_NVIDIA_REGISTER_DATA *regPtr = section_cper->Register;
280 for (int i = 0; i < numRegs; i++, regPtr++) {
281 json_object *reg = json_object_array_get_idx(regarr, i);
282 regPtr->Address = json_object_get_uint64(
283 json_object_object_get(reg, "address"));
284 regPtr->Value = json_object_get_uint64(
285 json_object_object_get(reg, "value"));
286 }
287
288 //Write to stream, free resources.
289 fwrite(section_cper, section_sz, 1, out);
290 fflush(out);
291 free(section_cper);
292 }
293