1 /**
2 * Pseudo-random generator for ARM RAS CPER System Architecture Node (Revision 1).
3 * Generates header (Table 20), error_syndrome_array entries (Table 21 descriptors),
4 * and a structured auxiliary data block (Tables 22-26).
5 * https://developer.arm.com/documentation/den0085/latest
6 */
7
8 #include <stdlib.h>
9 #include <string.h>
10 #include <libcper/Cper.h>
11 #include <libcper/BaseTypes.h>
12 #include <libcper/generator/gen-utils.h>
13 #include <libcper/generator/sections/gen-section.h>
14 #include <stdbool.h>
15
16 /* Legacy component type retained as standalone field only */
17 #define ARM_RAS_COMPONENT_TYPE_MAX 0x6
18
19 /* IP_type_format enumeration per Table 20 */
20 enum {
21 PE = 0,
22 SMMU_IIDR,
23 GIC_IIDR,
24 PIDR,
25 ARM_RAS_IP_TYPE_FORMAT_INVALID = 255
26 };
27
28 /* Auxiliary context generation metadata */
29 typedef struct {
30 UINT16 regCount;
31 UINT8 flags;
32 UINT16 asid;
33 } GEN_CTX_META;
34
gen_arm_ras_init_ctx_meta(GEN_CTX_META * ctxMeta,UINT16 ctxCount)35 static bool gen_arm_ras_init_ctx_meta(GEN_CTX_META *ctxMeta, UINT16 ctxCount)
36 {
37 for (UINT16 ci = 0; ci < ctxCount; ci++) {
38 ctxMeta[ci].regCount =
39 (UINT16)((cper_rand() % 4) + 1); /* 1..4 registers */
40 ctxMeta[ci].flags = (UINT8)(cper_rand() & 0x1);
41 ctxMeta[ci].asid = (ctxMeta[ci].flags & 0x1) ?
42 (UINT16)(cper_rand() & 0xFFFF) :
43 0;
44 }
45 return true;
46 }
47
gen_arm_ras_contexts_region_size(const GEN_CTX_META * ctxMeta,UINT16 ctxCount)48 static UINT32 gen_arm_ras_contexts_region_size(const GEN_CTX_META *ctxMeta,
49 UINT16 ctxCount)
50 {
51 UINT32 contextsRegion = 0;
52 for (UINT16 ci = 0; ci < ctxCount; ci++) {
53 contextsRegion +=
54 (UINT32)sizeof(EFI_ARM_RAS_AUX_CONTEXT_HEADER) +
55 ctxMeta[ci].regCount *
56 sizeof(EFI_ARM_RAS_AUX_MM_REG_ENTRY);
57 }
58 return contextsRegion;
59 }
60
gen_arm_ras_fill_node_fields(EFI_ARM_RAS_NODE * node,UINT8 legacyType,const UINT8 ipTypeFormatChoices[5])61 static bool gen_arm_ras_fill_node_fields(EFI_ARM_RAS_NODE *node,
62 UINT8 legacyType,
63 const UINT8 ipTypeFormatChoices[5])
64 {
65 node->Revision = 1;
66 /* Randomly select IPTypeFormat sometimes 255 (invalid/ignored => zero-filled IPType) */
67 // intentionally avoid soc specific identifier because it's not implemented
68 node->IPInstanceFormat = (UINT8)(cper_rand() % 3);
69 node->IPTypeFormat = ipTypeFormatChoices[cper_rand() % 5];
70 node->ComponentType = legacyType;
71 UINT8 *tmp = NULL;
72
73 switch (node->IPInstanceFormat) {
74 case 0:
75 node->IPInstance.pe.MPIDR_EL1 = cper_rand();
76 break;
77 case 1:
78 node->IPInstance.systemPhysicalAddress.SystemPhysicalAddress =
79 cper_rand();
80 break;
81 case 2:
82 node->IPInstance.localAddressIdentifier
83 .SocSpecificLocalAddressSpace = cper_rand();
84 node->IPInstance.localAddressIdentifier.BaseAddress =
85 cper_rand();
86 break;
87 case 3:
88 tmp = generate_random_bytes(16);
89 if (!tmp) {
90 return false;
91 }
92 memcpy(node->IPInstance.socSpecificIpIdentifier
93 .SocSpecificIPIdentifier,
94 tmp, 16);
95 free(tmp);
96 break;
97 }
98
99 switch (node->IPTypeFormat) {
100 case 0:
101 node->IPType.smmuIidr.MIDR_EL1 = cper_rand();
102 node->IPType.smmuIidr.REVIDR_EL1 = cper_rand();
103 node->IPType.smmuIidr.AIDR_EL1 = cper_rand();
104 break;
105 case 1:
106 node->IPType.gicIidr.IIDR = cper_rand();
107 node->IPType.gicIidr.AIDR = cper_rand();
108 node->IPType.gicIidr.Reserved1 = 0;
109 node->IPType.gicIidr.Reserved2 = 0;
110 break;
111 case 2:
112 node->IPType.pidr.PIDR3 = cper_rand();
113 node->IPType.pidr.PIDR2 = cper_rand();
114 node->IPType.pidr.PIDR1 = cper_rand();
115 node->IPType.pidr.PIDR0 = cper_rand();
116 node->IPType.pidr.PIDR7 = cper_rand();
117 node->IPType.pidr.PIDR6 = cper_rand();
118 node->IPType.pidr.PIDR5 = cper_rand();
119 node->IPType.pidr.PIDR4 = cper_rand();
120 node->IPType.pidr.Reserved1 = 0;
121 node->IPType.pidr.Reserved2 = 0;
122 break;
123 }
124
125 generate_random_printable_string(node->UserData,
126 sizeof(node->UserData));
127 return true;
128 }
129
gen_arm_ras_fill_descriptors(UINT8 * buf,UINT16 offset,UINT32 descriptorCount)130 static void gen_arm_ras_fill_descriptors(UINT8 *buf, UINT16 offset,
131 UINT32 descriptorCount)
132 {
133 UINT8 *cur = buf + offset;
134 for (UINT32 i = 0; i < descriptorCount; i++) {
135 EFI_ARM_RAS_ERROR_RECORD_DESCRIPTOR *d =
136 (EFI_ARM_RAS_ERROR_RECORD_DESCRIPTOR *)cur;
137 memset(d, 0, sizeof(*d));
138 d->ErrorRecordIndex = i;
139 /* 25% chance of zero RasExtensionRevision (no MISC2/3) */
140 if ((cper_rand() % 4) == 0) {
141 d->RasExtensionRevision = 0;
142 } else {
143 UINT8 revHigh = (UINT8)(cper_rand() % 0x10);
144 UINT8 revLow = (UINT8)(cper_rand() % 0x10);
145 d->RasExtensionRevision = (revHigh << 4) | revLow;
146 }
147 d->ERR_FR = (UINT64)cper_rand();
148 d->ERR_CTLR = (UINT64)cper_rand();
149 d->ERR_STATUS = (UINT64)cper_rand();
150 d->ERR_ADDR = (UINT64)cper_rand();
151 d->ERR_MISC0 = (UINT64)cper_rand();
152 d->ERR_MISC1 = (UINT64)cper_rand();
153 if (d->RasExtensionRevision) {
154 d->ERR_MISC2 = (UINT64)cper_rand();
155 d->ERR_MISC3 = (UINT64)cper_rand();
156 }
157 cur += sizeof(EFI_ARM_RAS_ERROR_RECORD_DESCRIPTOR);
158 }
159 }
160
gen_arm_ras_fill_aux_data(UINT8 * buf,UINT16 auxOffset,UINT32 chosenAuxLen,UINT16 ctxCount,UINT16 kvCount,UINT32 contextsRegion,const GEN_CTX_META * ctxMeta)161 static bool gen_arm_ras_fill_aux_data(UINT8 *buf, UINT16 auxOffset,
162 UINT32 chosenAuxLen, UINT16 ctxCount,
163 UINT16 kvCount, UINT32 contextsRegion,
164 const GEN_CTX_META *ctxMeta)
165 {
166 UINT8 *auxBase = buf + auxOffset;
167 EFI_ARM_RAS_AUX_DATA_HEADER *hdr =
168 (EFI_ARM_RAS_AUX_DATA_HEADER *)auxBase;
169 hdr->Version = 1;
170 hdr->AddressSpaceArrayEntryCount = ctxCount;
171 hdr->AuxiliaryDataSize = chosenAuxLen;
172 UINT32 kvOffset =
173 (UINT32)sizeof(EFI_ARM_RAS_AUX_DATA_HEADER) + contextsRegion;
174 if (kvCount == 0) {
175 kvOffset =
176 hdr->AuxiliaryDataSize; /* zero-pair case per allowance */
177 }
178 hdr->KeyValuePairArrayOffset = kvOffset;
179 hdr->KeyValuePairArrayEntryCount = kvCount;
180
181 UINT8 *cursor = auxBase + sizeof(EFI_ARM_RAS_AUX_DATA_HEADER);
182 /* Contexts */
183 for (UINT16 ci = 0; ci < ctxCount; ci++) {
184 UINT16 regCount = ctxMeta[ci].regCount;
185 UINT32 ctxLen = (UINT32)sizeof(EFI_ARM_RAS_AUX_CONTEXT_HEADER) +
186 regCount * sizeof(EFI_ARM_RAS_AUX_MM_REG_ENTRY);
187 EFI_ARM_RAS_AUX_CONTEXT_HEADER *ch =
188 (EFI_ARM_RAS_AUX_CONTEXT_HEADER *)cursor;
189 ch->Length = ctxLen;
190 ch->AddressSpaceIdentifierScope = ctxMeta[ci].flags;
191
192 ch->RegisterArrayEntryCount = regCount;
193 ch->AddressSpaceIdentifier = ctxMeta[ci].asid;
194
195 EFI_ARM_RAS_AUX_MM_REG_ENTRY *regs =
196 (EFI_ARM_RAS_AUX_MM_REG_ENTRY
197 *)(cursor +
198 sizeof(EFI_ARM_RAS_AUX_CONTEXT_HEADER));
199 for (UINT16 r = 0; r < regCount; r++) {
200 regs[r].RegisterAddress = (UINT64)cper_rand();
201 regs[r].RegisterValue = (UINT64)cper_rand();
202 }
203 cursor += ctxLen;
204 }
205
206 /* Key-value pairs */
207 if (kvCount) {
208 EFI_ARM_RAS_AUX_KEY_VALUE_PAIR *kvOut =
209 (EFI_ARM_RAS_AUX_KEY_VALUE_PAIR
210 *)(auxBase + hdr->KeyValuePairArrayOffset);
211 for (UINT16 ki = 0; ki < kvCount; ki++) {
212 if ((cper_rand() % 4) == 0) {
213 kvOut[ki].Key =
214 EFI_ARM_RAS_KVP_UUID_MPAM_PARTID;
215 kvOut[ki].Value = (UINT64)cper_rand() & 0xFFFF;
216 } else {
217 UINT8 *rb = generate_random_bytes(16);
218 kvOut[ki].Key = *((EFI_GUID *)rb);
219 kvOut[ki].Value = (UINT64)cper_rand();
220 free(rb);
221 }
222 }
223 }
224
225 return true;
226 }
227
generate_section_arm_ras(void ** location,GEN_VALID_BITS_TEST_TYPE validBitsType)228 size_t generate_section_arm_ras(void **location,
229 GEN_VALID_BITS_TEST_TYPE validBitsType)
230 {
231 (void)validBitsType;
232
233 /* Choose descriptor count and legacy component type (standalone only) */
234 UINT32 descriptorCount = (cper_rand() % 4) + 1; // 1..4 descriptors
235 UINT8 legacyType =
236 (UINT8)(cper_rand() % (ARM_RAS_COMPONENT_TYPE_MAX + 1));
237
238 /* Always generate structured auxiliary data per Tables 22-26 */
239 UINT16 ctxCount = (UINT16)((cper_rand() % 3) + 1); /* 1..3 contexts */
240 UINT16 kvCount = (UINT16)(cper_rand() % 4); /* 0..3 key-value pairs */
241 /* Use enum values: 0..3 are valid formats, 255 is invalid */
242 const UINT8 ipTypeFormatChoices[5] = { PE, SMMU_IIDR, GIC_IIDR, PIDR,
243 ARM_RAS_IP_TYPE_FORMAT_INVALID };
244 /* Pre-determine each context's register count & metadata to keep sizes consistent */
245 GEN_CTX_META *ctxMeta =
246 (GEN_CTX_META *)calloc(ctxCount, sizeof(GEN_CTX_META));
247 if (!ctxMeta) {
248 return 0;
249 }
250 gen_arm_ras_init_ctx_meta(ctxMeta, ctxCount);
251
252 UINT32 contextsRegion =
253 gen_arm_ras_contexts_region_size(ctxMeta, ctxCount);
254 UINT32 kvRegion = kvCount * sizeof(EFI_ARM_RAS_AUX_KEY_VALUE_PAIR);
255 UINT32 chosenAuxLen = (UINT32)sizeof(EFI_ARM_RAS_AUX_DATA_HEADER) +
256 contextsRegion + kvRegion;
257
258 size_t total =
259 sizeof(EFI_ARM_RAS_NODE) +
260 descriptorCount * sizeof(EFI_ARM_RAS_ERROR_RECORD_DESCRIPTOR) +
261 chosenAuxLen;
262
263 UINT8 *buf = (UINT8 *)calloc(1, total);
264 if (!buf) {
265 free(ctxMeta);
266 return 0;
267 }
268 EFI_ARM_RAS_NODE *node = (EFI_ARM_RAS_NODE *)buf;
269 if (!gen_arm_ras_fill_node_fields(node, legacyType,
270 ipTypeFormatChoices)) {
271 free(ctxMeta);
272 free(buf);
273 return 0;
274 }
275
276 node->ErrorSyndromeArrayOffset = (UINT16)sizeof(EFI_ARM_RAS_NODE);
277 node->ErrorSyndromeArrayNumEntries = descriptorCount;
278 UINT32 afterDescriptors =
279 (UINT32)sizeof(EFI_ARM_RAS_NODE) +
280 descriptorCount *
281 (UINT32)sizeof(EFI_ARM_RAS_ERROR_RECORD_DESCRIPTOR);
282 node->AuxiliaryDataOffset = chosenAuxLen ? (UINT16)afterDescriptors : 0;
283
284 /* Populate descriptors */
285 gen_arm_ras_fill_descriptors(buf, node->ErrorSyndromeArrayOffset,
286 descriptorCount);
287
288 /* Auxiliary data generation */
289 if (chosenAuxLen && node->AuxiliaryDataOffset) {
290 if (!gen_arm_ras_fill_aux_data(buf, node->AuxiliaryDataOffset,
291 chosenAuxLen, ctxCount, kvCount,
292 contextsRegion, ctxMeta)) {
293 free(ctxMeta);
294 free(buf);
295 return 0;
296 }
297 }
298
299 free(ctxMeta);
300
301 *location = buf;
302 return total;
303 }
304