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