/** * See: https://developer.arm.com/documentation/den0085/latest/ * Minimal parser/generator for ARM RAS CPER section (Table 20/21) * Author: prachotan.bathi@arm.com */ #include #include #include #include #include #include #include #include #include /* * Fixed-size fields in EFI_ARM_RAS_NODE. * * IPInstance: 16 bytes, serialized as a 32-character hex string. * IPType: 24 bytes, serialized as a 48-character hex string. * UserData: 16 bytes, but we emit up to 15 chars to keep a terminator. */ static void arm_ras_set_desc_string_valid(char **desc_string) { if (desc_string) { *desc_string = malloc(SECTION_DESC_STRING_SIZE); if (*desc_string) { snprintf(*desc_string, SECTION_DESC_STRING_SIZE, "ARM RAS error occured"); } } } static void arm_ras_set_desc_string_invalid(const char *reason, char **desc_string) { if (desc_string) { *desc_string = malloc(SECTION_DESC_STRING_SIZE); if (*desc_string) { snprintf(*desc_string, SECTION_DESC_STRING_SIZE, "ARM RAS (empty): %s", reason ? reason : "unspecified"); } } } static bool arm_ras_read_node(EFI_ARM_RAS_NODE *node, const UINT8 *section, UINT32 size, char **desc_string, json_object **root) { char reason[SECTION_DESC_STRING_SIZE]; *root = json_object_new_object(); if (size < sizeof(EFI_ARM_RAS_NODE)) { cper_print_log("ARM RAS section too small: %u < %zu", (unsigned)size, sizeof(EFI_ARM_RAS_NODE)); snprintf(reason, sizeof(reason), "invalid/too small %u < %zu", (unsigned)size, sizeof(EFI_ARM_RAS_NODE)); arm_ras_set_desc_string_invalid(reason, desc_string); return false; } memcpy(node, section, sizeof(*node)); UINT32 descriptorCount = node->ErrorSyndromeArrayNumEntries; UINT64 descBytes = (UINT64)descriptorCount * (UINT64)sizeof(EFI_ARM_RAS_ERROR_RECORD_DESCRIPTOR); if (descBytes > (UINT64)(size - node->ErrorSyndromeArrayOffset)) { cper_print_log("ARM RAS descriptor array out of range"); snprintf(reason, sizeof(reason), "descriptor array out of range"); arm_ras_set_desc_string_invalid(reason, desc_string); return false; } if (node->Revision != 1) { cper_print_log("Unsupported ARM RAS revision: %u", node->Revision); snprintf(reason, sizeof(reason), "unsupported ARM RAS revision: %u", node->Revision); arm_ras_set_desc_string_invalid(reason, desc_string); return false; } if (node->ErrorSyndromeArrayOffset < sizeof(EFI_ARM_RAS_NODE) || node->ErrorSyndromeArrayOffset >= size || (node->AuxiliaryDataOffset && node->AuxiliaryDataOffset >= size)) { cper_print_log("Invalid ARM RAS offsets"); snprintf(reason, sizeof(reason), "invalid ARM RAS offsets"); arm_ras_set_desc_string_invalid(reason, desc_string); return false; } return true; } static const char *ipInstanceFormat[] = { "PE", "SystemPhysicalAddress", "LocalAddressIdentifier", "SocSpecificIpIdentifier" }; static const char *componentType[] = { "ProcessorErrorNode", "MemoryErrorNode", "SMMUErrorNode", "VendorDefinedErrorNode", "GICErrorNode", "PciExpressErrorNode", "ProxyErrorNode", }; static void arm_ras_add_fixed_fields(json_object *root, const EFI_ARM_RAS_NODE *node) { add_uint(root, "revision", node->Revision); add_dict(root, "componentType", node->ComponentType, componentType, sizeof(componentType) / sizeof(componentType[0])); add_uint(root, "errorSyndromeArrayNumEntries", node->ErrorSyndromeArrayNumEntries); add_dict(root, "ipInstanceFormat", node->IPInstanceFormat, ipInstanceFormat, sizeof(ipInstanceFormat) / sizeof(ipInstanceFormat[0])); json_object *ipInstance = json_object_new_object(); switch (node->IPInstanceFormat) { case 0: add_int_hex_64(ipInstance, "mpidrEl1", node->IPInstance.pe.MPIDR_EL1); break; case 1: add_int_hex_64(ipInstance, "systemPhysicalAddress", node->IPInstance.systemPhysicalAddress .SystemPhysicalAddress); break; case 2: add_int_hex_64(ipInstance, "localAddressIdentifier", node->IPInstance.localAddressIdentifier .SocSpecificLocalAddressSpace); add_int_hex_64( ipInstance, "baseAddress", node->IPInstance.localAddressIdentifier.BaseAddress); break; case 3: add_string(ipInstance, "socSpecificIpIdentifier", ""); break; } json_object_object_add(root, "ipInstance", ipInstance); const char *ipTypeFormat[] = { "PE", "SMMU_IIDR", "GIC_IIDR", "PIDR" }; add_dict(root, "ipTypeFormat", node->IPTypeFormat, ipTypeFormat, sizeof(ipTypeFormat) / sizeof(ipTypeFormat[0])); json_object *ipType = json_object_new_object(); switch (node->IPTypeFormat) { case 0: add_int_hex_64(ipType, "midrEl1", node->IPType.smmuIidr.MIDR_EL1); add_int_hex_64(ipType, "revidrEl1", node->IPType.smmuIidr.REVIDR_EL1); add_int_hex_64(ipType, "aidrEl1", node->IPType.smmuIidr.AIDR_EL1); break; case 1: add_int_hex_32(ipType, "iidr", node->IPType.gicIidr.IIDR); add_int_hex_32(ipType, "aidr", node->IPType.gicIidr.AIDR); break; case 2: add_int_hex_8(ipType, "pidr3", node->IPType.pidr.PIDR3); add_int_hex_8(ipType, "pidr2", node->IPType.pidr.PIDR2); add_int_hex_8(ipType, "pidr1", node->IPType.pidr.PIDR1); add_int_hex_8(ipType, "pidr0", node->IPType.pidr.PIDR0); add_int_hex_8(ipType, "pidr7", node->IPType.pidr.PIDR7); add_int_hex_8(ipType, "pidr6", node->IPType.pidr.PIDR6); add_int_hex_8(ipType, "pidr5", node->IPType.pidr.PIDR5); add_int_hex_8(ipType, "pidr4", node->IPType.pidr.PIDR4); break; } json_object_object_add(root, "ipType", ipType); add_untrusted_string(root, "userData", (const char *)node->UserData, sizeof(node->UserData)); } static json_object *arm_ras_parse_descriptors(const UINT8 *section, const EFI_ARM_RAS_NODE *node, UINT32 descriptorCount) { json_object *descArrObj = json_object_new_array(); const UINT8 *desc_ptr = section + node->ErrorSyndromeArrayOffset; for (UINT32 i = 0; i < descriptorCount; i++) { const UINT8 *cur = desc_ptr + i * sizeof(EFI_ARM_RAS_ERROR_RECORD_DESCRIPTOR); EFI_ARM_RAS_ERROR_RECORD_DESCRIPTOR d; memcpy(&d, cur, sizeof(d)); json_object *desc = json_object_new_object(); add_uint(desc, "errorRecordIndex", d.ErrorRecordIndex); add_uint(desc, "rasExtensionRevisionField", (d.RasExtensionRevision >> 4) & 0x0F); add_uint(desc, "rasExtensionArchVersion", d.RasExtensionRevision & 0x0F); add_int_hex_64(desc, "errorRecordFeatureRegister", d.ERR_FR); add_int_hex_64(desc, "errorRecordControlRegister", d.ERR_CTLR); add_int_hex_64(desc, "errorRecordPrimaryStatusRegister", d.ERR_STATUS); add_int_hex_64(desc, "errorRecordAddressRegister", d.ERR_ADDR); add_int_hex_64(desc, "errorRecordMiscRegister0", d.ERR_MISC0); add_int_hex_64(desc, "errorRecordMiscRegister1", d.ERR_MISC1); if (d.RasExtensionRevision) { add_int_hex_64(desc, "errorRecordMiscRegister2", d.ERR_MISC2); add_int_hex_64(desc, "errorRecordMiscRegister3", d.ERR_MISC3); } json_object_array_add(descArrObj, desc); } return descArrObj; } /* * Validate the fixed-size ARM RAS auxiliary header fields. */ static bool arm_ras_aux_hdr_valid(const EFI_ARM_RAS_AUX_DATA_HEADER *auxHdr, UINT32 auxLen) { /* * KVP array layout: * - When there are no entries, the offset must be exactly the * end of the aux blob. * - When entries are present, the offset must be somewhere inside * the aux blob (the upper bound is checked here, the lower * bound below). */ bool kvOffsetValid = ((auxHdr->KeyValuePairArrayEntryCount == 0 && auxHdr->KeyValuePairArrayOffset == auxHdr->AuxiliaryDataSize) || (auxHdr->KeyValuePairArrayOffset < auxHdr->AuxiliaryDataSize)); /* * The spec requires (Table 22): * - Version must be 1 * - AuxiliaryDataSize is the total size of the aux block, including * the header itself, and must: * * fit within the remaining section buffer (<= auxLen) and * * be at least large enough to hold the header. */ return (auxHdr->Version == 1) && (auxHdr->AuxiliaryDataSize <= auxLen) && (auxHdr->AuxiliaryDataSize >= sizeof(EFI_ARM_RAS_AUX_DATA_HEADER)) && kvOffsetValid && (auxHdr->KeyValuePairArrayOffset >= sizeof(EFI_ARM_RAS_AUX_DATA_HEADER)); } static json_object * arm_ras_aux_emit_header_fields(const EFI_ARM_RAS_AUX_DATA_HEADER *auxHdr) { /* Emit auxiliary header fields in spec order (Table 22): * version, reserved0 (omitted - always zero), addressSpaceArrayEntryCount, * auxiliaryDataSize, keyValuePairArrayOffset, keyValuePairArrayEntryCount, * reserved1 (omitted). */ json_object *auxStructured = json_object_new_object(); add_uint(auxStructured, "version", auxHdr->Version); return auxStructured; } static bool arm_ras_aux_parse_contexts(json_object *auxStructured, const UINT8 *aux_ptr, const EFI_ARM_RAS_AUX_DATA_HEADER *auxHdr) { json_object *contexts = json_object_new_array(); const UINT8 *cursor = aux_ptr + sizeof(EFI_ARM_RAS_AUX_DATA_HEADER); UINT32 remaining = auxHdr->AuxiliaryDataSize - sizeof(EFI_ARM_RAS_AUX_DATA_HEADER); bool ok = true; for (UINT16 ci = 0; ci < auxHdr->AddressSpaceArrayEntryCount; ci++) { if (remaining < sizeof(EFI_ARM_RAS_AUX_CONTEXT_HEADER)) { ok = false; cper_print_log( "ARM RAS Auxiliary Data too small for context header: %u < %zu", remaining, sizeof(EFI_ARM_RAS_AUX_CONTEXT_HEADER)); break; } const EFI_ARM_RAS_AUX_CONTEXT_HEADER *ctx = (const EFI_ARM_RAS_AUX_CONTEXT_HEADER *)cursor; if (ctx->Length < sizeof(EFI_ARM_RAS_AUX_CONTEXT_HEADER)) { ok = false; cper_print_log( "ARM RAS Auxiliary Context length too small: %u < %zu", ctx->Length, sizeof(EFI_ARM_RAS_AUX_CONTEXT_HEADER)); break; } UINT32 needed = sizeof(EFI_ARM_RAS_AUX_CONTEXT_HEADER) + ctx->RegisterArrayEntryCount * sizeof(EFI_ARM_RAS_AUX_MM_REG_ENTRY); if (ctx->Length < needed || needed > remaining) { ok = false; cper_print_log( "ARM RAS Auxiliary Context length too small or exceeds remaining data: %u < %u or %u > %u", ctx->Length, needed, needed, remaining); break; } UINT32 afterCtxOffset = (UINT32)(cursor - aux_ptr) + ctx->Length; if (afterCtxOffset > auxHdr->KeyValuePairArrayOffset) { ok = false; cper_print_log( "ARM RAS Auxiliary Context overlaps KVP array"); break; } json_object *ctxObjInstance = json_object_new_object(); json_object *flags = json_object_new_object(); static const char *addressSpaceIdentifierScope[2] = { "SystemPhysicalAddressSpace", "LocalAddressSpace", }; add_bool_enum(flags, "addressSpaceIdentifierScope", addressSpaceIdentifierScope, ctx->AddressSpaceIdentifierScope); json_object_object_add(ctxObjInstance, "flags", flags); if (ctx->AddressSpaceIdentifierScope == 1) { add_uint(ctxObjInstance, "addressSpaceIdentifier", (UINT64)ctx->AddressSpaceIdentifier); } json_object *regs = json_object_new_array(); const EFI_ARM_RAS_AUX_MM_REG_ENTRY *regArr = (const EFI_ARM_RAS_AUX_MM_REG_ENTRY *)(cursor + sizeof(EFI_ARM_RAS_AUX_CONTEXT_HEADER)); for (UINT16 ri = 0; ri < ctx->RegisterArrayEntryCount; ri++) { json_object *r = json_object_new_object(); add_int_hex_64(r, "address", regArr[ri].RegisterAddress); add_int_hex_64(r, "value", regArr[ri].RegisterValue); json_object_array_add(regs, r); } json_object_object_add(ctxObjInstance, "registers", regs); json_object_array_add(contexts, ctxObjInstance); cursor += ctx->Length; remaining -= ctx->Length; } if (ok) { json_object_object_add(auxStructured, "contexts", contexts); } return ok; } int is_mpam(EFI_GUID *key) { if (guid_equal(key, &EFI_ARM_RAS_KVP_UUID_MPAM_PARTID)) { return 1; } #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION // The exact byte used here is arbitrary. return key->Data4[0] % 2; #endif return 0; } static void arm_ras_aux_parse_kvps(json_object *auxStructured, const UINT8 *aux_ptr, EFI_ARM_RAS_AUX_DATA_HEADER *auxHdr) { const UINT8 *kvBase = aux_ptr + auxHdr->KeyValuePairArrayOffset; UINT32 kvAvail = auxHdr->AuxiliaryDataSize - auxHdr->KeyValuePairArrayOffset; UINT32 kvNeeded = auxHdr->KeyValuePairArrayEntryCount * sizeof(EFI_ARM_RAS_AUX_KEY_VALUE_PAIR); if (kvNeeded > kvAvail) { return; } json_object *kvps = json_object_new_array(); EFI_ARM_RAS_AUX_KEY_VALUE_PAIR *kvArr = (EFI_ARM_RAS_AUX_KEY_VALUE_PAIR *)kvBase; for (UINT16 ki = 0; ki < auxHdr->KeyValuePairArrayEntryCount; ki++) { json_object *kv = json_object_new_object(); EFI_ARM_RAS_AUX_KEY_VALUE_PAIR *kvEntry = &kvArr[ki]; EFI_GUID key = kvEntry->Key; add_guid(kv, "key", &key); if (is_mpam(&key)) { UINT16 partId = (UINT16)(kvEntry->Value & 0xFFFF); add_uint(kv, "mpamPartId", partId); } else { add_int_hex_64(kv, "value", kvEntry->Value); } json_object_array_add(kvps, kv); } json_object_object_add(auxStructured, "keyValuePairs", kvps); } static json_object *arm_ras_parse_aux_data(const UINT8 *section, UINT32 size, const EFI_ARM_RAS_NODE *node, UINT64 descBytes) { if (!node->AuxiliaryDataOffset) { return NULL; } const UINT8 *aux_ptr = section + node->AuxiliaryDataOffset; if (node->AuxiliaryDataOffset < sizeof(EFI_ARM_RAS_NODE) + descBytes) { cper_print_log("ARM RAS aux offset overlaps descriptors"); return NULL; } UINT32 auxLen = size - node->AuxiliaryDataOffset; if (auxLen < sizeof(EFI_ARM_RAS_AUX_DATA_HEADER)) { cper_print_log("ARM RAS Auxiliary Data too small: %u < %zu", auxLen, sizeof(EFI_ARM_RAS_AUX_DATA_HEADER)); return NULL; } EFI_ARM_RAS_AUX_DATA_HEADER *auxHdr = (EFI_ARM_RAS_AUX_DATA_HEADER *)aux_ptr; if (!arm_ras_aux_hdr_valid(auxHdr, auxLen)) { cper_print_log( "Invalid ARM RAS auxiliary header: version=%u, auxSize=%u, kvOffset=%u, kvCount=%u", auxHdr->Version, auxHdr->AuxiliaryDataSize, auxHdr->KeyValuePairArrayOffset, auxHdr->KeyValuePairArrayEntryCount); return NULL; } json_object *auxStructured = arm_ras_aux_emit_header_fields(auxHdr); if (!arm_ras_aux_parse_contexts(auxStructured, aux_ptr, auxHdr)) { return auxStructured; } arm_ras_aux_parse_kvps(auxStructured, aux_ptr, auxHdr); return auxStructured; } json_object *cper_section_arm_ras_to_ir(const UINT8 *section, UINT32 size, char **desc_string) { EFI_ARM_RAS_NODE node; json_object *root = NULL; if (!arm_ras_read_node(&node, section, size, desc_string, &root)) { return root; } UINT32 descriptorCount = node.ErrorSyndromeArrayNumEntries; UINT64 descBytes = (UINT64)descriptorCount * (UINT64)sizeof(EFI_ARM_RAS_ERROR_RECORD_DESCRIPTOR); arm_ras_set_desc_string_valid(desc_string); arm_ras_add_fixed_fields(root, &node); json_object *descArray = arm_ras_parse_descriptors(section, &node, descriptorCount); json_object_object_add(root, "errorSyndromes", descArray); json_object *auxStructured = arm_ras_parse_aux_data(section, size, &node, descBytes); if (auxStructured) { json_object_object_add(root, "auxData", auxStructured); } return root; } static void arm_ras_fill_node_fixed_fields(EFI_ARM_RAS_NODE *node, json_object *section) { json_object *obj = NULL; memset(node, 0, sizeof(*node)); if (json_object_object_get_ex(section, "revision", &obj)) { node->Revision = (UINT32)json_object_get_uint64(obj); } if (json_object_object_get_ex(section, "componentType", &obj)) { if (json_object_object_get_ex(obj, "raw", &obj)) { node->ComponentType = (UINT8)json_object_get_int(obj); } } if (json_object_object_get_ex(section, "ipInstanceFormat", &obj)) { if (json_object_object_get_ex(obj, "raw", &obj)) { node->IPInstanceFormat = (UINT8)json_object_get_int(obj); } } if (json_object_object_get_ex(section, "ipTypeFormat", &obj)) { if (json_object_object_get_ex(obj, "raw", &obj)) { node->IPTypeFormat = (UINT8)json_object_get_int(obj); } } } static void arm_ras_fill_node_identifiers(EFI_ARM_RAS_NODE *node, json_object *section) { json_object *obj = NULL; if (json_object_object_get_ex(section, "ipInstance", &obj)) { get_value_hex_64(obj, "mpidrEl1", &node->IPInstance.pe.MPIDR_EL1); get_value_hex_64(obj, "systemPhysicalAddress", &node->IPInstance.systemPhysicalAddress .SystemPhysicalAddress); get_value_hex_64(obj, "localAddressIdentifier", &node->IPInstance.localAddressIdentifier .SocSpecificLocalAddressSpace); get_value_hex_64( obj, "baseAddress", &node->IPInstance.localAddressIdentifier.BaseAddress); //get_value_hex_64(obj, "socSpecificIpIdentifier", &node->IPInstance.socSpecificIpIdentifier.SocSpecificIPIdentifier); } if (json_object_object_get_ex(section, "ipType", &obj)) { get_value_hex_64(obj, "midrEl1", &node->IPType.smmuIidr.MIDR_EL1); get_value_hex_64(obj, "revidrEl1", &node->IPType.smmuIidr.REVIDR_EL1); get_value_hex_64(obj, "aidrEl1", &node->IPType.smmuIidr.AIDR_EL1); get_value_hex_32(obj, "iidr", &node->IPType.gicIidr.IIDR); get_value_hex_32(obj, "aidr", &node->IPType.gicIidr.AIDR); get_value_hex_8(obj, "pidr3", &node->IPType.pidr.PIDR3); get_value_hex_8(obj, "pidr2", &node->IPType.pidr.PIDR2); get_value_hex_8(obj, "pidr1", &node->IPType.pidr.PIDR1); get_value_hex_8(obj, "pidr0", &node->IPType.pidr.PIDR0); get_value_hex_8(obj, "pidr7", &node->IPType.pidr.PIDR7); get_value_hex_8(obj, "pidr6", &node->IPType.pidr.PIDR6); get_value_hex_8(obj, "pidr5", &node->IPType.pidr.PIDR5); get_value_hex_8(obj, "pidr4", &node->IPType.pidr.PIDR4); } } static void arm_ras_fill_node_user_data(EFI_ARM_RAS_NODE *node, json_object *section) { json_object *t = NULL; if (json_object_object_get_ex(section, "userData", &t)) { const char *s = json_object_get_string(t); int len = cper_printable_string_length(s, strlen(s) + 1); if (len < 0) { cper_print_log("ARM RAS user data invalid len=%d", len); return; } if ((size_t)len > sizeof(node->UserData)) { cper_print_log("ARM RAS user data too long: %d > %zu", len, sizeof(node->UserData)); return; } memcpy(node->UserData, s, len); node->UserData[len] = 0; } } static void arm_ras_init_descriptor_metadata(EFI_ARM_RAS_NODE *node, json_object *section, json_object **descArr, UINT32 *descCount, UINT32 *afterDescriptors) { *descArr = NULL; *descCount = 0; if (json_object_object_get_ex(section, "errorSyndromes", descArr)) { *descCount = json_object_array_length(*descArr); } if (*descCount > 896) { /* * Per the RAS System Architecture (Arm IHI0100), the error_syndrome_array * has at most 896 entries. Clamp larger inputs to avoid emitting * non-architectural records. */ cper_print_log( "ARM RAS error_syndrome_array entry count too large: %u > 896; clamping to 0", (unsigned)*descCount); *descCount = 0; } /* Compute offsets */ node->ErrorSyndromeArrayOffset = (UINT16)sizeof(EFI_ARM_RAS_NODE); *afterDescriptors = sizeof(EFI_ARM_RAS_ERROR_RECORD_DESCRIPTOR) * (*descCount) + node->ErrorSyndromeArrayOffset; } static void arm_ras_build_aux_contexts(UINT8 *builtAux, UINT16 ctxCount, json_object *contextsArr, UINT32 headerSize) { UINT8 *cursor = builtAux + headerSize; for (UINT16 ci = 0; ci < ctxCount; ci++) { json_object *ctx = json_object_array_get_idx(contextsArr, ci); if (!ctx) { continue; } json_object *regsArr = NULL; json_object_object_get_ex(ctx, "registers", ®sArr); UINT16 regCount = regsArr ? (UINT16)json_object_array_length(regsArr) : 0; UINT32 length = (UINT32)sizeof(EFI_ARM_RAS_AUX_CONTEXT_HEADER) + regCount * sizeof(EFI_ARM_RAS_AUX_MM_REG_ENTRY); EFI_ARM_RAS_AUX_CONTEXT_HEADER *ch = (EFI_ARM_RAS_AUX_CONTEXT_HEADER *)cursor; ch->Length = length; json_object *flagsObj = NULL; json_object_object_get_ex(ctx, "flags", &flagsObj); json_object *addressSpaceIdentifierScopeObj = NULL; json_object_object_get_ex(flagsObj, "addressSpaceIdentifierScope", &addressSpaceIdentifierScopeObj); const char *scope = json_object_get_string(addressSpaceIdentifierScopeObj); if (strcmp(scope, "LocalAddressSpace") == 0) { ch->AddressSpaceIdentifierScope = 1; } ch->AddressSpaceIdentifierScope = addressSpaceIdentifierScopeObj ? (UINT8)json_object_get_int( addressSpaceIdentifierScopeObj) : 0; ch->Reserved0 = 0; ch->RegisterArrayEntryCount = regCount; json_object *asidObj = NULL; json_object_object_get_ex(ctx, "addressSpaceIdentifier", &asidObj); memset(ch->Reserved1, 0, sizeof(ch->Reserved1)); EFI_ARM_RAS_AUX_MM_REG_ENTRY *regEntries = (EFI_ARM_RAS_AUX_MM_REG_ENTRY *)(cursor + sizeof(EFI_ARM_RAS_AUX_CONTEXT_HEADER)); for (UINT16 ri = 0; ri < regCount; ri++) { json_object *r = json_object_array_get_idx(regsArr, ri); if (!r) { regEntries[ri].RegisterAddress = 0; regEntries[ri].RegisterValue = 0; continue; } get_value_hex_64(r, "address", ®Entries[ri].RegisterAddress); get_value_hex_64(r, "value", ®Entries[ri].RegisterValue); } cursor += length; } } static void arm_ras_build_aux_kvps(UINT8 *builtAux, UINT16 kvpCount, json_object *kvpArr, UINT32 kvpOffset) { EFI_ARM_RAS_AUX_KEY_VALUE_PAIR *kvOut = (EFI_ARM_RAS_AUX_KEY_VALUE_PAIR *)(builtAux + kvpOffset); for (UINT16 ki = 0; ki < kvpCount; ki++) { json_object *kv = json_object_array_get_idx(kvpArr, ki); if (!kv) { continue; } json_object *keyObj = NULL; json_object_object_get_ex(kv, "key", &keyObj); const char *key = json_object_get_string(keyObj); if (key) { string_to_guid(&kvOut[ki].Key, key); } get_value_hex_64(kv, "value", &kvOut[ki].Value); json_object *mpamPartIdObj = NULL; if (json_object_object_get_ex(kv, "mpamPartId", &mpamPartIdObj)) { kvOut[ki].Value = (UINT64)json_object_get_uint64(mpamPartIdObj); } } } static void arm_ras_build_aux_blob(json_object *auxStructured, UINT8 **builtAux, UINT32 *builtAuxLen) { *builtAux = NULL; *builtAuxLen = 0; if (!auxStructured) { return; } json_object *contextsArr = NULL; json_object_object_get_ex(auxStructured, "contexts", &contextsArr); json_object *kvpArr = NULL; json_object_object_get_ex(auxStructured, "keyValuePairs", &kvpArr); UINT16 ctxCount = contextsArr ? (UINT16)json_object_array_length(contextsArr) : 0; UINT16 kvpCount = kvpArr ? (UINT16)json_object_array_length(kvpArr) : 0; /* First compute size of contexts region */ UINT32 contextsSize = 0; for (UINT16 i = 0; i < ctxCount; i++) { json_object *ctx = json_object_array_get_idx(contextsArr, i); if (!ctx) { continue; } json_object *regsArr = NULL; json_object_object_get_ex(ctx, "registers", ®sArr); UINT16 regCount = regsArr ? (UINT16)json_object_array_length(regsArr) : 0; UINT32 length = (UINT32)sizeof(EFI_ARM_RAS_AUX_CONTEXT_HEADER) + regCount * sizeof(EFI_ARM_RAS_AUX_MM_REG_ENTRY); contextsSize += length; } UINT32 headerSize = sizeof(EFI_ARM_RAS_AUX_DATA_HEADER); UINT32 kvpOffset = headerSize + contextsSize; /* from start of aux block */ UINT32 kvpSize = kvpCount * sizeof(EFI_ARM_RAS_AUX_KEY_VALUE_PAIR); UINT32 auxSize = headerSize + contextsSize + kvpSize; /* Per the spec, AuxiliaryDataSize can be up to 2^32, but that's extreme and unrealistic. * We limit auxSize to 0xFFFF to support reasonable sizes while keeping the code simple. */ if (auxSize > 0xFFFF) { cper_print_log( "Implementation doesn't support large AuxiliaryDataSize: %u > 0xFFFF", auxSize); return; } UINT8 *buf = (UINT8 *)calloc(1, auxSize); if (!buf) { return; } EFI_ARM_RAS_AUX_DATA_HEADER *hdr = (EFI_ARM_RAS_AUX_DATA_HEADER *)buf; hdr->Version = 1; hdr->Reserved0 = 0; hdr->AddressSpaceArrayEntryCount = ctxCount; hdr->AuxiliaryDataSize = auxSize; hdr->KeyValuePairArrayOffset = kvpOffset; hdr->KeyValuePairArrayEntryCount = kvpCount; hdr->Reserved1 = 0; /* Write contexts */ arm_ras_build_aux_contexts(buf, ctxCount, contextsArr, headerSize); /* Write key-value pairs */ arm_ras_build_aux_kvps(buf, kvpCount, kvpArr, kvpOffset); *builtAux = buf; *builtAuxLen = auxSize; } static void arm_ras_write_descriptors(json_object *descArr, UINT32 descCount, FILE *out) { for (UINT32 i = 0; i < descCount; i++) { EFI_ARM_RAS_ERROR_RECORD_DESCRIPTOR d; memset(&d, 0, sizeof(d)); json_object *dj = json_object_array_get_idx(descArr, i); json_object *x = NULL; if (json_object_object_get_ex(dj, "errorRecordIndex", &x)) { d.ErrorRecordIndex = json_object_get_uint64(x); } /* Reconstruct rasExtensionRevision from split fields */ UINT8 rev = 0; UINT8 arch = 0; if (json_object_object_get_ex(dj, "rasExtensionRevisionField", &x)) { rev = (UINT8)json_object_get_uint64(x); } if (json_object_object_get_ex(dj, "rasExtensionArchVersion", &x)) { arch = (UINT8)json_object_get_uint64(x); } d.RasExtensionRevision = ((rev & 0x0F) << 4) | (arch & 0x0F); get_value_hex_64(dj, "errorRecordFeatureRegister", &d.ERR_FR); get_value_hex_64(dj, "errorRecordControlRegister", &d.ERR_CTLR); get_value_hex_64(dj, "errorRecordPrimaryStatusRegister", &d.ERR_STATUS); get_value_hex_64(dj, "errorRecordAddressRegister", &d.ERR_ADDR); get_value_hex_64(dj, "errorRecordMiscRegister0", &d.ERR_MISC0); get_value_hex_64(dj, "errorRecordMiscRegister1", &d.ERR_MISC1); get_value_hex_64(dj, "errorRecordMiscRegister2", &d.ERR_MISC2); get_value_hex_64(dj, "errorRecordMiscRegister3", &d.ERR_MISC3); fwrite(&d, sizeof(d), 1, out); } } void ir_section_arm_ras_to_cper(json_object *section, FILE *out) { EFI_ARM_RAS_NODE node; arm_ras_fill_node_fixed_fields(&node, section); arm_ras_fill_node_identifiers(&node, section); arm_ras_fill_node_user_data(&node, section); json_object *descArr = NULL; UINT32 descCount = 0; UINT32 afterDescriptors = 0; arm_ras_init_descriptor_metadata(&node, section, &descArr, &descCount, &afterDescriptors); json_object *auxStructured = NULL; json_object_object_get_ex(section, "auxData", &auxStructured); UINT8 *builtAux = NULL; UINT32 builtAuxLen = 0; arm_ras_build_aux_blob(auxStructured, &builtAux, &builtAuxLen); if (builtAux) { /* * Architecturally safe: from the RAS System Architecture (Arm IHI0100), * the header is 80 bytes and each ErrorSyndromes entry is 72 bytes, * with at most 896 entries. So the maximum AuxiliaryDataOffset is * 80 + 896 * 72 < 2^16, * and fits in the UINT16 field. */ node.AuxiliaryDataOffset = (UINT16)afterDescriptors; } else { node.AuxiliaryDataOffset = 0; } node.ErrorSyndromeArrayNumEntries = descCount; // N fwrite(&node, sizeof(node), 1, out); arm_ras_write_descriptors(descArr, descCount, out); if (builtAux) { fwrite(builtAux, builtAuxLen, 1, out); free(builtAux); } fflush(out); }