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