xref: /openbmc/libcper/sections/cper-section-nvidia.c (revision 9f68932652295f8dce232bd8376703c008a0e9df)
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