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", ®sArr);
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 ®Entries[ri].RegisterAddress);
654 get_value_hex_64(r, "value",
655 ®Entries[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", ®sArr);
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