xref: /aosp_15_r20/external/coreboot/src/arch/x86/acpi_bert_storage.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <bootstate.h>
4 #include <cbmem.h>
5 #include <console/console.h>
6 #include <cpu/x86/name.h>
7 #include <cpu/x86/msr.h>
8 #include <cpu/x86/lapic.h>
9 #include <acpi/acpi.h>
10 #include <arch/bert_storage.h>
11 #include <string.h>
12 #include <types.h>
13 
14 /* BERT region management:  Allow the chipset to determine the specific
15  * location of the BERT region.  We find that base and size, then manage
16  * the allocation of error information within it.
17  *
18  * Use simple static variables for managing the BERT region.  This is a thin
19  * implementation; it is only created and consumed by coreboot, and only in
20  * a single stage, and we don't want its information to survive reboot or
21  * resume cycles.  If the requirements change, consider using IMD to help
22  * manage the space.
23  */
24 static bool bert_region_broken;
25 static void *bert_region_base;
26 static size_t bert_region_size;
27 static size_t bert_region_used;
28 
29 /* Calculate the remaining space in the BERT region.  This knowledge may help
30  * the caller prioritize the information to store.
31  */
bert_storage_remaining(void)32 size_t bert_storage_remaining(void)
33 {
34 	return bert_region_broken ? 0 : bert_region_size - bert_region_used;
35 }
36 
bert_errors_present(void)37 bool bert_errors_present(void)
38 {
39 	return !bert_region_broken && bert_region_used;
40 }
41 
bert_errors_region(void ** start,size_t * size)42 void bert_errors_region(void **start, size_t *size)
43 {
44 	if (bert_region_broken) {
45 		*start = NULL;
46 		*size = 0;
47 		return;
48 	}
49 
50 	/* No metadata, etc. with our region, so this is easy */
51 	*start = bert_region_base;
52 	*size = bert_region_used;
53 }
54 
bert_allocate_storage(size_t size)55 static void *bert_allocate_storage(size_t size)
56 {
57 	size_t alloc;
58 
59 	if (bert_region_broken)
60 		return NULL;
61 	if (bert_region_used + size > bert_region_size)
62 		return NULL;
63 
64 	alloc = bert_region_used;
65 	bert_region_used += size;
66 
67 	return (void *)((u8 *)bert_region_base + alloc);
68 }
69 
70 /* Generic Error Status:  Each Status represents a unique error event within
71  * the BERT errors region.  Each event may have multiple errors associated
72  * with it.
73  */
74 
75 /* Find the nth (1-based) Generic Data Structure attached to an Error Status */
acpi_hest_generic_data_nth(acpi_generic_error_status_t * status,int num)76 static void *acpi_hest_generic_data_nth(
77 		acpi_generic_error_status_t *status, int num)
78 {
79 	acpi_hest_generic_data_v300_t *ptr;
80 	size_t struct_size;
81 
82 	if (!num || num > bert_entry_count(status))
83 		return NULL;
84 
85 	ptr = (acpi_hest_generic_data_v300_t *)(status + 1);
86 	while (--num) {
87 		if (ptr->revision == HEST_GENERIC_ENTRY_V300)
88 			struct_size = sizeof(acpi_hest_generic_data_v300_t);
89 		else
90 			struct_size = sizeof(acpi_hest_generic_data_t);
91 		ptr = (acpi_hest_generic_data_v300_t *)(
92 				(u8 *)ptr
93 				+ ptr->data_length
94 				+ struct_size);
95 	}
96 	return ptr;
97 }
98 
99 /* Update data_length for this Error Status, and final Data Entry it contains */
revise_error_sizes(acpi_generic_error_status_t * status,size_t size)100 static void revise_error_sizes(acpi_generic_error_status_t *status, size_t size)
101 {
102 	acpi_hest_generic_data_v300_t *entry;
103 	int entries;
104 
105 	if (!status)
106 		return;
107 
108 	entries = bert_entry_count(status);
109 	entry = acpi_hest_generic_data_nth(status, entries);
110 	status->data_length += size;
111 	if (entry)
112 		entry->data_length += size;
113 }
114 
115 /* Create space for a new BERT Generic Error Status Block, by finding the next
116  * available slot and moving the ending location.  There is nothing to designate
117  * this as another Generic Error Status Block (e.g. no signature); only that it
118  * is within the BERT region.
119  *
120  * It is up to the caller to correctly fill the information, including status
121  * and error severity, and to update/maintain data offsets and lengths as
122  * entries are added.
123  */
new_bert_status(void)124 static acpi_generic_error_status_t *new_bert_status(void)
125 {
126 	acpi_generic_error_status_t *status;
127 
128 	status = bert_allocate_storage(sizeof(*status));
129 
130 	if (!status) {
131 		printk(BIOS_ERR, "New BERT error entry would exceed available region\n");
132 		return NULL;
133 	}
134 
135 	status->error_severity = ACPI_GENERROR_SEV_NONE;
136 	return status;
137 }
138 
139 /* Generic Error Data:  Each Generic Error Status may contain zero or more
140  * Generic Error Data structures.  The data structures describe particular
141  * error(s) associated with an event.  The definition for the structure is
142  * found in the ACPI spec, however the data types and any accompanying data
143  * definitions are in the Common Platform Error Record appendix of the UEFI
144  * spec.
145  */
146 
147 /* Create space for a new BERT Generic Data Entry.  Update the count and
148  * data length in the parent Generic Error Status Block.  Version 0x300 of
149  * the structure is used, and the timestamp is filled and marked precise
150  * (i.e. assumed close enough for reporting).
151  *
152  * It is up to the caller to fill the Section Type field and add the Common
153  * Platform Error Record type data as appropriate.  In addition, the caller
154  * should update the error severity, and may optionally add FRU information
155  * or override any existing information.
156  */
new_generic_error_entry(acpi_generic_error_status_t * status)157 static acpi_hest_generic_data_v300_t *new_generic_error_entry(
158 		acpi_generic_error_status_t *status)
159 {
160 	acpi_hest_generic_data_v300_t *entry;
161 
162 	if (bert_entry_count(status) == GENERIC_ERR_STS_ENTRY_COUNT_MAX) {
163 		printk(BIOS_ERR, "New BERT error would exceed maximum entries\n");
164 		return NULL;
165 	}
166 
167 	entry = bert_allocate_storage(sizeof(*entry));
168 	if (!entry) {
169 		printk(BIOS_ERR, "New BERT error entry would exceed available region\n");
170 		return NULL;
171 	}
172 
173 	entry->revision = HEST_GENERIC_ENTRY_V300;
174 
175 	entry->timestamp = cper_timestamp(CPER_TIMESTAMP_PRECISE);
176 	entry->validation_bits |= ACPI_GENERROR_VALID_TIMESTAMP;
177 
178 	status->data_length += sizeof(*entry);
179 	bert_bump_entry_count(status);
180 
181 	return entry;
182 }
183 
184 /* Find the size of a CPER error section w/o any add-ons */
sizeof_error_section(guid_t * guid)185 static size_t sizeof_error_section(guid_t *guid)
186 {
187 	if (!guidcmp(guid, &CPER_SEC_PROC_GENERIC_GUID))
188 		return sizeof(cper_proc_generic_error_section_t);
189 	else if (!guidcmp(guid, &CPER_SEC_PROC_IA32X64_GUID))
190 		return sizeof(cper_ia32x64_proc_error_section_t);
191 	else if (!guidcmp(guid, &CPER_SEC_FW_ERR_REC_REF_GUID))
192 		return sizeof(cper_fw_err_rec_section_t);
193 	/* else if ... sizeof(structures not yet defined) */
194 
195 	printk(BIOS_ERR, "Requested size of unrecognized CPER GUID\n");
196 	return 0;
197 }
198 
new_cper_fw_error_crashlog(acpi_generic_error_status_t * status,size_t cl_size)199 void *new_cper_fw_error_crashlog(acpi_generic_error_status_t *status, size_t cl_size)
200 {
201 	void *cl_data = bert_allocate_storage(cl_size);
202 	if (!cl_data) {
203 		printk(BIOS_ERR, "Crashlog entry (size %zu) would exceed available region\n",
204 			cl_size);
205 		return NULL;
206 	}
207 
208 	revise_error_sizes(status, cl_size);
209 
210 	return cl_data;
211 }
212 
213 /* Helper to append an ACPI Generic Error Data Entry per crashlog data */
bert_append_fw_err(acpi_generic_error_status_t * status)214 acpi_hest_generic_data_v300_t *bert_append_fw_err(acpi_generic_error_status_t *status)
215 {
216 	acpi_hest_generic_data_v300_t *entry;
217 	cper_fw_err_rec_section_t *fw_err;
218 
219 	entry = bert_append_error_datasection(status, &CPER_SEC_FW_ERR_REC_REF_GUID);
220 	if (!entry)
221 		return NULL;
222 
223 	status->block_status |= GENERIC_ERR_STS_UNCORRECTABLE_VALID;
224 	status->error_severity = ACPI_GENERROR_SEV_FATAL;
225 	entry->error_severity = ACPI_GENERROR_SEV_FATAL;
226 
227 	fw_err = section_of_acpientry(fw_err, entry);
228 
229 	fw_err->record_type = CRASHLOG_RECORD_TYPE;
230 	fw_err->revision = CRASHLOG_FW_ERR_REV;
231 	fw_err->record_id = 0;
232 	guidcpy(&fw_err->record_guid, &FW_ERR_RECORD_ID_CRASHLOG_GUID);
233 
234 	return entry;
235 }
236 
237 /* Append a new ACPI Generic Error Data Entry plus CPER Error Section to an
238  * existing ACPI Generic Error Status Block.  The caller is responsible for
239  * the setting the status and entry severity, as well as populating all fields
240  * of the error section.
241  */
bert_append_error_datasection(acpi_generic_error_status_t * status,guid_t * guid)242 acpi_hest_generic_data_v300_t *bert_append_error_datasection(
243 		acpi_generic_error_status_t *status, guid_t *guid)
244 {
245 	acpi_hest_generic_data_v300_t *entry;
246 	void *sect;
247 	size_t sect_size;
248 
249 	sect_size = sizeof_error_section(guid);
250 	if (!sect_size)
251 		return NULL; /* Don't allocate structure if bad GUID passed */
252 
253 	if (sizeof(*entry) + sect_size > bert_storage_remaining())
254 		return NULL;
255 
256 	entry = new_generic_error_entry(status);
257 	if (!entry)
258 		return NULL;
259 
260 	/* error section immediately follows the Generic Error Data Entry */
261 	sect = bert_allocate_storage(sect_size);
262 	if (!sect)
263 		return NULL;
264 
265 	revise_error_sizes(status, sect_size);
266 
267 	guidcpy(&entry->section_type, guid);
268 	return entry;
269 }
270 
271 /* Helper to append an ACPI Generic Error Data Entry plus a CPER Processor
272  * Generic Error Section.  As many fields are populated as possible for the
273  * caller.
274  */
bert_append_genproc(acpi_generic_error_status_t * status)275 acpi_hest_generic_data_v300_t *bert_append_genproc(
276 		acpi_generic_error_status_t *status)
277 {
278 	acpi_hest_generic_data_v300_t *entry;
279 	cper_proc_generic_error_section_t *ges;
280 
281 	entry = bert_append_error_datasection(status,
282 					&CPER_SEC_PROC_GENERIC_GUID);
283 	if (!entry)
284 		return NULL;
285 
286 	status->block_status |= GENERIC_ERR_STS_UNCORRECTABLE_VALID;
287 	status->error_severity = ACPI_GENERROR_SEV_FATAL;
288 
289 	entry->error_severity = ACPI_GENERROR_SEV_FATAL;
290 
291 	ges = section_of_acpientry(ges, entry);
292 
293 	ges->proc_type = GENPROC_PROCTYPE_IA32X64;
294 	ges->validation |= GENPROC_VALID_PROC_TYPE;
295 
296 	ges->cpu_version = cpuid_eax(1);
297 	ges->validation |= GENPROC_VALID_CPU_VERSION;
298 
299 	fill_processor_name(ges->cpu_brand_string);
300 	ges->validation |= GENPROC_VALID_CPU_BRAND;
301 
302 	ges->proc_id = lapicid();
303 	ges->validation |= GENPROC_VALID_CPU_ID;
304 
305 	return entry;
306 }
307 
308 /* Add a new IA32/X64 Processor Context Structure (Table 261), following any
309  * other contexts, to an existing Processor Error Section (Table 255).  Contexts
310  * may only be added after the entire Processor Error Info array has been
311  * created.
312  *
313  * This function fills only the minimal amount of information required to parse
314  * or step through the contexts.  The type is filled and PROC_CONTEXT_INFO_NUM
315  * is updated.
316  *
317  * type is one of:
318  *   CPER_IA32X64_CTX_UNCL
319  *   CPER_IA32X64_CTX_MSR
320  *   CPER_IA32X64_CTX_32BIT_EX
321  *   CPER_IA32X64_CTX_64BIT_EX
322  *   CPER_IA32X64_CTX_FXSAVE
323  *   CPER_IA32X64_CTX_32BIT_DBG
324  *   CPER_IA32X64_CTX_64BIT_DBG
325  *   CPER_IA32X64_CTX_MEMMAPPED
326  * num is the number of bytes eventually used to fill the context's register
327  *   array, e.g. 4 MSRs * sizeof(msr_t)
328  *
329  * status and entry data_length values are updated.
330  */
new_cper_ia32x64_ctx(acpi_generic_error_status_t * status,cper_ia32x64_proc_error_section_t * x86err,int type,int num)331 cper_ia32x64_context_t *new_cper_ia32x64_ctx(
332 		acpi_generic_error_status_t *status,
333 		cper_ia32x64_proc_error_section_t *x86err, int type, int num)
334 {
335 	size_t size;
336 	cper_ia32x64_context_t *ctx;
337 	static const char * const ctx_names[] = {
338 			"Unclassified Data",
339 			"MSR Registers",
340 			"32-bit Mode Execution",
341 			"64-bit Mode Execution",
342 			"FXSAVE",
343 			"32-bit Mode Debug",
344 			"64-bit Mode Debug",
345 			"Memory Mapped"
346 	};
347 
348 	if (type > CPER_IA32X64_CTX_MEMMAPPED)
349 		return NULL;
350 
351 	if (cper_ia32x64_proc_num_ctxs(x86err) == I32X64SEC_VALID_CTXNUM_MAX) {
352 		printk(BIOS_ERR, "New IA32X64 %s context entry would exceed max allowable contexts\n",
353 				ctx_names[type]);
354 		return NULL;
355 	}
356 
357 	size = cper_ia32x64_ctx_sz_bytype(type, num);
358 	ctx = bert_allocate_storage(size);
359 	if (!ctx) {
360 		printk(BIOS_ERR, "New IA32X64 %s context entry would exceed available region\n",
361 				ctx_names[type]);
362 		return NULL;
363 	}
364 
365 	revise_error_sizes(status, size);
366 
367 	ctx->type = type;
368 	ctx->array_size = num;
369 	cper_bump_ia32x64_ctx_count(x86err);
370 
371 	return ctx;
372 }
373 
374 /* Add a new IA32/X64 Processor Error Information Structure (Table 256),
375  * following any other errors, to an existing Processor Error Section
376  * (Table 255).  All error structures must be added before any contexts are
377  * added.
378  *
379  * This function fills only the minimal amount of information required to parse
380  * or step through the errors.  The type is filled and PROC_ERR_INFO_NUM is
381  * updated.
382  */
new_cper_ia32x64_check(acpi_generic_error_status_t * status,cper_ia32x64_proc_error_section_t * x86err,enum cper_x86_check_type type)383 cper_ia32x64_proc_error_info_t *new_cper_ia32x64_check(
384 		acpi_generic_error_status_t *status,
385 		cper_ia32x64_proc_error_section_t *x86err,
386 		enum cper_x86_check_type type)
387 {
388 	cper_ia32x64_proc_error_info_t *check;
389 	static const char * const check_names[] = {
390 			"cache",
391 			"TLB",
392 			"bus",
393 			"MS"
394 	};
395 	const guid_t check_guids[] = {
396 			X86_PROCESSOR_CACHE_CHK_ERROR_GUID,
397 			X86_PROCESSOR_TLB_CHK_ERROR_GUID,
398 			X86_PROCESSOR_BUS_CHK_ERROR_GUID,
399 			X86_PROCESSOR_MS_CHK_ERROR_GUID
400 	};
401 
402 	if (type > X86_PROCESSOR_CHK_MAX)
403 		return NULL;
404 
405 	if (cper_ia32x64_proc_num_chks(x86err) == I32X64SEC_VALID_ERRNUM_MAX) {
406 		printk(BIOS_ERR, "New IA32X64 %s check entry would exceed max allowable errors\n",
407 				check_names[type]);
408 		return NULL;
409 	}
410 
411 	check = bert_allocate_storage(sizeof(*check));
412 	if (!check) {
413 		printk(BIOS_ERR, "New IA32X64 %s check entry would exceed available region\n",
414 				check_names[type]);
415 		return NULL;
416 	}
417 
418 	revise_error_sizes(status, sizeof(*check));
419 
420 	guidcpy(&check->type, &check_guids[type]);
421 	cper_bump_ia32x64_chk_count(x86err);
422 
423 	return check;
424 }
425 
426 /* Helper to append an ACPI Generic Error Data Entry plus a CPER IA32/X64
427  * Processor Error Section.  As many fields are populated as possible for the
428  * caller.
429  */
bert_append_ia32x64(acpi_generic_error_status_t * status)430 acpi_hest_generic_data_v300_t *bert_append_ia32x64(
431 					acpi_generic_error_status_t *status)
432 {
433 	acpi_hest_generic_data_v300_t *entry;
434 	cper_ia32x64_proc_error_section_t *ipe;
435 	struct cpuid_result id;
436 
437 	entry = bert_append_error_datasection(status,
438 					&CPER_SEC_PROC_IA32X64_GUID);
439 	if (!entry)
440 		return NULL;
441 
442 	status->block_status |= GENERIC_ERR_STS_UNCORRECTABLE_VALID;
443 	status->error_severity = ACPI_GENERROR_SEV_FATAL;
444 
445 	entry->error_severity = ACPI_GENERROR_SEV_FATAL;
446 
447 	ipe = section_of_acpientry(ipe, entry);
448 
449 	ipe->apicid = lapicid();
450 	ipe->validation |= I32X64SEC_VALID_LAPIC;
451 
452 	id = cpuid(1);
453 	ipe->cpuid[0] = id.eax;
454 	ipe->cpuid[1] = id.ebx;
455 	ipe->cpuid[2] = id.ecx;
456 	ipe->cpuid[3] = id.edx;
457 	ipe->validation |= I32X64SEC_VALID_CPUID;
458 
459 	return entry;
460 }
461 
462 static const char * const generic_error_types[] = {
463 	"PROCESSOR_GENERIC",
464 	"PROCESSOR_SPECIFIC_X86",
465 	"PROCESSOR_SPECIFIC_ARM",
466 	"PLATFORM_MEMORY",
467 	"PLATFORM_MEMORY2",
468 	"PCIE",
469 	"FW_ERROR_RECORD",
470 	"PCI_PCIX_BUS",
471 	"PCI_DEVICE",
472 	"DMAR_GENERIC",
473 	"DIRECTED_IO_DMAR",
474 	"IOMMU_DMAR",
475 	"UNRECOGNIZED"
476 };
477 
generic_error_name(guid_t * guid)478 static const char *generic_error_name(guid_t *guid)
479 {
480 	if (!guidcmp(guid, &CPER_SEC_PROC_GENERIC_GUID))
481 		return generic_error_types[0];
482 	if (!guidcmp(guid, &CPER_SEC_PROC_IA32X64_GUID))
483 		return generic_error_types[1];
484 	if (!guidcmp(guid, &CPER_SEC_PROC_ARM_GUID))
485 		return generic_error_types[2];
486 	if (!guidcmp(guid, &CPER_SEC_PLATFORM_MEM_GUID))
487 		return generic_error_types[3];
488 	if (!guidcmp(guid, &CPER_SEC_PLATFORM_MEM2_GUID))
489 		return generic_error_types[4];
490 	if (!guidcmp(guid, &CPER_SEC_PCIE_GUID))
491 		return generic_error_types[5];
492 	if (!guidcmp(guid, &CPER_SEC_FW_ERR_REC_REF_GUID))
493 		return generic_error_types[6];
494 	if (!guidcmp(guid, &CPER_SEC_PCI_X_BUS_GUID))
495 		return generic_error_types[7];
496 	if (!guidcmp(guid, &CPER_SEC_PCI_DEV_GUID))
497 		return generic_error_types[8];
498 	if (!guidcmp(guid, &CPER_SEC_DMAR_GENERIC_GUID))
499 		return generic_error_types[9];
500 	if (!guidcmp(guid, &CPER_SEC_DMAR_VT_GUID))
501 		return generic_error_types[10];
502 	if (!guidcmp(guid, &CPER_SEC_DMAR_IOMMU_GUID))
503 		return generic_error_types[11];
504 	return generic_error_types[12];
505 }
506 
507 /* Add a new event to the BERT region.  An event consists of an ACPI Error
508  * Status Block, a Generic Error Data Entry, and an associated CPER Error
509  * Section.
510  */
bert_new_event(guid_t * guid)511 acpi_generic_error_status_t *bert_new_event(guid_t *guid)
512 {
513 	size_t size;
514 	acpi_generic_error_status_t *status;
515 	acpi_hest_generic_data_v300_t *entry, *r;
516 
517 	size = sizeof(*status);
518 	size += sizeof(*entry);
519 	size += sizeof_error_section(guid);
520 
521 	if (size > bert_storage_remaining()) {
522 		printk(BIOS_ERR, "Not enough BERT region space to add event for type %s\n",
523 				generic_error_name(guid));
524 		return NULL;
525 	}
526 
527 	status = new_bert_status();
528 	if (!status)
529 		return NULL;
530 
531 	if (!guidcmp(guid, &CPER_SEC_PROC_GENERIC_GUID))
532 		r = bert_append_genproc(status);
533 	else if (!guidcmp(guid, &CPER_SEC_PROC_GENERIC_GUID))
534 		r = bert_append_ia32x64(status);
535 	else if (!guidcmp(guid, &CPER_SEC_FW_ERR_REC_REF_GUID))
536 		r = bert_append_fw_err(status);
537 	/* else if other types not implemented */
538 	else
539 		r = NULL;
540 
541 	if (r)
542 		return status;
543 	return NULL;
544 }
545 
546 /* Helper to add an MSR context to an existing IA32/X64-type error entry */
cper_new_ia32x64_context_msr(acpi_generic_error_status_t * status,cper_ia32x64_proc_error_section_t * x86err,u32 addr,int num)547 cper_ia32x64_context_t *cper_new_ia32x64_context_msr(
548 		acpi_generic_error_status_t *status,
549 		cper_ia32x64_proc_error_section_t *x86err, u32 addr, int num)
550 {
551 	cper_ia32x64_context_t *ctx;
552 	int i;
553 	msr_t *dest;
554 
555 	ctx = new_cper_ia32x64_ctx(status, x86err, CPER_IA32X64_CTX_MSR, num);
556 	if (!ctx)
557 		return NULL;
558 
559 	/* already filled ctx->type = CPER_IA32X64_CTX_MSR; */
560 	ctx->msr_addr = addr;
561 	ctx->array_size = num * sizeof(msr_t);
562 
563 	dest = (msr_t *)((u8 *)(ctx + 1)); /* point to the Register Array */
564 
565 	for (i = 0 ; i < num ; i++)
566 		*(dest + i) = rdmsr(addr + i);
567 	return ctx;
568 }
569 
bert_reserved_region(void ** start,size_t * size)570 static void bert_reserved_region(void **start, size_t *size)
571 {
572 	if (!CONFIG(ACPI_BERT)) {
573 		*start = NULL;
574 		*size = 0;
575 	} else {
576 		*start = cbmem_add(CBMEM_ID_ACPI_BERT, CONFIG_ACPI_BERT_SIZE);
577 		*size = CONFIG_ACPI_BERT_SIZE;
578 	}
579 	printk(BIOS_INFO, "Reserved BERT region base: %p, size: 0x%zx\n", *start, *size);
580 }
581 
bert_storage_setup(void * unused)582 static void bert_storage_setup(void *unused)
583 {
584 	/* Always start with a blank bert region.  Make sure nothing is
585 	 * maintained across reboots or resumes.
586 	 */
587 	bert_region_broken = false;
588 	bert_region_used = 0;
589 
590 	bert_reserved_region(&bert_region_base, &bert_region_size);
591 
592 	if (!bert_region_base || !bert_region_size) {
593 		printk(BIOS_ERR, "Bug: Can't find/add BERT storage area\n");
594 		bert_region_broken = true;
595 		return;
596 	}
597 
598 	memset(bert_region_base, 0, bert_region_size);
599 }
600 
601 BOOT_STATE_INIT_ENTRY(BS_PRE_DEVICE, BS_ON_EXIT, bert_storage_setup, NULL);
602