xref: /openbmc/libcper/sections/cper-section-arm-ras.c (revision e1cba52dd4aeaeca17ec229251a8afc7be891d00)
1 /**
2  * See: https://developer.arm.com/documentation/den0085/latest/
3  * Minimal parser/generator for ARM RAS CPER section (Table 20/21)
4  * Author: prachotan.bathi@arm.com
5  */
6 #include <libcper/Cper.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <stdbool.h>
10 #include <inttypes.h>
11 #include <libcper/base64.h>
12 #include <libcper/cper-utils.h>
13 #include <libcper/sections/cper-section-arm-ras.h>
14 #include <libcper/log.h>
15 
16 /*
17  * Fixed-size fields in EFI_ARM_RAS_NODE.
18  *
19  * IPInstance: 16 bytes, serialized as a 32-character hex string.
20  * IPType:     24 bytes, serialized as a 48-character hex string.
21  * UserData:   16 bytes, but we emit up to 15 chars to keep a terminator.
22  */
arm_ras_set_desc_string_valid(char ** desc_string)23 static void arm_ras_set_desc_string_valid(char **desc_string)
24 {
25 	if (desc_string) {
26 		*desc_string = malloc(SECTION_DESC_STRING_SIZE);
27 		if (*desc_string) {
28 			snprintf(*desc_string, SECTION_DESC_STRING_SIZE,
29 				 "ARM RAS error occured");
30 		}
31 	}
32 }
33 
arm_ras_set_desc_string_invalid(const char * reason,char ** desc_string)34 static void arm_ras_set_desc_string_invalid(const char *reason,
35 					    char **desc_string)
36 {
37 	if (desc_string) {
38 		*desc_string = malloc(SECTION_DESC_STRING_SIZE);
39 		if (*desc_string) {
40 			snprintf(*desc_string, SECTION_DESC_STRING_SIZE,
41 				 "ARM RAS (empty): %s",
42 				 reason ? reason : "unspecified");
43 		}
44 	}
45 }
46 
arm_ras_read_node(EFI_ARM_RAS_NODE * node,const UINT8 * section,UINT32 size,char ** desc_string,json_object ** root)47 static bool arm_ras_read_node(EFI_ARM_RAS_NODE *node, const UINT8 *section,
48 			      UINT32 size, char **desc_string,
49 			      json_object **root)
50 {
51 	char reason[SECTION_DESC_STRING_SIZE];
52 	*root = json_object_new_object();
53 	if (size < sizeof(EFI_ARM_RAS_NODE)) {
54 		cper_print_log("ARM RAS section too small: %u < %zu",
55 			       (unsigned)size, sizeof(EFI_ARM_RAS_NODE));
56 		snprintf(reason, sizeof(reason), "invalid/too small %u < %zu",
57 			 (unsigned)size, sizeof(EFI_ARM_RAS_NODE));
58 		arm_ras_set_desc_string_invalid(reason, desc_string);
59 		return false;
60 	}
61 
62 	memcpy(node, section, sizeof(*node));
63 	UINT32 descriptorCount = node->ErrorSyndromeArrayNumEntries;
64 	UINT64 descBytes = (UINT64)descriptorCount *
65 			   (UINT64)sizeof(EFI_ARM_RAS_ERROR_RECORD_DESCRIPTOR);
66 	if (descBytes > (UINT64)(size - node->ErrorSyndromeArrayOffset)) {
67 		cper_print_log("ARM RAS descriptor array out of range");
68 		snprintf(reason, sizeof(reason),
69 			 "descriptor array out of range");
70 		arm_ras_set_desc_string_invalid(reason, desc_string);
71 		return false;
72 	}
73 	if (node->Revision != 1) {
74 		cper_print_log("Unsupported ARM RAS revision: %u",
75 			       node->Revision);
76 		snprintf(reason, sizeof(reason),
77 			 "unsupported ARM RAS revision: %u", node->Revision);
78 		arm_ras_set_desc_string_invalid(reason, desc_string);
79 		return false;
80 	}
81 
82 	if (node->ErrorSyndromeArrayOffset < sizeof(EFI_ARM_RAS_NODE) ||
83 	    node->ErrorSyndromeArrayOffset >= size ||
84 	    (node->AuxiliaryDataOffset && node->AuxiliaryDataOffset >= size)) {
85 		cper_print_log("Invalid ARM RAS offsets");
86 		snprintf(reason, sizeof(reason), "invalid ARM RAS offsets");
87 		arm_ras_set_desc_string_invalid(reason, desc_string);
88 		return false;
89 	}
90 
91 	return true;
92 }
93 
94 static const char *ipInstanceFormat[] = { "PE", "SystemPhysicalAddress",
95 					  "LocalAddressIdentifier",
96 					  "SocSpecificIpIdentifier" };
97 
98 static const char *componentType[] = {
99 	"ProcessorErrorNode",	  "MemoryErrorNode", "SMMUErrorNode",
100 	"VendorDefinedErrorNode", "GICErrorNode",    "PciExpressErrorNode",
101 	"ProxyErrorNode",
102 };
103 
arm_ras_add_fixed_fields(json_object * root,const EFI_ARM_RAS_NODE * node)104 static void arm_ras_add_fixed_fields(json_object *root,
105 				     const EFI_ARM_RAS_NODE *node)
106 {
107 	json_object_object_add(root, "revision",
108 			       json_object_new_uint64(node->Revision));
109 	add_dict(root, "componentType", node->ComponentType, componentType,
110 		 sizeof(componentType) / sizeof(componentType[0]));
111 	json_object_object_add(
112 		root, "errorSyndromeArrayNumEntries",
113 		json_object_new_uint64(node->ErrorSyndromeArrayNumEntries));
114 
115 	add_dict(root, "ipInstanceFormat", node->IPInstanceFormat,
116 		 ipInstanceFormat,
117 		 sizeof(ipInstanceFormat) / sizeof(ipInstanceFormat[0]));
118 
119 	json_object *ipInstance = json_object_new_object();
120 	switch (node->IPInstanceFormat) {
121 	case 0:
122 		add_int_hex_64(ipInstance, "mpidrEl1",
123 			       node->IPInstance.pe.MPIDR_EL1);
124 		break;
125 	case 1:
126 		add_int_hex_64(ipInstance, "systemPhysicalAddress",
127 			       node->IPInstance.systemPhysicalAddress
128 				       .SystemPhysicalAddress);
129 		break;
130 	case 2:
131 		add_int_hex_64(ipInstance, "localAddressIdentifier",
132 			       node->IPInstance.localAddressIdentifier
133 				       .SocSpecificLocalAddressSpace);
134 		add_int_hex_64(
135 			ipInstance, "baseAddress",
136 			node->IPInstance.localAddressIdentifier.BaseAddress);
137 		break;
138 	case 3:
139 		json_object_object_add(ipInstance, "socSpecificIpIdentifier",
140 				       json_object_new_string("<OpaqueData>"));
141 		break;
142 	}
143 	json_object_object_add(root, "ipInstance", ipInstance);
144 
145 	const char *ipTypeFormat[] = { "PE", "SMMU_IIDR", "GIC_IIDR", "PIDR" };
146 	add_dict(root, "ipTypeFormat", node->IPTypeFormat, ipTypeFormat,
147 		 sizeof(ipTypeFormat) / sizeof(ipTypeFormat[0]));
148 
149 	json_object *ipType = json_object_new_object();
150 	switch (node->IPTypeFormat) {
151 	case 0:
152 		add_int_hex_64(ipType, "midrEl1",
153 			       node->IPType.smmuIidr.MIDR_EL1);
154 		add_int_hex_64(ipType, "revidrEl1",
155 			       node->IPType.smmuIidr.REVIDR_EL1);
156 		add_int_hex_64(ipType, "aidrEl1",
157 			       node->IPType.smmuIidr.AIDR_EL1);
158 		break;
159 	case 1:
160 		add_int_hex_32(ipType, "iidr", node->IPType.gicIidr.IIDR);
161 		add_int_hex_32(ipType, "aidr", node->IPType.gicIidr.AIDR);
162 		break;
163 	case 2:
164 		add_int_hex_8(ipType, "pidr3", node->IPType.pidr.PIDR3);
165 		add_int_hex_8(ipType, "pidr2", node->IPType.pidr.PIDR2);
166 		add_int_hex_8(ipType, "pidr1", node->IPType.pidr.PIDR1);
167 		add_int_hex_8(ipType, "pidr0", node->IPType.pidr.PIDR0);
168 		add_int_hex_8(ipType, "pidr7", node->IPType.pidr.PIDR7);
169 		add_int_hex_8(ipType, "pidr6", node->IPType.pidr.PIDR6);
170 		add_int_hex_8(ipType, "pidr5", node->IPType.pidr.PIDR5);
171 		add_int_hex_8(ipType, "pidr4", node->IPType.pidr.PIDR4);
172 		break;
173 	}
174 
175 	json_object_object_add(root, "ipType", ipType);
176 
177 	add_untrusted_string(root, "userData", (const char *)node->UserData,
178 			     sizeof(node->UserData));
179 }
180 
arm_ras_parse_descriptors(const UINT8 * section,const EFI_ARM_RAS_NODE * node,UINT32 descriptorCount)181 static json_object *arm_ras_parse_descriptors(const UINT8 *section,
182 					      const EFI_ARM_RAS_NODE *node,
183 					      UINT32 descriptorCount)
184 {
185 	json_object *descArrObj = json_object_new_array();
186 	const UINT8 *desc_ptr = section + node->ErrorSyndromeArrayOffset;
187 
188 	for (UINT32 i = 0; i < descriptorCount; i++) {
189 		const UINT8 *cur =
190 			desc_ptr +
191 			i * sizeof(EFI_ARM_RAS_ERROR_RECORD_DESCRIPTOR);
192 		EFI_ARM_RAS_ERROR_RECORD_DESCRIPTOR d;
193 		memcpy(&d, cur, sizeof(d));
194 		json_object *desc = json_object_new_object();
195 		json_object_object_add(
196 			desc, "errorRecordIndex",
197 			json_object_new_uint64(d.ErrorRecordIndex));
198 		json_object_object_add(
199 			desc, "rasExtensionRevisionField",
200 			json_object_new_uint64((d.RasExtensionRevision >> 4) &
201 					       0x0F));
202 		json_object_object_add(
203 			desc, "rasExtensionArchVersion",
204 			json_object_new_uint64(d.RasExtensionRevision & 0x0F));
205 		add_int_hex_64(desc, "errorRecordFeatureRegister", d.ERR_FR);
206 		add_int_hex_64(desc, "errorRecordControlRegister", d.ERR_CTLR);
207 		add_int_hex_64(desc, "errorRecordPrimaryStatusRegister",
208 			       d.ERR_STATUS);
209 		add_int_hex_64(desc, "errorRecordAddressRegister", d.ERR_ADDR);
210 		add_int_hex_64(desc, "errorRecordMiscRegister0", d.ERR_MISC0);
211 		add_int_hex_64(desc, "errorRecordMiscRegister1", d.ERR_MISC1);
212 		if (d.RasExtensionRevision) {
213 			add_int_hex_64(desc, "errorRecordMiscRegister2",
214 				       d.ERR_MISC2);
215 			add_int_hex_64(desc, "errorRecordMiscRegister3",
216 				       d.ERR_MISC3);
217 		}
218 		json_object_array_add(descArrObj, desc);
219 	}
220 
221 	return descArrObj;
222 }
223 
224 /*
225  * Validate the fixed-size ARM RAS auxiliary header fields.
226  */
arm_ras_aux_hdr_valid(const EFI_ARM_RAS_AUX_DATA_HEADER * auxHdr,UINT32 auxLen)227 static bool arm_ras_aux_hdr_valid(const EFI_ARM_RAS_AUX_DATA_HEADER *auxHdr,
228 				  UINT32 auxLen)
229 {
230 	/*
231 	 * KVP array layout:
232 	 *   - When there are no entries, the offset must be exactly the
233 	 *     end of the aux blob.
234 	 *   - When entries are present, the offset must be somewhere inside
235 	 *     the aux blob (the upper bound is checked here, the lower
236 	 *     bound below).
237 	 */
238 	bool kvOffsetValid =
239 		((auxHdr->KeyValuePairArrayEntryCount == 0 &&
240 		  auxHdr->KeyValuePairArrayOffset ==
241 			  auxHdr->AuxiliaryDataSize) ||
242 		 (auxHdr->KeyValuePairArrayOffset < auxHdr->AuxiliaryDataSize));
243 	/*
244 	* The spec requires (Table 22):
245 	*   - Version must be 1
246 	*   - AuxiliaryDataSize is the total size of the aux block, including
247 	*     the header itself, and must:
248 	*       * fit within the remaining section buffer (<= auxLen) and
249 	*       * be at least large enough to hold the header.
250 	*/
251 	return (auxHdr->Version == 1) &&
252 	       (auxHdr->AuxiliaryDataSize <= auxLen) &&
253 	       (auxHdr->AuxiliaryDataSize >=
254 		sizeof(EFI_ARM_RAS_AUX_DATA_HEADER)) &&
255 	       kvOffsetValid &&
256 	       (auxHdr->KeyValuePairArrayOffset >=
257 		sizeof(EFI_ARM_RAS_AUX_DATA_HEADER));
258 }
259 
260 static json_object *
arm_ras_aux_emit_header_fields(const EFI_ARM_RAS_AUX_DATA_HEADER * auxHdr)261 arm_ras_aux_emit_header_fields(const EFI_ARM_RAS_AUX_DATA_HEADER *auxHdr)
262 {
263 	/* Emit auxiliary header fields in spec order (Table 22):
264 	 * version, reserved0 (omitted - always zero), addressSpaceArrayEntryCount,
265 	 * auxiliaryDataSize, keyValuePairArrayOffset, keyValuePairArrayEntryCount,
266 	 * reserved1 (omitted).
267 	 */
268 	json_object *auxStructured = json_object_new_object();
269 	json_object_object_add(auxStructured, "version",
270 			       json_object_new_uint64(auxHdr->Version));
271 	return auxStructured;
272 }
273 
274 static bool
arm_ras_aux_parse_contexts(json_object * auxStructured,const UINT8 * aux_ptr,const EFI_ARM_RAS_AUX_DATA_HEADER * auxHdr)275 arm_ras_aux_parse_contexts(json_object *auxStructured, const UINT8 *aux_ptr,
276 			   const EFI_ARM_RAS_AUX_DATA_HEADER *auxHdr)
277 {
278 	json_object *contexts = json_object_new_array();
279 	const UINT8 *cursor = aux_ptr + sizeof(EFI_ARM_RAS_AUX_DATA_HEADER);
280 	UINT32 remaining =
281 		auxHdr->AuxiliaryDataSize - sizeof(EFI_ARM_RAS_AUX_DATA_HEADER);
282 	bool ok = true;
283 
284 	for (UINT16 ci = 0; ci < auxHdr->AddressSpaceArrayEntryCount; ci++) {
285 		if (remaining < sizeof(EFI_ARM_RAS_AUX_CONTEXT_HEADER)) {
286 			ok = false;
287 			cper_print_log(
288 				"ARM RAS Auxiliary Data too small for context header: %u < %zu",
289 				remaining,
290 				sizeof(EFI_ARM_RAS_AUX_CONTEXT_HEADER));
291 			break;
292 		}
293 		const EFI_ARM_RAS_AUX_CONTEXT_HEADER *ctx =
294 			(const EFI_ARM_RAS_AUX_CONTEXT_HEADER *)cursor;
295 		if (ctx->Length < sizeof(EFI_ARM_RAS_AUX_CONTEXT_HEADER)) {
296 			ok = false;
297 			cper_print_log(
298 				"ARM RAS Auxiliary Context length too small: %u < %zu",
299 				ctx->Length,
300 				sizeof(EFI_ARM_RAS_AUX_CONTEXT_HEADER));
301 			break;
302 		}
303 		UINT32 needed = sizeof(EFI_ARM_RAS_AUX_CONTEXT_HEADER) +
304 				ctx->RegisterArrayEntryCount *
305 					sizeof(EFI_ARM_RAS_AUX_MM_REG_ENTRY);
306 		if (ctx->Length < needed || needed > remaining) {
307 			ok = false;
308 			cper_print_log(
309 				"ARM RAS Auxiliary Context length too small or exceeds remaining data: %u < %u or %u > %u",
310 				ctx->Length, needed, needed, remaining);
311 			break;
312 		}
313 		UINT32 afterCtxOffset =
314 			(UINT32)(cursor - aux_ptr) + ctx->Length;
315 		if (afterCtxOffset > auxHdr->KeyValuePairArrayOffset) {
316 			ok = false;
317 			cper_print_log(
318 				"ARM RAS Auxiliary Context overlaps KVP array");
319 			break;
320 		}
321 
322 		json_object *ctxObjInstance = json_object_new_object();
323 		json_object *flags = json_object_new_object();
324 
325 		static const char *addressSpaceIdentifierScope[2] = {
326 			"SystemPhysicalAddressSpace",
327 			"LocalAddressSpace",
328 		};
329 
330 		add_bool_enum(flags, "addressSpaceIdentifierScope",
331 			      addressSpaceIdentifierScope,
332 			      ctx->AddressSpaceIdentifierScope);
333 		json_object_object_add(ctxObjInstance, "flags", flags);
334 
335 		if (ctx->AddressSpaceIdentifierScope == 1) {
336 			json_object_object_add(
337 				ctxObjInstance, "addressSpaceIdentifier",
338 				json_object_new_uint64(
339 					(UINT64)ctx->AddressSpaceIdentifier));
340 		}
341 
342 		json_object *regs = json_object_new_array();
343 		const EFI_ARM_RAS_AUX_MM_REG_ENTRY *regArr =
344 			(const EFI_ARM_RAS_AUX_MM_REG_ENTRY
345 				 *)(cursor +
346 				    sizeof(EFI_ARM_RAS_AUX_CONTEXT_HEADER));
347 		for (UINT16 ri = 0; ri < ctx->RegisterArrayEntryCount; ri++) {
348 			json_object *r = json_object_new_object();
349 			add_int_hex_64(r, "address",
350 				       regArr[ri].RegisterAddress);
351 			add_int_hex_64(r, "value", regArr[ri].RegisterValue);
352 			json_object_array_add(regs, r);
353 		}
354 		json_object_object_add(ctxObjInstance, "registers", regs);
355 		json_object_array_add(contexts, ctxObjInstance);
356 
357 		cursor += ctx->Length;
358 		remaining -= ctx->Length;
359 	}
360 
361 	if (ok) {
362 		json_object_object_add(auxStructured, "contexts", contexts);
363 	}
364 
365 	return ok;
366 }
367 
is_mpam(EFI_GUID * key)368 int is_mpam(EFI_GUID *key)
369 {
370 	if (guid_equal(key, &EFI_ARM_RAS_KVP_UUID_MPAM_PARTID)) {
371 		return 1;
372 	}
373 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
374 	// The exact byte used here is arbitrary.
375 	return key->Data4[0] % 2;
376 #endif
377 	return 0;
378 }
379 
arm_ras_aux_parse_kvps(json_object * auxStructured,const UINT8 * aux_ptr,EFI_ARM_RAS_AUX_DATA_HEADER * auxHdr)380 static void arm_ras_aux_parse_kvps(json_object *auxStructured,
381 				   const UINT8 *aux_ptr,
382 				   EFI_ARM_RAS_AUX_DATA_HEADER *auxHdr)
383 {
384 	const UINT8 *kvBase = aux_ptr + auxHdr->KeyValuePairArrayOffset;
385 	UINT32 kvAvail =
386 		auxHdr->AuxiliaryDataSize - auxHdr->KeyValuePairArrayOffset;
387 	UINT32 kvNeeded = auxHdr->KeyValuePairArrayEntryCount *
388 			  sizeof(EFI_ARM_RAS_AUX_KEY_VALUE_PAIR);
389 	if (kvNeeded > kvAvail) {
390 		return;
391 	}
392 
393 	json_object *kvps = json_object_new_array();
394 	EFI_ARM_RAS_AUX_KEY_VALUE_PAIR *kvArr =
395 		(EFI_ARM_RAS_AUX_KEY_VALUE_PAIR *)kvBase;
396 	for (UINT16 ki = 0; ki < auxHdr->KeyValuePairArrayEntryCount; ki++) {
397 		json_object *kv = json_object_new_object();
398 		EFI_ARM_RAS_AUX_KEY_VALUE_PAIR *kvEntry = &kvArr[ki];
399 		EFI_GUID key = kvEntry->Key;
400 		add_guid(kv, "key", &key);
401 
402 		if (is_mpam(&key)) {
403 			UINT16 partId = (UINT16)(kvEntry->Value & 0xFFFF);
404 			json_object_object_add(kv, "mpamPartId",
405 					       json_object_new_uint64(partId));
406 		} else {
407 			add_int_hex_64(kv, "value", kvEntry->Value);
408 		}
409 		json_object_array_add(kvps, kv);
410 	}
411 	json_object_object_add(auxStructured, "keyValuePairs", kvps);
412 }
413 
arm_ras_parse_aux_data(const UINT8 * section,UINT32 size,const EFI_ARM_RAS_NODE * node,UINT64 descBytes)414 static json_object *arm_ras_parse_aux_data(const UINT8 *section, UINT32 size,
415 					   const EFI_ARM_RAS_NODE *node,
416 					   UINT64 descBytes)
417 {
418 	if (!node->AuxiliaryDataOffset) {
419 		return NULL;
420 	}
421 
422 	const UINT8 *aux_ptr = section + node->AuxiliaryDataOffset;
423 	if (node->AuxiliaryDataOffset < sizeof(EFI_ARM_RAS_NODE) + descBytes) {
424 		cper_print_log("ARM RAS aux offset overlaps descriptors");
425 		return NULL;
426 	}
427 
428 	UINT32 auxLen = size - node->AuxiliaryDataOffset;
429 	if (auxLen < sizeof(EFI_ARM_RAS_AUX_DATA_HEADER)) {
430 		cper_print_log("ARM RAS Auxiliary Data too small: %u < %zu",
431 			       auxLen, sizeof(EFI_ARM_RAS_AUX_DATA_HEADER));
432 		return NULL;
433 	}
434 
435 	EFI_ARM_RAS_AUX_DATA_HEADER *auxHdr =
436 		(EFI_ARM_RAS_AUX_DATA_HEADER *)aux_ptr;
437 	if (!arm_ras_aux_hdr_valid(auxHdr, auxLen)) {
438 		cper_print_log(
439 			"Invalid ARM RAS auxiliary header: version=%u, auxSize=%u, kvOffset=%u, kvCount=%u",
440 			auxHdr->Version, auxHdr->AuxiliaryDataSize,
441 			auxHdr->KeyValuePairArrayOffset,
442 			auxHdr->KeyValuePairArrayEntryCount);
443 		return NULL;
444 	}
445 
446 	json_object *auxStructured = arm_ras_aux_emit_header_fields(auxHdr);
447 	if (!arm_ras_aux_parse_contexts(auxStructured, aux_ptr, auxHdr)) {
448 		return auxStructured;
449 	}
450 	arm_ras_aux_parse_kvps(auxStructured, aux_ptr, auxHdr);
451 	return auxStructured;
452 }
453 
cper_section_arm_ras_to_ir(const UINT8 * section,UINT32 size,char ** desc_string)454 json_object *cper_section_arm_ras_to_ir(const UINT8 *section, UINT32 size,
455 					char **desc_string)
456 {
457 	EFI_ARM_RAS_NODE node;
458 	json_object *root = NULL;
459 	if (!arm_ras_read_node(&node, section, size, desc_string, &root)) {
460 		return root;
461 	}
462 
463 	UINT32 descriptorCount = node.ErrorSyndromeArrayNumEntries;
464 	UINT64 descBytes = (UINT64)descriptorCount *
465 			   (UINT64)sizeof(EFI_ARM_RAS_ERROR_RECORD_DESCRIPTOR);
466 
467 	arm_ras_set_desc_string_valid(desc_string);
468 	arm_ras_add_fixed_fields(root, &node);
469 
470 	json_object *descArray =
471 		arm_ras_parse_descriptors(section, &node, descriptorCount);
472 	json_object_object_add(root, "errorSyndromes", descArray);
473 
474 	json_object *auxStructured =
475 		arm_ras_parse_aux_data(section, size, &node, descBytes);
476 
477 	if (auxStructured) {
478 		json_object_object_add(root, "auxData", auxStructured);
479 	}
480 
481 	return root;
482 }
483 
arm_ras_fill_node_fixed_fields(EFI_ARM_RAS_NODE * node,json_object * section)484 static void arm_ras_fill_node_fixed_fields(EFI_ARM_RAS_NODE *node,
485 					   json_object *section)
486 {
487 	json_object *obj = NULL;
488 	memset(node, 0, sizeof(*node));
489 	if (json_object_object_get_ex(section, "revision", &obj)) {
490 		node->Revision = (UINT32)json_object_get_uint64(obj);
491 	}
492 	if (json_object_object_get_ex(section, "componentType", &obj)) {
493 		if (json_object_object_get_ex(obj, "raw", &obj)) {
494 			node->ComponentType = (UINT8)json_object_get_int(obj);
495 		}
496 	}
497 	if (json_object_object_get_ex(section, "ipInstanceFormat", &obj)) {
498 		if (json_object_object_get_ex(obj, "raw", &obj)) {
499 			node->IPInstanceFormat =
500 				(UINT8)json_object_get_int(obj);
501 		}
502 	}
503 	if (json_object_object_get_ex(section, "ipTypeFormat", &obj)) {
504 		if (json_object_object_get_ex(obj, "raw", &obj)) {
505 			node->IPTypeFormat = (UINT8)json_object_get_int(obj);
506 		}
507 	}
508 }
509 
arm_ras_fill_node_identifiers(EFI_ARM_RAS_NODE * node,json_object * section)510 static void arm_ras_fill_node_identifiers(EFI_ARM_RAS_NODE *node,
511 					  json_object *section)
512 {
513 	json_object *obj = NULL;
514 	if (json_object_object_get_ex(section, "ipInstance", &obj)) {
515 		get_value_hex_64(obj, "mpidrEl1",
516 				 &node->IPInstance.pe.MPIDR_EL1);
517 		get_value_hex_64(obj, "systemPhysicalAddress",
518 				 &node->IPInstance.systemPhysicalAddress
519 					  .SystemPhysicalAddress);
520 		get_value_hex_64(obj, "localAddressIdentifier",
521 				 &node->IPInstance.localAddressIdentifier
522 					  .SocSpecificLocalAddressSpace);
523 		get_value_hex_64(
524 			obj, "baseAddress",
525 			&node->IPInstance.localAddressIdentifier.BaseAddress);
526 
527 		//get_value_hex_64(obj, "socSpecificIpIdentifier", &node->IPInstance.socSpecificIpIdentifier.SocSpecificIPIdentifier);
528 	}
529 	if (json_object_object_get_ex(section, "ipType", &obj)) {
530 		get_value_hex_64(obj, "midrEl1",
531 				 &node->IPType.smmuIidr.MIDR_EL1);
532 		get_value_hex_64(obj, "revidrEl1",
533 				 &node->IPType.smmuIidr.REVIDR_EL1);
534 		get_value_hex_64(obj, "aidrEl1",
535 				 &node->IPType.smmuIidr.AIDR_EL1);
536 		get_value_hex_32(obj, "iidr", &node->IPType.gicIidr.IIDR);
537 		get_value_hex_32(obj, "aidr", &node->IPType.gicIidr.AIDR);
538 		get_value_hex_8(obj, "pidr3", &node->IPType.pidr.PIDR3);
539 		get_value_hex_8(obj, "pidr2", &node->IPType.pidr.PIDR2);
540 		get_value_hex_8(obj, "pidr1", &node->IPType.pidr.PIDR1);
541 		get_value_hex_8(obj, "pidr0", &node->IPType.pidr.PIDR0);
542 		get_value_hex_8(obj, "pidr7", &node->IPType.pidr.PIDR7);
543 		get_value_hex_8(obj, "pidr6", &node->IPType.pidr.PIDR6);
544 		get_value_hex_8(obj, "pidr5", &node->IPType.pidr.PIDR5);
545 		get_value_hex_8(obj, "pidr4", &node->IPType.pidr.PIDR4);
546 	}
547 }
548 
arm_ras_fill_node_user_data(EFI_ARM_RAS_NODE * node,json_object * section)549 static void arm_ras_fill_node_user_data(EFI_ARM_RAS_NODE *node,
550 					json_object *section)
551 {
552 	json_object *t = NULL;
553 	if (json_object_object_get_ex(section, "userData", &t)) {
554 		const char *s = json_object_get_string(t);
555 		int len = cper_printable_string_length(s, strlen(s) + 1);
556 
557 		if (len < 0) {
558 			cper_print_log("ARM RAS user data invalid len=%d", len);
559 			return;
560 		}
561 		if ((size_t)len > sizeof(node->UserData)) {
562 			cper_print_log("ARM RAS user data too long: %d > %zu",
563 				       len, sizeof(node->UserData));
564 			return;
565 		}
566 		memcpy(node->UserData, s, len);
567 		node->UserData[len] = 0;
568 	}
569 }
570 
arm_ras_init_descriptor_metadata(EFI_ARM_RAS_NODE * node,json_object * section,json_object ** descArr,UINT32 * descCount,UINT32 * afterDescriptors)571 static void arm_ras_init_descriptor_metadata(EFI_ARM_RAS_NODE *node,
572 					     json_object *section,
573 					     json_object **descArr,
574 					     UINT32 *descCount,
575 					     UINT32 *afterDescriptors)
576 {
577 	*descArr = NULL;
578 	*descCount = 0;
579 	if (json_object_object_get_ex(section, "errorSyndromes", descArr)) {
580 		*descCount = json_object_array_length(*descArr);
581 	}
582 	if (*descCount > 896) {
583 		/*
584 		 * Per the RAS System Architecture (Arm IHI0100), the error_syndrome_array
585 		 * has at most 896 entries. Clamp larger inputs to avoid emitting
586 		 * non-architectural records.
587 		 */
588 		cper_print_log(
589 			"ARM RAS error_syndrome_array entry count too large: %u > 896; clamping to 0",
590 			(unsigned)*descCount);
591 		*descCount = 0;
592 	}
593 	/* Compute offsets */
594 	node->ErrorSyndromeArrayOffset = (UINT16)sizeof(EFI_ARM_RAS_NODE);
595 	*afterDescriptors =
596 		sizeof(EFI_ARM_RAS_ERROR_RECORD_DESCRIPTOR) * (*descCount) +
597 		node->ErrorSyndromeArrayOffset;
598 }
599 
arm_ras_build_aux_contexts(UINT8 * builtAux,UINT16 ctxCount,json_object * contextsArr,UINT32 headerSize)600 static void arm_ras_build_aux_contexts(UINT8 *builtAux, UINT16 ctxCount,
601 				       json_object *contextsArr,
602 				       UINT32 headerSize)
603 {
604 	UINT8 *cursor = builtAux + headerSize;
605 	for (UINT16 ci = 0; ci < ctxCount; ci++) {
606 		json_object *ctx = json_object_array_get_idx(contextsArr, ci);
607 		if (!ctx) {
608 			continue;
609 		}
610 		json_object *regsArr = NULL;
611 		json_object_object_get_ex(ctx, "registers", &regsArr);
612 		UINT16 regCount =
613 			regsArr ? (UINT16)json_object_array_length(regsArr) : 0;
614 		UINT32 length = (UINT32)sizeof(EFI_ARM_RAS_AUX_CONTEXT_HEADER) +
615 				regCount * sizeof(EFI_ARM_RAS_AUX_MM_REG_ENTRY);
616 		EFI_ARM_RAS_AUX_CONTEXT_HEADER *ch =
617 			(EFI_ARM_RAS_AUX_CONTEXT_HEADER *)cursor;
618 		ch->Length = length;
619 		json_object *flagsObj = NULL;
620 		json_object_object_get_ex(ctx, "flags", &flagsObj);
621 		json_object *addressSpaceIdentifierScopeObj = NULL;
622 		json_object_object_get_ex(flagsObj,
623 					  "addressSpaceIdentifierScope",
624 					  &addressSpaceIdentifierScopeObj);
625 		const char *scope =
626 			json_object_get_string(addressSpaceIdentifierScopeObj);
627 		if (strcmp(scope, "LocalAddressSpace") == 0) {
628 			ch->AddressSpaceIdentifierScope = 1;
629 		}
630 		ch->AddressSpaceIdentifierScope =
631 			addressSpaceIdentifierScopeObj ?
632 				(UINT8)json_object_get_int(
633 					addressSpaceIdentifierScopeObj) :
634 				0;
635 		ch->Reserved0 = 0;
636 		ch->RegisterArrayEntryCount = regCount;
637 		json_object *asidObj = NULL;
638 		json_object_object_get_ex(ctx, "addressSpaceIdentifier",
639 					  &asidObj);
640 		memset(ch->Reserved1, 0, sizeof(ch->Reserved1));
641 		EFI_ARM_RAS_AUX_MM_REG_ENTRY *regEntries =
642 			(EFI_ARM_RAS_AUX_MM_REG_ENTRY
643 				 *)(cursor +
644 				    sizeof(EFI_ARM_RAS_AUX_CONTEXT_HEADER));
645 		for (UINT16 ri = 0; ri < regCount; ri++) {
646 			json_object *r = json_object_array_get_idx(regsArr, ri);
647 			if (!r) {
648 				regEntries[ri].RegisterAddress = 0;
649 				regEntries[ri].RegisterValue = 0;
650 				continue;
651 			}
652 			get_value_hex_64(r, "address",
653 					 &regEntries[ri].RegisterAddress);
654 			get_value_hex_64(r, "value",
655 					 &regEntries[ri].RegisterValue);
656 		}
657 		cursor += length;
658 	}
659 }
660 
arm_ras_build_aux_kvps(UINT8 * builtAux,UINT16 kvpCount,json_object * kvpArr,UINT32 kvpOffset)661 static void arm_ras_build_aux_kvps(UINT8 *builtAux, UINT16 kvpCount,
662 				   json_object *kvpArr, UINT32 kvpOffset)
663 {
664 	EFI_ARM_RAS_AUX_KEY_VALUE_PAIR *kvOut =
665 		(EFI_ARM_RAS_AUX_KEY_VALUE_PAIR *)(builtAux + kvpOffset);
666 	for (UINT16 ki = 0; ki < kvpCount; ki++) {
667 		json_object *kv = json_object_array_get_idx(kvpArr, ki);
668 		if (!kv) {
669 			continue;
670 		}
671 		json_object *keyObj = NULL;
672 		json_object_object_get_ex(kv, "key", &keyObj);
673 		const char *key = json_object_get_string(keyObj);
674 		if (key) {
675 			string_to_guid(&kvOut[ki].Key, key);
676 		}
677 		get_value_hex_64(kv, "value", &kvOut[ki].Value);
678 		json_object *mpamPartIdObj = NULL;
679 		if (json_object_object_get_ex(kv, "mpamPartId",
680 					      &mpamPartIdObj)) {
681 			kvOut[ki].Value =
682 				(UINT64)json_object_get_uint64(mpamPartIdObj);
683 		}
684 	}
685 }
686 
arm_ras_build_aux_blob(json_object * auxStructured,UINT8 ** builtAux,UINT32 * builtAuxLen)687 static void arm_ras_build_aux_blob(json_object *auxStructured, UINT8 **builtAux,
688 				   UINT32 *builtAuxLen)
689 {
690 	*builtAux = NULL;
691 	*builtAuxLen = 0;
692 	if (!auxStructured) {
693 		return;
694 	}
695 
696 	json_object *contextsArr = NULL;
697 	json_object_object_get_ex(auxStructured, "contexts", &contextsArr);
698 	json_object *kvpArr = NULL;
699 	json_object_object_get_ex(auxStructured, "keyValuePairs", &kvpArr);
700 	UINT16 ctxCount =
701 		contextsArr ? (UINT16)json_object_array_length(contextsArr) : 0;
702 	UINT16 kvpCount = kvpArr ? (UINT16)json_object_array_length(kvpArr) : 0;
703 	/* First compute size of contexts region */
704 	UINT32 contextsSize = 0;
705 	for (UINT16 i = 0; i < ctxCount; i++) {
706 		json_object *ctx = json_object_array_get_idx(contextsArr, i);
707 		if (!ctx) {
708 			continue;
709 		}
710 		json_object *regsArr = NULL;
711 		json_object_object_get_ex(ctx, "registers", &regsArr);
712 		UINT16 regCount =
713 			regsArr ? (UINT16)json_object_array_length(regsArr) : 0;
714 		UINT32 length = (UINT32)sizeof(EFI_ARM_RAS_AUX_CONTEXT_HEADER) +
715 				regCount * sizeof(EFI_ARM_RAS_AUX_MM_REG_ENTRY);
716 		contextsSize += length;
717 	}
718 	UINT32 headerSize = sizeof(EFI_ARM_RAS_AUX_DATA_HEADER);
719 	UINT32 kvpOffset =
720 		headerSize + contextsSize; /* from start of aux block */
721 	UINT32 kvpSize = kvpCount * sizeof(EFI_ARM_RAS_AUX_KEY_VALUE_PAIR);
722 	UINT32 auxSize = headerSize + contextsSize + kvpSize;
723 
724 	/* Per the spec, AuxiliaryDataSize can be up to 2^32, but that's extreme and unrealistic.
725 	 * We limit auxSize to 0xFFFF to support reasonable sizes while keeping the code simple.
726 	 */
727 	if (auxSize > 0xFFFF) {
728 		cper_print_log(
729 			"Implementation doesn't support large AuxiliaryDataSize: %u > 0xFFFF",
730 			auxSize);
731 		return;
732 	}
733 
734 	UINT8 *buf = (UINT8 *)calloc(1, auxSize);
735 	if (!buf) {
736 		return;
737 	}
738 
739 	EFI_ARM_RAS_AUX_DATA_HEADER *hdr = (EFI_ARM_RAS_AUX_DATA_HEADER *)buf;
740 	hdr->Version = 1;
741 	hdr->Reserved0 = 0;
742 	hdr->AddressSpaceArrayEntryCount = ctxCount;
743 	hdr->AuxiliaryDataSize = auxSize;
744 	hdr->KeyValuePairArrayOffset = kvpOffset;
745 	hdr->KeyValuePairArrayEntryCount = kvpCount;
746 	hdr->Reserved1 = 0;
747 
748 	/* Write contexts */
749 	arm_ras_build_aux_contexts(buf, ctxCount, contextsArr, headerSize);
750 	/* Write key-value pairs */
751 	arm_ras_build_aux_kvps(buf, kvpCount, kvpArr, kvpOffset);
752 
753 	*builtAux = buf;
754 	*builtAuxLen = auxSize;
755 }
756 
arm_ras_write_descriptors(json_object * descArr,UINT32 descCount,FILE * out)757 static void arm_ras_write_descriptors(json_object *descArr, UINT32 descCount,
758 				      FILE *out)
759 {
760 	for (UINT32 i = 0; i < descCount; i++) {
761 		EFI_ARM_RAS_ERROR_RECORD_DESCRIPTOR d;
762 		memset(&d, 0, sizeof(d));
763 		json_object *dj = json_object_array_get_idx(descArr, i);
764 		json_object *x = NULL;
765 		if (json_object_object_get_ex(dj, "errorRecordIndex", &x)) {
766 			d.ErrorRecordIndex = json_object_get_uint64(x);
767 		}
768 		/* Reconstruct rasExtensionRevision from split fields */
769 		UINT8 rev = 0;
770 		UINT8 arch = 0;
771 		if (json_object_object_get_ex(dj, "rasExtensionRevisionField",
772 					      &x)) {
773 			rev = (UINT8)json_object_get_uint64(x);
774 		}
775 		if (json_object_object_get_ex(dj, "rasExtensionArchVersion",
776 					      &x)) {
777 			arch = (UINT8)json_object_get_uint64(x);
778 		}
779 		d.RasExtensionRevision = ((rev & 0x0F) << 4) | (arch & 0x0F);
780 		get_value_hex_64(dj, "errorRecordFeatureRegister", &d.ERR_FR);
781 		get_value_hex_64(dj, "errorRecordControlRegister", &d.ERR_CTLR);
782 		get_value_hex_64(dj, "errorRecordPrimaryStatusRegister",
783 				 &d.ERR_STATUS);
784 		get_value_hex_64(dj, "errorRecordAddressRegister", &d.ERR_ADDR);
785 		get_value_hex_64(dj, "errorRecordMiscRegister0", &d.ERR_MISC0);
786 		get_value_hex_64(dj, "errorRecordMiscRegister1", &d.ERR_MISC1);
787 		get_value_hex_64(dj, "errorRecordMiscRegister2", &d.ERR_MISC2);
788 		get_value_hex_64(dj, "errorRecordMiscRegister3", &d.ERR_MISC3);
789 		fwrite(&d, sizeof(d), 1, out);
790 	}
791 }
792 
ir_section_arm_ras_to_cper(json_object * section,FILE * out)793 void ir_section_arm_ras_to_cper(json_object *section, FILE *out)
794 {
795 	EFI_ARM_RAS_NODE node;
796 	arm_ras_fill_node_fixed_fields(&node, section);
797 	arm_ras_fill_node_identifiers(&node, section);
798 	arm_ras_fill_node_user_data(&node, section);
799 
800 	json_object *descArr = NULL;
801 	UINT32 descCount = 0;
802 	UINT32 afterDescriptors = 0;
803 	arm_ras_init_descriptor_metadata(&node, section, &descArr, &descCount,
804 					 &afterDescriptors);
805 
806 	json_object *auxStructured = NULL;
807 	json_object_object_get_ex(section, "auxData", &auxStructured);
808 	UINT8 *builtAux = NULL;
809 	UINT32 builtAuxLen = 0;
810 	arm_ras_build_aux_blob(auxStructured, &builtAux, &builtAuxLen);
811 	if (builtAux) {
812 		/*
813 		 * Architecturally safe: from the RAS System Architecture (Arm IHI0100),
814 		 * the header is 80 bytes and each ErrorSyndromes entry is 72 bytes,
815 		 * with at most 896 entries. So the maximum AuxiliaryDataOffset is
816 		 *   80 + 896 * 72 < 2^16,
817 		 * and fits in the UINT16 field.
818 		 */
819 		node.AuxiliaryDataOffset = (UINT16)afterDescriptors;
820 	} else {
821 		node.AuxiliaryDataOffset = 0;
822 	}
823 	node.ErrorSyndromeArrayNumEntries = descCount; // N
824 
825 	fwrite(&node, sizeof(node), 1, out);
826 	arm_ras_write_descriptors(descArr, descCount, out);
827 	if (builtAux) {
828 		fwrite(builtAux, builtAuxLen, 1, out);
829 		free(builtAux);
830 	}
831 	fflush(out);
832 }
833