mb/google/fatcat: Suppress unnecessary extra space in device trees
[coreboot2.git] / src / arch / x86 / acpi_bert_storage.c
blob62c044137d343ebcdddfd24819a29a61d2e73629
1 /* SPDX-License-Identifier: GPL-2.0-only */
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>
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
22 * manage the space.
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) {
45 *start = NULL;
46 *size = 0;
47 return;
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)
57 size_t alloc;
59 if (bert_region_broken)
60 return NULL;
61 if (bert_region_used + size > bert_region_size)
62 return NULL;
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
72 * with it.
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;
80 size_t struct_size;
82 if (!num || num > bert_entry_count(status))
83 return NULL;
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);
96 return ptr;
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;
103 int entries;
105 if (!status)
106 return;
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;
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
122 * entries are added.
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));
130 if (!status) {
131 printk(BIOS_ERR, "New BERT error entry would exceed available region\n");
132 return NULL;
135 status->error_severity = ACPI_GENERROR_SEV_NONE;
136 return status;
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.
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");
164 return NULL;
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;
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);
181 return entry;
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");
196 return 0;
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);
202 if (!cl_data) {
203 printk(BIOS_ERR, "Crashlog entry (size %zu) would exceed available region\n",
204 cl_size);
205 return NULL;
208 revise_error_sizes(status, cl_size);
210 return cl_data;
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);
220 if (!entry)
221 return NULL;
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);
234 return entry;
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;
246 void *sect;
247 size_t sect_size;
249 sect_size = sizeof_error_section(guid);
250 if (!sect_size)
251 return NULL; /* Don't allocate structure if bad GUID passed */
253 if (sizeof(*entry) + sect_size > bert_storage_remaining())
254 return NULL;
256 entry = new_generic_error_entry(status);
257 if (!entry)
258 return NULL;
260 /* error section immediately follows the Generic Error Data Entry */
261 sect = bert_allocate_storage(sect_size);
262 if (!sect)
263 return NULL;
265 revise_error_sizes(status, sect_size);
267 guidcpy(&entry->section_type, guid);
268 return entry;
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.
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);
283 if (!entry)
284 return NULL;
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;
305 return entry;
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.
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.
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)
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)
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"
348 if (type > CPER_IA32X64_CTX_MEMMAPPED)
349 return NULL;
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;
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;
365 revise_error_sizes(status, size);
367 ctx->type = type;
368 ctx->array_size = num;
369 cper_bump_ia32x64_ctx_count(x86err);
371 return ctx;
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.
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.
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[] = {
390 "cache",
391 "TLB",
392 "bus",
393 "MS"
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)
403 return NULL;
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;
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;
418 revise_error_sizes(status, sizeof(*check));
420 guidcpy(&check->type, &check_guids[type]);
421 cper_bump_ia32x64_chk_count(x86err);
423 return check;
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.
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);
439 if (!entry)
440 return NULL;
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;
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;
459 return entry;
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"
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
509 * Section.
511 acpi_generic_error_status_t *bert_new_event(guid_t *guid)
513 size_t size;
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));
524 return NULL;
527 status = new_bert_status();
528 if (!status)
529 return NULL;
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;
541 if (r)
542 return status;
543 return NULL;
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;
552 int i;
553 msr_t *dest;
555 ctx = new_cper_ia32x64_ctx(status, x86err, CPER_IA32X64_CTX_MSR, num);
556 if (!ctx)
557 return NULL;
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);
567 return ctx;
570 static void bert_reserved_region(void **start, size_t *size)
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;
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;
595 return;
598 memset(bert_region_base, 0, bert_region_size);
601 BOOT_STATE_INIT_ENTRY(BS_PRE_DEVICE, BS_ON_EXIT, bert_storage_setup, NULL);