xref: /openbmc/libcper/generator/sections/gen-section-arm-ras.c (revision e1cba52dd4aeaeca17ec229251a8afc7be891d00)
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