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