1 /* SPDX-License-Identifier: GPL-2.0-only */
5 #include <console/console.h>
6 #include <cpu/x86/name.h>
7 #include <cpu/x86/msr.h>
8 #include <cpu/x86/lapic.h>
10 #include <arch/bert_storage.h>
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.
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
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
;
29 /* Calculate the remaining space in the BERT region. This knowledge may help
30 * the caller prioritize the information to store.
32 size_t bert_storage_remaining(void)
34 return bert_region_broken
? 0 : bert_region_size
- bert_region_used
;
37 bool bert_errors_present(void)
39 return !bert_region_broken
&& bert_region_used
;
42 void bert_errors_region(void **start
, size_t *size
)
44 if (bert_region_broken
) {
50 /* No metadata, etc. with our region, so this is easy */
51 *start
= bert_region_base
;
52 *size
= bert_region_used
;
55 static void *bert_allocate_storage(size_t size
)
59 if (bert_region_broken
)
61 if (bert_region_used
+ size
> bert_region_size
)
64 alloc
= bert_region_used
;
65 bert_region_used
+= size
;
67 return (void *)((u8
*)bert_region_base
+ alloc
);
70 /* Generic Error Status: Each Status represents a unique error event within
71 * the BERT errors region. Each event may have multiple errors associated
75 /* Find the nth (1-based) Generic Data Structure attached to an Error Status */
76 static void *acpi_hest_generic_data_nth(
77 acpi_generic_error_status_t
*status
, int num
)
79 acpi_hest_generic_data_v300_t
*ptr
;
82 if (!num
|| num
> bert_entry_count(status
))
85 ptr
= (acpi_hest_generic_data_v300_t
*)(status
+ 1);
87 if (ptr
->revision
== HEST_GENERIC_ENTRY_V300
)
88 struct_size
= sizeof(acpi_hest_generic_data_v300_t
);
90 struct_size
= sizeof(acpi_hest_generic_data_t
);
91 ptr
= (acpi_hest_generic_data_v300_t
*)(
99 /* Update data_length for this Error Status, and final Data Entry it contains */
100 static void revise_error_sizes(acpi_generic_error_status_t
*status
, size_t size
)
102 acpi_hest_generic_data_v300_t
*entry
;
108 entries
= bert_entry_count(status
);
109 entry
= acpi_hest_generic_data_nth(status
, entries
);
110 status
->data_length
+= size
;
112 entry
->data_length
+= size
;
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.
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
124 static acpi_generic_error_status_t
*new_bert_status(void)
126 acpi_generic_error_status_t
*status
;
128 status
= bert_allocate_storage(sizeof(*status
));
131 printk(BIOS_ERR
, "New BERT error entry would exceed available region\n");
135 status
->error_severity
= ACPI_GENERROR_SEV_NONE
;
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
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).
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.
157 static acpi_hest_generic_data_v300_t
*new_generic_error_entry(
158 acpi_generic_error_status_t
*status
)
160 acpi_hest_generic_data_v300_t
*entry
;
162 if (bert_entry_count(status
) == GENERIC_ERR_STS_ENTRY_COUNT_MAX
) {
163 printk(BIOS_ERR
, "New BERT error would exceed maximum entries\n");
167 entry
= bert_allocate_storage(sizeof(*entry
));
169 printk(BIOS_ERR
, "New BERT error entry would exceed available region\n");
173 entry
->revision
= HEST_GENERIC_ENTRY_V300
;
175 entry
->timestamp
= cper_timestamp(CPER_TIMESTAMP_PRECISE
);
176 entry
->validation_bits
|= ACPI_GENERROR_VALID_TIMESTAMP
;
178 status
->data_length
+= sizeof(*entry
);
179 bert_bump_entry_count(status
);
184 /* Find the size of a CPER error section w/o any add-ons */
185 static size_t sizeof_error_section(guid_t
*guid
)
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) */
195 printk(BIOS_ERR
, "Requested size of unrecognized CPER GUID\n");
199 void *new_cper_fw_error_crashlog(acpi_generic_error_status_t
*status
, size_t cl_size
)
201 void *cl_data
= bert_allocate_storage(cl_size
);
203 printk(BIOS_ERR
, "Crashlog entry (size %zu) would exceed available region\n",
208 revise_error_sizes(status
, cl_size
);
213 /* Helper to append an ACPI Generic Error Data Entry per crashlog data */
214 acpi_hest_generic_data_v300_t
*bert_append_fw_err(acpi_generic_error_status_t
*status
)
216 acpi_hest_generic_data_v300_t
*entry
;
217 cper_fw_err_rec_section_t
*fw_err
;
219 entry
= bert_append_error_datasection(status
, &CPER_SEC_FW_ERR_REC_REF_GUID
);
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
;
227 fw_err
= section_of_acpientry(fw_err
, entry
);
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
);
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.
242 acpi_hest_generic_data_v300_t
*bert_append_error_datasection(
243 acpi_generic_error_status_t
*status
, guid_t
*guid
)
245 acpi_hest_generic_data_v300_t
*entry
;
249 sect_size
= sizeof_error_section(guid
);
251 return NULL
; /* Don't allocate structure if bad GUID passed */
253 if (sizeof(*entry
) + sect_size
> bert_storage_remaining())
256 entry
= new_generic_error_entry(status
);
260 /* error section immediately follows the Generic Error Data Entry */
261 sect
= bert_allocate_storage(sect_size
);
265 revise_error_sizes(status
, sect_size
);
267 guidcpy(&entry
->section_type
, guid
);
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
275 acpi_hest_generic_data_v300_t
*bert_append_genproc(
276 acpi_generic_error_status_t
*status
)
278 acpi_hest_generic_data_v300_t
*entry
;
279 cper_proc_generic_error_section_t
*ges
;
281 entry
= bert_append_error_datasection(status
,
282 &CPER_SEC_PROC_GENERIC_GUID
);
286 status
->block_status
|= GENERIC_ERR_STS_UNCORRECTABLE_VALID
;
287 status
->error_severity
= ACPI_GENERROR_SEV_FATAL
;
289 entry
->error_severity
= ACPI_GENERROR_SEV_FATAL
;
291 ges
= section_of_acpientry(ges
, entry
);
293 ges
->proc_type
= GENPROC_PROCTYPE_IA32X64
;
294 ges
->validation
|= GENPROC_VALID_PROC_TYPE
;
296 ges
->cpu_version
= cpuid_eax(1);
297 ges
->validation
|= GENPROC_VALID_CPU_VERSION
;
299 fill_processor_name(ges
->cpu_brand_string
);
300 ges
->validation
|= GENPROC_VALID_CPU_BRAND
;
302 ges
->proc_id
= lapicid();
303 ges
->validation
|= GENPROC_VALID_CPU_ID
;
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
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
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)
329 * status and entry data_length values are updated.
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
)
336 cper_ia32x64_context_t
*ctx
;
337 static const char * const ctx_names
[] = {
340 "32-bit Mode Execution",
341 "64-bit Mode Execution",
348 if (type
> CPER_IA32X64_CTX_MEMMAPPED
)
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",
357 size
= cper_ia32x64_ctx_sz_bytype(type
, num
);
358 ctx
= bert_allocate_storage(size
);
360 printk(BIOS_ERR
, "New IA32X64 %s context entry would exceed available region\n",
365 revise_error_sizes(status
, size
);
368 ctx
->array_size
= num
;
369 cper_bump_ia32x64_ctx_count(x86err
);
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
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
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
)
388 cper_ia32x64_proc_error_info_t
*check
;
389 static const char * const check_names
[] = {
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
402 if (type
> X86_PROCESSOR_CHK_MAX
)
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",
411 check
= bert_allocate_storage(sizeof(*check
));
413 printk(BIOS_ERR
, "New IA32X64 %s check entry would exceed available region\n",
418 revise_error_sizes(status
, sizeof(*check
));
420 guidcpy(&check
->type
, &check_guids
[type
]);
421 cper_bump_ia32x64_chk_count(x86err
);
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
430 acpi_hest_generic_data_v300_t
*bert_append_ia32x64(
431 acpi_generic_error_status_t
*status
)
433 acpi_hest_generic_data_v300_t
*entry
;
434 cper_ia32x64_proc_error_section_t
*ipe
;
435 struct cpuid_result id
;
437 entry
= bert_append_error_datasection(status
,
438 &CPER_SEC_PROC_IA32X64_GUID
);
442 status
->block_status
|= GENERIC_ERR_STS_UNCORRECTABLE_VALID
;
443 status
->error_severity
= ACPI_GENERROR_SEV_FATAL
;
445 entry
->error_severity
= ACPI_GENERROR_SEV_FATAL
;
447 ipe
= section_of_acpientry(ipe
, entry
);
449 ipe
->apicid
= lapicid();
450 ipe
->validation
|= I32X64SEC_VALID_LAPIC
;
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
;
462 static const char * const generic_error_types
[] = {
464 "PROCESSOR_SPECIFIC_X86",
465 "PROCESSOR_SPECIFIC_ARM",
478 static const char *generic_error_name(guid_t
*guid
)
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];
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
511 acpi_generic_error_status_t
*bert_new_event(guid_t
*guid
)
514 acpi_generic_error_status_t
*status
;
515 acpi_hest_generic_data_v300_t
*entry
, *r
;
517 size
= sizeof(*status
);
518 size
+= sizeof(*entry
);
519 size
+= sizeof_error_section(guid
);
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
));
527 status
= new_bert_status();
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 */
546 /* Helper to add an MSR context to an existing IA32/X64-type error entry */
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
)
551 cper_ia32x64_context_t
*ctx
;
555 ctx
= new_cper_ia32x64_ctx(status
, x86err
, CPER_IA32X64_CTX_MSR
, num
);
559 /* already filled ctx->type = CPER_IA32X64_CTX_MSR; */
560 ctx
->msr_addr
= addr
;
561 ctx
->array_size
= num
* sizeof(msr_t
);
563 dest
= (msr_t
*)((u8
*)(ctx
+ 1)); /* point to the Register Array */
565 for (i
= 0 ; i
< num
; i
++)
566 *(dest
+ i
) = rdmsr(addr
+ i
);
570 static void bert_reserved_region(void **start
, size_t *size
)
572 if (!CONFIG(ACPI_BERT
)) {
576 *start
= cbmem_add(CBMEM_ID_ACPI_BERT
, CONFIG_ACPI_BERT_SIZE
);
577 *size
= CONFIG_ACPI_BERT_SIZE
;
579 printk(BIOS_INFO
, "Reserved BERT region base: %p, size: 0x%zx\n", *start
, *size
);
582 static void bert_storage_setup(void *unused
)
584 /* Always start with a blank bert region. Make sure nothing is
585 * maintained across reboots or resumes.
587 bert_region_broken
= false;
588 bert_region_used
= 0;
590 bert_reserved_region(&bert_region_base
, &bert_region_size
);
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;
598 memset(bert_region_base
, 0, bert_region_size
);
601 BOOT_STATE_INIT_ENTRY(BS_PRE_DEVICE
, BS_ON_EXIT
, bert_storage_setup
, NULL
);