xref: /openbmc/libcper/cper-utils.c (revision 51c1813200b42fa35d49eb0c214339ac479f090d)
1 /**
2  * Describes utility functions for parsing CPER into JSON IR.
3  *
4  * Author: Lawrence.Tang@arm.com
5  **/
6 
7 #include <ctype.h>
8 #include <stdio.h>
9 #include <json.h>
10 #include <string.h>
11 #include <libcper/Cper.h>
12 #include <libcper/cper-utils.h>
13 #include <libcper/log.h>
14 
15 //The available severity types for CPER.
16 const char *CPER_SEVERITY_TYPES[4] = { "Recoverable", "Fatal", "Corrected",
17 				       "Informational" };
18 
19 //Converts the given generic CPER error status to JSON IR.
20 json_object *
cper_generic_error_status_to_ir(EFI_GENERIC_ERROR_STATUS * error_status)21 cper_generic_error_status_to_ir(EFI_GENERIC_ERROR_STATUS *error_status)
22 {
23 	json_object *error_status_ir = json_object_new_object();
24 
25 	//Error type.
26 	json_object_object_add(error_status_ir, "errorType",
27 			       integer_to_readable_pair_with_desc(
28 				       error_status->Type, 18,
29 				       CPER_GENERIC_ERROR_TYPES_KEYS,
30 				       CPER_GENERIC_ERROR_TYPES_VALUES,
31 				       CPER_GENERIC_ERROR_TYPES_DESCRIPTIONS,
32 				       "Unknown (Reserved)"));
33 
34 	//Boolean bit fields.
35 	json_object_object_add(
36 		error_status_ir, "addressSignal",
37 		json_object_new_boolean(error_status->AddressSignal));
38 	json_object_object_add(
39 		error_status_ir, "controlSignal",
40 		json_object_new_boolean(error_status->ControlSignal));
41 	json_object_object_add(
42 		error_status_ir, "dataSignal",
43 		json_object_new_boolean(error_status->DataSignal));
44 	json_object_object_add(
45 		error_status_ir, "detectedByResponder",
46 		json_object_new_boolean(error_status->DetectedByResponder));
47 	json_object_object_add(
48 		error_status_ir, "detectedByRequester",
49 		json_object_new_boolean(error_status->DetectedByRequester));
50 	json_object_object_add(
51 		error_status_ir, "firstError",
52 		json_object_new_boolean(error_status->FirstError));
53 	json_object_object_add(
54 		error_status_ir, "overflowDroppedLogs",
55 		json_object_new_boolean(error_status->OverflowNotLogged));
56 
57 	return error_status_ir;
58 }
59 
60 //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)61 void ir_generic_error_status_to_cper(
62 	json_object *error_status, EFI_GENERIC_ERROR_STATUS *error_status_cper)
63 {
64 	error_status_cper->Type = readable_pair_to_integer(
65 		json_object_object_get(error_status, "errorType"));
66 	error_status_cper->AddressSignal = json_object_get_boolean(
67 		json_object_object_get(error_status, "addressSignal"));
68 	error_status_cper->ControlSignal = json_object_get_boolean(
69 		json_object_object_get(error_status, "controlSignal"));
70 	error_status_cper->DataSignal = json_object_get_boolean(
71 		json_object_object_get(error_status, "dataSignal"));
72 	error_status_cper->DetectedByResponder = json_object_get_boolean(
73 		json_object_object_get(error_status, "detectedByResponder"));
74 	error_status_cper->DetectedByRequester = json_object_get_boolean(
75 		json_object_object_get(error_status, "detectedByRequester"));
76 	error_status_cper->FirstError = json_object_get_boolean(
77 		json_object_object_get(error_status, "firstError"));
78 	error_status_cper->OverflowNotLogged = json_object_get_boolean(
79 		json_object_object_get(error_status, "overflowDroppedLogs"));
80 }
81 
82 //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[])83 json_object *uniform_struct64_to_ir(UINT64 *start, int len, const char *names[])
84 {
85 	json_object *result = json_object_new_object();
86 
87 	UINT64 *cur = start;
88 	for (int i = 0; i < len; i++) {
89 		json_object_object_add(result, names[i],
90 				       json_object_new_uint64(*cur));
91 		cur++;
92 	}
93 
94 	return result;
95 }
96 
97 //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[])98 json_object *uniform_struct_to_ir(UINT32 *start, int len, const char *names[])
99 {
100 	json_object *result = json_object_new_object();
101 
102 	UINT32 *cur = start;
103 	for (int i = 0; i < len; i++) {
104 		UINT32 value;
105 		memcpy(&value, cur, sizeof(UINT32));
106 		json_object_object_add(result, names[i],
107 				       json_object_new_uint64(value));
108 		cur++;
109 	}
110 
111 	return result;
112 }
113 
114 //Converts a single object containing UINT32s into a uniform struct.
ir_to_uniform_struct64(json_object * ir,UINT64 * start,int len,const char * names[])115 void ir_to_uniform_struct64(json_object *ir, UINT64 *start, int len,
116 			    const char *names[])
117 {
118 	UINT64 *cur = start;
119 	for (int i = 0; i < len; i++) {
120 		*cur = json_object_get_uint64(
121 			json_object_object_get(ir, names[i]));
122 		cur++;
123 	}
124 }
125 
126 //Converts a single object containing UINT32s into a uniform struct.
ir_to_uniform_struct(json_object * ir,UINT32 * start,int len,const char * names[])127 void ir_to_uniform_struct(json_object *ir, UINT32 *start, int len,
128 			  const char *names[])
129 {
130 	UINT32 *cur = start;
131 	for (int i = 0; i < len; i++) {
132 		*cur = (UINT32)json_object_get_uint64(
133 			json_object_object_get(ir, names[i]));
134 		cur++;
135 	}
136 }
137 
138 //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)139 json_object *integer_to_readable_pair(UINT64 value, int len, const int keys[],
140 				      const char *values[],
141 				      const char *default_value)
142 {
143 	json_object *result = json_object_new_object();
144 	json_object_object_add(result, "value", json_object_new_uint64(value));
145 
146 	//Search for human readable name, add.
147 	const char *name = default_value;
148 	for (int i = 0; i < len; i++) {
149 		if ((UINT64)keys[i] == value) {
150 			name = values[i];
151 		}
152 	}
153 
154 	json_object_object_add(result, "name", json_object_new_string(name));
155 	return result;
156 }
157 
158 //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)159 json_object *integer_to_readable_pair_with_desc(int value, int len,
160 						const int keys[],
161 						const char *values[],
162 						const char *descriptions[],
163 						const char *default_value)
164 {
165 	json_object *result = json_object_new_object();
166 	json_object_object_add(result, "value", json_object_new_int(value));
167 
168 	//Search for human readable name, add.
169 	const char *name = default_value;
170 	for (int i = 0; i < len; i++) {
171 		if (keys[i] == value) {
172 			name = values[i];
173 			json_object_object_add(
174 				result, "description",
175 				json_object_new_string(descriptions[i]));
176 		}
177 	}
178 
179 	json_object_object_add(result, "name", json_object_new_string(name));
180 	return result;
181 }
182 
183 //Returns a single UINT64 value from the given readable pair object.
184 //Assumes the integer value is held in the "value" field.
readable_pair_to_integer(json_object * pair)185 UINT64 readable_pair_to_integer(json_object *pair)
186 {
187 	return json_object_get_uint64(json_object_object_get(pair, "value"));
188 }
189 
190 //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[])191 json_object *bitfield_to_ir(UINT64 bitfield, int num_fields,
192 			    const char *names[])
193 {
194 	json_object *result = json_object_new_object();
195 	for (int i = 0; i < num_fields; i++) {
196 		json_object_object_add(result, names[i],
197 				       json_object_new_boolean((bitfield >> i) &
198 							       0x1));
199 	}
200 
201 	return result;
202 }
203 
204 //Filters properties based on Validation Bits.
205 // Refer to CPER spec for vbit_idx to be passed here.
add_to_valid_bitfield(ValidationTypes * val,int vbit_idx)206 void add_to_valid_bitfield(ValidationTypes *val, int vbit_idx)
207 {
208 	switch (val->size) {
209 	case UINT_8T:
210 		val->value.ui8 |= (0x01 << vbit_idx);
211 		break;
212 	case UINT_16T:
213 		val->value.ui16 |= (0x01 << vbit_idx);
214 		break;
215 	case UINT_32T:
216 		val->value.ui32 |= (0x01 << vbit_idx);
217 		break;
218 	case UINT_64T:
219 		val->value.ui64 |= (0x01 << vbit_idx);
220 		break;
221 	default:
222 		cper_print_log(
223 			"IR to CPER: Unknown validation bits size passed, Enum IntType=%d",
224 			val->size);
225 	}
226 }
227 
228 //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[])229 UINT64 ir_to_bitfield(json_object *ir, int num_fields, const char *names[])
230 {
231 	UINT64 result = 0x0;
232 	for (int i = 0; i < num_fields; i++) {
233 		if (json_object_get_boolean(
234 			    json_object_object_get(ir, names[i]))) {
235 			result |= (0x1 << i);
236 		}
237 	}
238 
239 	return result;
240 }
241 
242 // Filters properties based on Validation Bits.
243 // Refer to CPER spec for vbit_idx to be passed here.
244 // Overload function for 16, 32, 64b
isvalid_prop_to_ir(ValidationTypes * val,int vbit_idx)245 bool isvalid_prop_to_ir(ValidationTypes *val, int vbit_idx)
246 {
247 // If the option is enabled, output invalid properties
248 // as well as valid ones.
249 #ifdef OUTPUT_ALL_PROPERTIES
250 	return true;
251 #endif //OUTPUT_ALL_PROPERTIES
252 	UINT64 vbit_mask = 0x01 << vbit_idx;
253 	switch (val->size) {
254 	case UINT_16T:
255 		return (vbit_mask & val->value.ui16);
256 
257 	case UINT_32T:
258 		return (vbit_mask & val->value.ui32);
259 
260 	case UINT_64T:
261 		return (vbit_mask & val->value.ui64);
262 
263 	default:
264 		cper_print_log(
265 			"CPER to IR:Unknown validation bits size passed. Enum IntType: %d",
266 			val->size);
267 	}
268 	return 0;
269 }
270 
print_val(ValidationTypes * val)271 void print_val(ValidationTypes *val)
272 {
273 	switch (val->size) {
274 	case UINT_8T:
275 		cper_print_log("Validation bits: %x\n", val->value.ui8);
276 		break;
277 	case UINT_16T:
278 		cper_print_log("Validation bits: %x\n", val->value.ui16);
279 		break;
280 
281 	case UINT_32T:
282 		cper_print_log("Validation bits: %x\n", val->value.ui32);
283 		break;
284 
285 	case UINT_64T:
286 		cper_print_log("Validation bits: %llx\n", val->value.ui64);
287 		break;
288 
289 	default:
290 		cper_print_log(
291 			"CPER to IR:Unknown validation bits size passed. Enum IntType: %d",
292 			val->size);
293 	}
294 }
295 
296 //Converts the given UINT64 array into a JSON IR array, given the length.
uint64_array_to_ir_array(UINT64 * array,int len)297 json_object *uint64_array_to_ir_array(UINT64 *array, int len)
298 {
299 	json_object *array_ir = json_object_new_array();
300 	for (int i = 0; i < len; i++) {
301 		json_object_array_add(array_ir,
302 				      json_object_new_uint64(array[i]));
303 	}
304 	return array_ir;
305 }
306 
307 //Converts a single UINT16 revision number into JSON IR representation.
revision_to_ir(UINT16 revision)308 json_object *revision_to_ir(UINT16 revision)
309 {
310 	json_object *revision_info = json_object_new_object();
311 	json_object_object_add(revision_info, "major",
312 			       json_object_new_int(revision >> 8));
313 	json_object_object_add(revision_info, "minor",
314 			       json_object_new_int(revision & 0xFF));
315 	return revision_info;
316 }
317 
318 //Returns the appropriate string for the given integer severity.
severity_to_string(UINT32 severity)319 const char *severity_to_string(UINT32 severity)
320 {
321 	return severity < 4 ? CPER_SEVERITY_TYPES[severity] : "Unknown";
322 }
323 
324 //Converts a single EFI timestamp to string, at the given output.
325 //Output must be at least TIMESTAMP_LENGTH bytes long.
timestamp_to_string(char * out,int out_len,EFI_ERROR_TIME_STAMP * timestamp)326 int timestamp_to_string(char *out, int out_len, EFI_ERROR_TIME_STAMP *timestamp)
327 {
328 	//Cannot go to three digits.
329 	int century = bcd_to_int(timestamp->Century) % 100;
330 	if (century >= 100) {
331 		return -1;
332 	}
333 	int year = bcd_to_int(timestamp->Year) % 100;
334 	if (year >= 100) {
335 		return -1;
336 	}
337 	int month = bcd_to_int(timestamp->Month);
338 	if (month > 12) {
339 		return -1;
340 	}
341 	int day = bcd_to_int(timestamp->Day);
342 	if (day > 31) {
343 		return -1;
344 	}
345 	int hours = bcd_to_int(timestamp->Hours);
346 	if (hours > 24) {
347 		return -1;
348 	}
349 	int minutes = bcd_to_int(timestamp->Minutes);
350 	if (minutes > 60) {
351 		return -1;
352 	}
353 	int seconds = bcd_to_int(timestamp->Seconds);
354 	if (seconds >= 60) {
355 		return -1;
356 	}
357 	int written = snprintf(
358 		out, out_len,
359 		"%02hhu%02hhu-%02hhu-%02hhuT%02hhu:%02hhu:%02hhu+00:00",
360 		century, year, month, day, hours, minutes, seconds);
361 
362 	if (written < 0 || written >= out_len) {
363 		cper_print_log("Timestamp buffer of insufficient size\n");
364 		return -1;
365 	}
366 	return 0;
367 }
368 
369 //Converts a single timestamp string to an EFI timestamp.
string_to_timestamp(EFI_ERROR_TIME_STAMP * out,const char * timestamp)370 void string_to_timestamp(EFI_ERROR_TIME_STAMP *out, const char *timestamp)
371 {
372 	//Ignore invalid timestamps.
373 	if (timestamp == NULL) {
374 		return;
375 	}
376 
377 	sscanf(timestamp, "%2hhu%2hhu-%hhu-%hhuT%hhu:%hhu:%hhu+00:00",
378 	       &out->Century, &out->Year, &out->Month, &out->Day, &out->Hours,
379 	       &out->Minutes, &out->Seconds);
380 
381 	//Convert back to BCD.
382 	out->Century = int_to_bcd(out->Century);
383 	out->Year = int_to_bcd(out->Year);
384 	out->Month = int_to_bcd(out->Month);
385 	out->Day = int_to_bcd(out->Day);
386 	out->Hours = int_to_bcd(out->Hours);
387 	out->Minutes = int_to_bcd(out->Minutes);
388 	out->Seconds = int_to_bcd(out->Seconds);
389 }
390 
391 //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)392 int guid_to_string(char *out, size_t out_len, EFI_GUID *guid)
393 {
394 	size_t len = snprintf(
395 		out, out_len,
396 		"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", guid->Data1,
397 		guid->Data2, guid->Data3, guid->Data4[0], guid->Data4[1],
398 		guid->Data4[2], guid->Data4[3], guid->Data4[4], guid->Data4[5],
399 		guid->Data4[6], guid->Data4[7]);
400 	if (len != out_len) {
401 		return -1;
402 	}
403 	return len;
404 }
405 
406 //Helper function to convert a string into an EDK EFI GUID.
string_to_guid(EFI_GUID * out,const char * guid)407 void string_to_guid(EFI_GUID *out, const char *guid)
408 {
409 	//Ignore invalid GUIDs.
410 	if (guid == NULL) {
411 		return;
412 	}
413 
414 	sscanf(guid,
415 	       "%08x-%04hx-%04hx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
416 	       &out->Data1, &out->Data2, &out->Data3, out->Data4,
417 	       out->Data4 + 1, out->Data4 + 2, out->Data4 + 3, out->Data4 + 4,
418 	       out->Data4 + 5, out->Data4 + 6, out->Data4 + 7);
419 }
420 
421 //Returns one if two EFI GUIDs are equal, zero otherwise.
guid_equal(const EFI_GUID * a,const EFI_GUID * b)422 int guid_equal(const EFI_GUID *a, const EFI_GUID *b)
423 {
424 	//Check top base 3 components.
425 	if (a->Data1 != b->Data1 || a->Data2 != b->Data2 ||
426 	    a->Data3 != b->Data3) {
427 		return 0;
428 	}
429 
430 	//Check Data4 array for equality.
431 	for (int i = 0; i < 8; i++) {
432 		if (a->Data4[i] != b->Data4[i]) {
433 			return 0;
434 		}
435 	}
436 
437 	return 1;
438 }
439 
select_guid_from_list(EFI_GUID * guid,EFI_GUID * guid_list[],int len)440 int select_guid_from_list(EFI_GUID *guid, EFI_GUID *guid_list[], int len)
441 {
442 	int i = 0;
443 	for (; i < len; i++) {
444 		if (guid_equal(guid, guid_list[i])) {
445 			break;
446 		}
447 	}
448 	// It's unlikely fuzzing can reliably come up with a correct guid, given how
449 	// much entropy there is.  If we're in fuzzing mode, and if we haven't found
450 	// a match, try to force a match so we get some coverage.  Note, we still
451 	// want coverage of the section failed to convert code, so treat index ==
452 	// size as section failed to convert.
453 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
454 	if (i == len) {
455 		i = guid->Data1 % (len + 1);
456 	}
457 #endif
458 
459 	return i;
460 }
461 
cper_printable_string_length(const char * str,int number_chars)462 int cper_printable_string_length(const char *str, int number_chars)
463 {
464 	if (str == NULL) {
465 		return -1; // Return 0 for a null pointer input
466 	}
467 
468 	int fru_text_len = 0;
469 	for (; fru_text_len < number_chars; fru_text_len++) {
470 		char c = str[fru_text_len];
471 		if (c == '\0') {
472 			break;
473 		}
474 		if (!isprint(c)) {
475 			fru_text_len = -1;
476 			break;
477 		}
478 	}
479 
480 	if (fru_text_len == 0 || fru_text_len == number_chars) {
481 		// string was not null terminated
482 		return -1;
483 	}
484 
485 	return fru_text_len;
486 }
487 
hex_string_to_bytes(const char * hex_string,size_t hex_string_len,UINT8 * bytes,size_t bytes_len)488 size_t hex_string_to_bytes(const char *hex_string, size_t hex_string_len,
489 			   UINT8 *bytes, size_t bytes_len)
490 {
491 	if (hex_string == NULL || bytes == NULL) {
492 		return -1;
493 	}
494 
495 	size_t index = 0;
496 	UINT8 value = 0;
497 	while (index < hex_string_len) {
498 		char c = hex_string[index];
499 		if (c >= '0' && c <= '9') {
500 			value += (c - '0');
501 		} else if (c >= 'A' && c <= 'F') {
502 			value += (10 + (c - 'A'));
503 		} else if (c >= 'a' && c <= 'f') {
504 			value += (10 + (c - 'a'));
505 		} else {
506 			return -1;
507 		}
508 		if (index % 2 == 1) {
509 			bytes[index / 2] = value;
510 			value = 0;
511 		} else {
512 			value <<= 4;
513 		}
514 
515 		index++;
516 	}
517 
518 	// Failed to write all bytes
519 	if (index != bytes_len * 2) {
520 		return -1;
521 	}
522 
523 	return bytes_len;
524 }
525 
add_untrusted_string(json_object * ir,const char * field_name,const char * str,int len)526 void add_untrusted_string(json_object *ir, const char *field_name,
527 			  const char *str, int len)
528 {
529 	int fru_text_len = cper_printable_string_length(str, len);
530 	if (fru_text_len >= 0) {
531 		json_object_object_add(
532 			ir, field_name,
533 			json_object_new_string_len(str, fru_text_len));
534 	}
535 }
536 
add_guid(json_object * ir,const char * field_name,EFI_GUID * guid)537 void add_guid(json_object *ir, const char *field_name, EFI_GUID *guid)
538 {
539 	char platform_string[GUID_STRING_LENGTH + 1];
540 	if (!guid_to_string(platform_string, sizeof(platform_string), guid)) {
541 		return;
542 	}
543 	json_object_object_add(
544 		ir, field_name,
545 		json_object_new_string_len(platform_string,
546 					   sizeof(platform_string) - 1));
547 }
548 
add_int(json_object * register_ir,const char * field_name,int value)549 void add_int(json_object *register_ir, const char *field_name, int value)
550 {
551 	json_object_object_add(register_ir, field_name,
552 			       json_object_new_uint64(value));
553 }
554 
add_int_hex_common(json_object * register_ir,const char * field_name,UINT64 value,int len)555 static void add_int_hex_common(json_object *register_ir, const char *field_name,
556 			       UINT64 value, int len)
557 {
558 	char hexstring_buf[EFI_UINT64_HEX_STRING_LEN];
559 	snprintf(hexstring_buf, EFI_UINT64_HEX_STRING_LEN, "0x%0*llX", len,
560 		 value);
561 	json_object_object_add(register_ir, field_name,
562 			       json_object_new_string(hexstring_buf));
563 }
564 
add_int_hex_8(json_object * register_ir,const char * field_name,UINT8 value)565 void add_int_hex_8(json_object *register_ir, const char *field_name,
566 		   UINT8 value)
567 {
568 	add_int_hex_common(register_ir, field_name, value, 2);
569 }
570 
add_int_hex_16(json_object * register_ir,const char * field_name,UINT16 value)571 void add_int_hex_16(json_object *register_ir, const char *field_name,
572 		    UINT16 value)
573 {
574 	add_int_hex_common(register_ir, field_name, value, 4);
575 }
576 
add_int_hex_24(json_object * register_ir,const char * field_name,UINT64 value)577 void add_int_hex_24(json_object *register_ir, const char *field_name,
578 		    UINT64 value)
579 {
580 	add_int_hex_common(register_ir, field_name, value, 6);
581 }
582 
add_int_hex_32(json_object * register_ir,const char * field_name,UINT64 value)583 void add_int_hex_32(json_object *register_ir, const char *field_name,
584 		    UINT64 value)
585 {
586 	add_int_hex_common(register_ir, field_name, value, 8);
587 }
588 
589 // TODO, deduplicate with get_value_hex_64/32
get_value_hex_8(json_object * obj,const char * field_name,UINT8 * value_out)590 void get_value_hex_8(json_object *obj, const char *field_name, UINT8 *value_out)
591 {
592 	json_object *value = json_object_object_get(obj, field_name);
593 	if (!value) {
594 		return;
595 	}
596 	const char *hex_string = json_object_get_string(value);
597 	if (!hex_string) {
598 		return;
599 	}
600 	UINT8 byte;
601 	size_t hex_string_len = strlen(hex_string);
602 	if (hex_string_len != 4) {
603 		return;
604 	}
605 	if (hex_string[0] != '0' || hex_string[1] != 'x') {
606 		return;
607 	}
608 
609 	if (hex_string_to_bytes(hex_string + 2, hex_string_len - 2, &byte, 1) !=
610 	    1) {
611 		return;
612 	}
613 	*value_out = byte;
614 }
615 
616 // TODO, deduplicate with get_value_hex_64
get_value_hex_32(json_object * obj,const char * field_name,UINT32 * value_out)617 void get_value_hex_32(json_object *obj, const char *field_name,
618 		      UINT32 *value_out)
619 {
620 	json_object *value = json_object_object_get(obj, field_name);
621 	if (!value) {
622 		return;
623 	}
624 	const char *hex_string = json_object_get_string(value);
625 	if (!hex_string) {
626 		return;
627 	}
628 	UINT8 bytes[4];
629 	size_t hex_string_len = strlen(hex_string);
630 	if (hex_string_len != 10) {
631 		return;
632 	}
633 	if (hex_string[0] != '0' || hex_string[1] != 'x') {
634 		return;
635 	}
636 
637 	if (hex_string_to_bytes(hex_string + 2, hex_string_len - 2, bytes,
638 				sizeof(bytes)) != 4) {
639 		return;
640 	}
641 	*value_out = (UINT32)bytes[0] << 24 | (UINT32)bytes[1] << 16 |
642 		     (UINT32)bytes[2] << 8 | (UINT32)bytes[3];
643 }
644 
get_value_hex_64(json_object * obj,const char * field_name,UINT64 * value_out)645 void get_value_hex_64(json_object *obj, const char *field_name,
646 		      UINT64 *value_out)
647 {
648 	json_object *value = json_object_object_get(obj, field_name);
649 	if (!value) {
650 		return;
651 	}
652 	const char *hex_string = json_object_get_string(value);
653 	if (!hex_string) {
654 		return;
655 	}
656 	UINT8 bytes[8];
657 	size_t hex_string_len = strlen(hex_string);
658 	if (hex_string_len != 18) {
659 		return;
660 	}
661 	if (hex_string[0] != '0' || hex_string[1] != 'x') {
662 		return;
663 	}
664 
665 	if (hex_string_to_bytes(hex_string + 2, hex_string_len - 2, bytes,
666 				sizeof(bytes)) != 8) {
667 		return;
668 	}
669 	*value_out = (UINT64)bytes[0] << 56 | (UINT64)bytes[1] << 48 |
670 		     (UINT64)bytes[2] << 40 | (UINT64)bytes[3] << 32 |
671 		     (UINT64)bytes[4] << 24 | (UINT64)bytes[5] << 16 |
672 		     (UINT64)bytes[6] << 8 | (UINT64)bytes[7];
673 }
674 
add_int_hex_64(json_object * register_ir,const char * field_name,UINT64 value)675 void add_int_hex_64(json_object *register_ir, const char *field_name,
676 		    UINT64 value)
677 {
678 	add_int_hex_common(register_ir, field_name, value, 16);
679 }
680 
add_bytes_hex(json_object * obj,const char * field_name,const UINT8 * bytes,size_t byte_len)681 void add_bytes_hex(json_object *obj, const char *field_name, const UINT8 *bytes,
682 		   size_t byte_len)
683 {
684 	if (obj == NULL || bytes == NULL || byte_len == 0) {
685 		return;
686 	}
687 
688 	size_t hex_len = byte_len * 2;
689 	char *hex_buf = (char *)malloc(hex_len + 1);
690 	if (hex_buf == NULL) {
691 		return;
692 	}
693 
694 	for (size_t i = 0; i < byte_len; i++) {
695 		snprintf(&hex_buf[i * 2], 3, "%02x", bytes[i]);
696 	}
697 	hex_buf[hex_len] = '\0';
698 
699 	json_object_object_add(obj, field_name,
700 			       json_object_new_string_len(hex_buf,
701 							  (int)hex_len));
702 	free(hex_buf);
703 }
704 
705 // Convert hex character to nibble value, returns -1 on invalid input
hex_char_to_nibble(char c)706 static int hex_char_to_nibble(char c)
707 {
708 	if (c >= '0' && c <= '9') {
709 		return c - '0';
710 	}
711 	if (c >= 'a' && c <= 'f') {
712 		return c - 'a' + 10;
713 	}
714 	if (c >= 'A' && c <= 'F') {
715 		return c - 'A' + 10;
716 	}
717 	return -1;
718 }
719 
720 // Returns malloc'd buffer (caller must free), or NULL on error
get_bytes_hex(json_object * obj,const char * field_name,size_t * out_len)721 UINT8 *get_bytes_hex(json_object *obj, const char *field_name, size_t *out_len)
722 {
723 	if (obj == NULL || out_len == NULL) {
724 		return NULL;
725 	}
726 
727 	json_object *field = json_object_object_get(obj, field_name);
728 	if (field == NULL || !json_object_is_type(field, json_type_string)) {
729 		return NULL;
730 	}
731 
732 	const char *hex_str = json_object_get_string(field);
733 	if (hex_str == NULL) {
734 		return NULL;
735 	}
736 	size_t hex_len = (size_t)json_object_get_string_len(field);
737 
738 	// Must have even number of hex characters
739 	if (hex_len % 2 != 0) {
740 		return NULL;
741 	}
742 
743 	size_t byte_len = hex_len / 2;
744 	UINT8 *bytes = (UINT8 *)malloc(byte_len);
745 	if (bytes == NULL) {
746 		return NULL;
747 	}
748 
749 	for (size_t i = 0; i < byte_len; i++) {
750 		int high = hex_char_to_nibble(hex_str[i * 2]);
751 		int low = hex_char_to_nibble(hex_str[i * 2 + 1]);
752 		if (high < 0 || low < 0) {
753 			free(bytes);
754 			return NULL;
755 		}
756 		bytes[i] = (UINT8)((high << 4) | low);
757 	}
758 
759 	*out_len = byte_len;
760 	return bytes;
761 }
762 
add_bool(json_object * register_ir,const char * field_name,UINT64 value)763 void add_bool(json_object *register_ir, const char *field_name, UINT64 value)
764 {
765 	json_object_object_add(register_ir, field_name,
766 			       json_object_new_boolean(value));
767 }
768 
add_bool_enum(json_object * register_ir,const char * field_name,const char * value_dict[2],UINT64 value_int)769 void add_bool_enum(json_object *register_ir, const char *field_name,
770 		   const char *value_dict[2], UINT64 value_int)
771 {
772 	const char *value = value_dict[0];
773 	if (value_int > 0) {
774 		value = value_dict[1];
775 	}
776 	json_object_object_add(register_ir, field_name,
777 			       json_object_new_string(value));
778 }
779 
add_dict(json_object * register_ir,const char * field_name,UINT64 value,const char * dict[],size_t dict_size)780 void add_dict(json_object *register_ir, const char *field_name, UINT64 value,
781 	      const char *dict[], size_t dict_size)
782 {
783 	json_object *field_ir = json_object_new_object();
784 	json_object_object_add(register_ir, field_name, field_ir);
785 	json_object_object_add(field_ir, "raw", json_object_new_uint64(value));
786 
787 	if (dict != NULL) {
788 		if (value < dict_size) {
789 			const char *name = dict[value];
790 			if (name != NULL) {
791 				const char *value_name = name;
792 
793 				json_object_object_add(
794 					field_ir, "value",
795 					json_object_new_string(value_name));
796 			}
797 		}
798 	}
799 }
800