blob: a8b8628b1113f5597354f9f86a5eaa02d001693c [file] [log] [blame]
Patrick Georgi11f00792020-03-04 15:10:45 +01001/* SPDX-License-Identifier: GPL-2.0-only */
Marshall Dawson991467d2018-09-04 12:32:56 -06002
Marshall Dawson991467d2018-09-04 12:32:56 -06003#include <cbmem.h>
4#include <console/console.h>
5#include <cpu/x86/name.h>
6#include <cpu/x86/msr.h>
7#include <cpu/x86/lapic.h>
Furquan Shaikh76cedd22020-05-02 10:24:23 -07008#include <acpi/acpi.h>
Marshall Dawson991467d2018-09-04 12:32:56 -06009#include <arch/bert_storage.h>
10#include <string.h>
Felix Held0a88c602021-05-27 20:16:45 +020011#include <types.h>
Marshall Dawson991467d2018-09-04 12:32:56 -060012
13/* BERT region management: Allow the chipset to determine the specific
14 * location of the BERT region. We find that base and size, then manage
15 * the allocation of error information within it.
16 *
17 * Use simple static variables for managing the BERT region. This is a thin
18 * implementation; it is only created and consumed by coreboot, and only in
19 * a single stage, and we don't want its information to survive reboot or
20 * resume cycles. If the requirements change, consider using IMD to help
21 * manage the space.
22 */
Felix Held0a88c602021-05-27 20:16:45 +020023static bool bert_region_broken;
Marshall Dawson991467d2018-09-04 12:32:56 -060024static void *bert_region_base;
25static size_t bert_region_size;
26static size_t bert_region_used;
27
28/* Calculate the remaining space in the BERT region. This knowledge may help
29 * the caller prioritize the information to store.
30 */
31size_t bert_storage_remaining(void)
32{
33 return bert_region_broken ? 0 : bert_region_size - bert_region_used;
34}
35
Felix Held0a88c602021-05-27 20:16:45 +020036bool bert_errors_present(void)
Marshall Dawson991467d2018-09-04 12:32:56 -060037{
Felix Held0a88c602021-05-27 20:16:45 +020038 return !bert_region_broken && bert_region_used;
Marshall Dawson991467d2018-09-04 12:32:56 -060039}
40
41void bert_errors_region(void **start, size_t *size)
42{
43 if (bert_region_broken) {
44 *start = NULL;
45 *size = 0;
46 return;
47 }
48
49 /* No metadata, etc. with our region, so this is easy */
50 *start = bert_region_base;
51 *size = bert_region_used;
52}
53
54static void *bert_allocate_storage(size_t size)
55{
56 size_t alloc;
57
58 if (bert_region_broken)
59 return NULL;
60 if (bert_region_used + size > bert_region_size)
61 return NULL;
62
63 alloc = bert_region_used;
64 bert_region_used += size;
65
66 return (void *)((u8 *)bert_region_base + alloc);
67}
68
69/* Generic Error Status: Each Status represents a unique error event within
70 * the BERT errors region. Each event may have multiple errors associated
71 * with it.
72 */
73
74/* Find the nth (1-based) Generic Data Structure attached to an Error Status */
75static void *acpi_hest_generic_data_nth(
76 acpi_generic_error_status_t *status, int num)
77{
78 acpi_hest_generic_data_v300_t *ptr;
79 size_t struct_size;
80
81 if (!num || num > bert_entry_count(status))
82 return NULL;
83
84 ptr = (acpi_hest_generic_data_v300_t *)(status + 1);
85 while (--num) {
86 if (ptr->revision == HEST_GENERIC_ENTRY_V300)
87 struct_size = sizeof(acpi_hest_generic_data_v300_t);
88 else
89 struct_size = sizeof(acpi_hest_generic_data_t);
90 ptr = (acpi_hest_generic_data_v300_t *)(
91 (u8 *)ptr
92 + ptr->data_length
93 + struct_size);
94 }
95 return ptr;
96}
97
98/* Update data_length for this Error Status, and final Data Entry it contains */
99static void revise_error_sizes(acpi_generic_error_status_t *status, size_t size)
100{
101 acpi_hest_generic_data_v300_t *entry;
102 int entries;
103
104 if (!status)
105 return;
106
107 entries = bert_entry_count(status);
108 entry = acpi_hest_generic_data_nth(status, entries);
109 status->data_length += size;
Francois Toguo522e0db2021-01-21 09:55:19 -0800110 status->raw_data_length += size;
Marshall Dawson991467d2018-09-04 12:32:56 -0600111 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 */
124static 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, "Error: 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 */
157static 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, "Error: 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, "Error: 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);
Francois Toguo522e0db2021-01-21 09:55:19 -0800179 status->raw_data_length += sizeof(*entry);
Marshall Dawson991467d2018-09-04 12:32:56 -0600180 bert_bump_entry_count(status);
181
182 return entry;
183}
184
185/* Find the size of a CPER error section w/o any add-ons */
186static size_t sizeof_error_section(guid_t *guid)
187{
188 if (!guidcmp(guid, &CPER_SEC_PROC_GENERIC_GUID))
189 return sizeof(cper_proc_generic_error_section_t);
190 else if (!guidcmp(guid, &CPER_SEC_PROC_IA32X64_GUID))
191 return sizeof(cper_ia32x64_proc_error_section_t);
Francois Toguo522e0db2021-01-21 09:55:19 -0800192 else if (!guidcmp(guid, &CPER_SEC_FW_ERR_REC_REF_GUID))
193 return sizeof(cper_fw_err_rec_section_t);
Marshall Dawson991467d2018-09-04 12:32:56 -0600194 /* else if ... sizeof(structures not yet defined) */
195
196 printk(BIOS_ERR, "Error: Requested size of unrecognized CPER GUID\n");
197 return 0;
198}
199
Francois Toguo522e0db2021-01-21 09:55:19 -0800200void *new_cper_fw_error_crashlog(acpi_generic_error_status_t *status, size_t cl_size)
201{
202 void *cl_data = bert_allocate_storage(cl_size);
203 if (!cl_data) {
Benjamin Doron07dda332021-02-05 00:23:46 +0000204 printk(BIOS_ERR, "Error: Crashlog entry (size %zu) would exceed available region\n",
Francois Toguo522e0db2021-01-21 09:55:19 -0800205 cl_size);
206 return NULL;
207 }
208
209 revise_error_sizes(status, cl_size);
210
211 return cl_data;
212}
213
214/* Helper to append an ACPI Generic Error Data Entry per crashlog data */
215acpi_hest_generic_data_v300_t *bert_append_fw_err(acpi_generic_error_status_t *status)
216{
217 acpi_hest_generic_data_v300_t *entry;
218 cper_fw_err_rec_section_t *fw_err;
219
220 entry = bert_append_error_datasection(status, &CPER_SEC_FW_ERR_REC_REF_GUID);
221 if (!entry)
222 return NULL;
223
224 status->block_status |= GENERIC_ERR_STS_UNCORRECTABLE_VALID;
225 status->error_severity = ACPI_GENERROR_SEV_FATAL;
226 entry->error_severity = ACPI_GENERROR_SEV_FATAL;
227
228 fw_err = section_of_acpientry(fw_err, entry);
229
230 fw_err->record_type = CRASHLOG_RECORD_TYPE;
231 fw_err->revision = CRASHLOG_FW_ERR_REV;
232 fw_err->record_id = 0;
233 guidcpy(&fw_err->record_guid, &FW_ERR_RECORD_ID_CRASHLOG_GUID);
234
235 return entry;
236}
237
Marshall Dawson991467d2018-09-04 12:32:56 -0600238/* Append a new ACPI Generic Error Data Entry plus CPER Error Section to an
239 * existing ACPI Generic Error Status Block. The caller is responsible for
240 * the setting the status and entry severity, as well as populating all fields
241 * of the error section.
242 */
243acpi_hest_generic_data_v300_t *bert_append_error_datasection(
244 acpi_generic_error_status_t *status, guid_t *guid)
245{
246 acpi_hest_generic_data_v300_t *entry;
247 void *sect;
248 size_t sect_size;
249
250 sect_size = sizeof_error_section(guid);
251 if (!sect_size)
252 return NULL; /* Don't allocate structure if bad GUID passed */
253
254 if (sizeof(*entry) + sect_size > bert_storage_remaining())
255 return NULL;
256
257 entry = new_generic_error_entry(status);
258 if (!entry)
259 return NULL;
260
261 /* error section immediately follows the Generic Error Data Entry */
262 sect = bert_allocate_storage(sect_size);
263 if (!sect)
264 return NULL;
265
266 revise_error_sizes(status, sect_size);
267
268 guidcpy(&entry->section_type, guid);
269 return entry;
270}
271
272/* Helper to append an ACPI Generic Error Data Entry plus a CPER Processor
273 * Generic Error Section. As many fields are populated as possible for the
274 * caller.
275 */
276acpi_hest_generic_data_v300_t *bert_append_genproc(
277 acpi_generic_error_status_t *status)
278{
279 acpi_hest_generic_data_v300_t *entry;
280 cper_proc_generic_error_section_t *ges;
281
282 entry = bert_append_error_datasection(status,
283 &CPER_SEC_PROC_GENERIC_GUID);
284 if (!entry)
285 return NULL;
286
287 status->block_status |= GENERIC_ERR_STS_UNCORRECTABLE_VALID;
288 status->error_severity = ACPI_GENERROR_SEV_FATAL;
289
290 entry->error_severity = ACPI_GENERROR_SEV_FATAL;
291
292 ges = section_of_acpientry(ges, entry);
293
294 ges->proc_type = GENPROC_PROCTYPE_IA32X64;
295 ges->validation |= GENPROC_VALID_PROC_TYPE;
296
297 ges->cpu_version = cpuid_eax(1);
298 ges->validation |= GENPROC_VALID_CPU_VERSION;
299
300 fill_processor_name(ges->cpu_brand_string);
301 ges->validation |= GENPROC_VALID_CPU_BRAND;
302
303 ges->proc_id = lapicid();
304 ges->validation |= GENPROC_VALID_CPU_ID;
305
306 return entry;
307}
308
309/* Add a new IA32/X64 Processor Context Structure (Table 261), following any
310 * other contexts, to an existing Processor Error Section (Table 255). Contexts
311 * may only be added after the entire Processor Error Info array has been
312 * created.
313 *
314 * This function fills only the minimal amount of information required to parse
315 * or step through the contexts. The type is filled and PROC_CONTEXT_INFO_NUM
316 * is updated.
317 *
318 * type is one of:
319 * CPER_IA32X64_CTX_UNCL
320 * CPER_IA32X64_CTX_MSR
321 * CPER_IA32X64_CTX_32BIT_EX
322 * CPER_IA32X64_CTX_64BIT_EX
323 * CPER_IA32X64_CTX_FXSAVE
324 * CPER_IA32X64_CTX_32BIT_DBG
325 * CPER_IA32X64_CTX_64BIT_DBG
326 * CPER_IA32X64_CTX_MEMMAPPED
327 * num is the number of bytes eventually used to fill the context's register
328 * array, e.g. 4 MSRs * sizeof(msr_t)
329 *
330 * status and entry data_length values are updated.
331 */
332cper_ia32x64_context_t *new_cper_ia32x64_ctx(
333 acpi_generic_error_status_t *status,
334 cper_ia32x64_proc_error_section_t *x86err, int type, int num)
335{
336 size_t size;
337 cper_ia32x64_context_t *ctx;
338 static const char * const ctx_names[] = {
339 "Unclassified Data",
340 "MSR Registers",
341 "32-bit Mode Execution",
342 "64-bit Mode Execution",
Richard Spiegelc75f2d82018-09-14 08:27:50 -0700343 "FXSAVE",
344 "32-bit Mode Debug",
345 "64-bit Mode Debug",
Marshall Dawson991467d2018-09-04 12:32:56 -0600346 "Memory Mapped"
347 };
348
349 if (type > CPER_IA32X64_CTX_MEMMAPPED)
350 return NULL;
351
352 if (cper_ia32x64_proc_num_ctxs(x86err) == I32X64SEC_VALID_CTXNUM_MAX) {
353 printk(BIOS_ERR, "Error: New IA32X64 %s context entry would exceed max allowable contexts\n",
354 ctx_names[type]);
355 return NULL;
356 }
357
358 size = cper_ia32x64_ctx_sz_bytype(type, num);
359 ctx = bert_allocate_storage(size);
360 if (!ctx) {
361 printk(BIOS_ERR, "Error: New IA32X64 %s context entry would exceed available region\n",
362 ctx_names[type]);
363 return NULL;
364 }
365
366 revise_error_sizes(status, size);
367
368 ctx->type = type;
369 ctx->array_size = num;
370 cper_bump_ia32x64_ctx_count(x86err);
371
372 return ctx;
373}
374
375/* Add a new IA32/X64 Processor Error Information Structure (Table 256),
376 * following any other errors, to an existing Processor Error Section
377 * (Table 255). All error structures must be added before any contexts are
378 * added.
379 *
380 * This function fills only the minimal amount of information required to parse
381 * or step through the errors. The type is filled and PROC_ERR_INFO_NUM is
382 * updated.
383 */
384cper_ia32x64_proc_error_info_t *new_cper_ia32x64_check(
385 acpi_generic_error_status_t *status,
386 cper_ia32x64_proc_error_section_t *x86err,
387 enum cper_x86_check_type type)
388{
389 cper_ia32x64_proc_error_info_t *check;
390 static const char * const check_names[] = {
391 "cache",
392 "TLB",
393 "bus",
394 "MS"
395 };
396 const guid_t check_guids[] = {
397 X86_PROCESSOR_CACHE_CHK_ERROR_GUID,
398 X86_PROCESSOR_TLB_CHK_ERROR_GUID,
399 X86_PROCESSOR_BUS_CHK_ERROR_GUID,
400 X86_PROCESSOR_MS_CHK_ERROR_GUID
401 };
402
403 if (type > X86_PROCESSOR_CHK_MAX)
404 return NULL;
405
406 if (cper_ia32x64_proc_num_chks(x86err) == I32X64SEC_VALID_ERRNUM_MAX) {
407 printk(BIOS_ERR, "Error: New IA32X64 %s check entry would exceed max allowable errors\n",
408 check_names[type]);
409 return NULL;
410 }
411
412 check = bert_allocate_storage(sizeof(*check));
413 if (!check) {
414 printk(BIOS_ERR, "Error: New IA32X64 %s check entry would exceed available region\n",
415 check_names[type]);
416 return NULL;
417 }
418
419 revise_error_sizes(status, sizeof(*check));
420
421 guidcpy(&check->type, &check_guids[type]);
422 cper_bump_ia32x64_chk_count(x86err);
423
424 return check;
425}
426
427/* Helper to append an ACPI Generic Error Data Entry plus a CPER IA32/X64
428 * Processor Error Section. As many fields are populated as possible for the
429 * caller.
430 */
431acpi_hest_generic_data_v300_t *bert_append_ia32x64(
432 acpi_generic_error_status_t *status)
433{
434 acpi_hest_generic_data_v300_t *entry;
435 cper_ia32x64_proc_error_section_t *ipe;
436 struct cpuid_result id;
437
438 entry = bert_append_error_datasection(status,
439 &CPER_SEC_PROC_IA32X64_GUID);
440 if (!entry)
441 return NULL;
442
443 status->block_status |= GENERIC_ERR_STS_UNCORRECTABLE_VALID;
444 status->error_severity = ACPI_GENERROR_SEV_FATAL;
445
446 entry->error_severity = ACPI_GENERROR_SEV_FATAL;
447
448 ipe = section_of_acpientry(ipe, entry);
449
450 ipe->apicid = lapicid();
451 ipe->validation |= I32X64SEC_VALID_LAPIC;
452
453 id = cpuid(1);
454 ipe->cpuid[0] = id.eax;
455 ipe->cpuid[1] = id.ebx;
456 ipe->cpuid[2] = id.ecx;
457 ipe->cpuid[3] = id.edx;
458 ipe->validation |= I32X64SEC_VALID_CPUID;
459
460 return entry;
461}
462
463static const char * const generic_error_types[] = {
464 "PROCESSOR_GENERIC",
465 "PROCESSOR_SPECIFIC_X86",
466 "PROCESSOR_SPECIFIC_ARM",
467 "PLATFORM_MEMORY",
468 "PLATFORM_MEMORY2",
469 "PCIE",
470 "FW_ERROR_RECORD",
471 "PCI_PCIX_BUS",
472 "PCI_DEVICE",
473 "DMAR_GENERIC",
474 "DIRECTED_IO_DMAR",
475 "IOMMU_DMAR",
476 "UNRECOGNIZED"
477};
478
479static const char *generic_error_name(guid_t *guid)
480{
481 if (!guidcmp(guid, &CPER_SEC_PROC_GENERIC_GUID))
482 return generic_error_types[0];
483 if (!guidcmp(guid, &CPER_SEC_PROC_IA32X64_GUID))
484 return generic_error_types[1];
485 if (!guidcmp(guid, &CPER_SEC_PROC_ARM_GUID))
486 return generic_error_types[2];
487 if (!guidcmp(guid, &CPER_SEC_PLATFORM_MEM_GUID))
488 return generic_error_types[3];
489 if (!guidcmp(guid, &CPER_SEC_PLATFORM_MEM2_GUID))
490 return generic_error_types[4];
491 if (!guidcmp(guid, &CPER_SEC_PCIE_GUID))
492 return generic_error_types[5];
493 if (!guidcmp(guid, &CPER_SEC_FW_ERR_REC_REF_GUID))
494 return generic_error_types[6];
495 if (!guidcmp(guid, &CPER_SEC_PCI_X_BUS_GUID))
496 return generic_error_types[7];
497 if (!guidcmp(guid, &CPER_SEC_PCI_DEV_GUID))
498 return generic_error_types[8];
499 if (!guidcmp(guid, &CPER_SEC_DMAR_GENERIC_GUID))
500 return generic_error_types[9];
501 if (!guidcmp(guid, &CPER_SEC_DMAR_VT_GUID))
502 return generic_error_types[10];
503 if (!guidcmp(guid, &CPER_SEC_DMAR_IOMMU_GUID))
504 return generic_error_types[11];
505 return generic_error_types[12];
506}
507
508/* Add a new event to the BERT region. An event consists of an ACPI Error
509 * Status Block, a Generic Error Data Entry, and an associated CPER Error
510 * Section.
511 */
512acpi_generic_error_status_t *bert_new_event(guid_t *guid)
513{
514 size_t size;
515 acpi_generic_error_status_t *status;
516 acpi_hest_generic_data_v300_t *entry, *r;
517
518 size = sizeof(*status);
519 size += sizeof(*entry);
520 size += sizeof_error_section(guid);
521
522 if (size > bert_storage_remaining()) {
523 printk(BIOS_ERR, "Error: Not enough BERT region space to add event for type %s\n",
524 generic_error_name(guid));
525 return NULL;
526 }
527
528 status = new_bert_status();
529 if (!status)
530 return NULL;
531
Francois Toguo522e0db2021-01-21 09:55:19 -0800532 status->raw_data_length = sizeof(*status);
533
Marshall Dawson991467d2018-09-04 12:32:56 -0600534 if (!guidcmp(guid, &CPER_SEC_PROC_GENERIC_GUID))
535 r = bert_append_genproc(status);
536 else if (!guidcmp(guid, &CPER_SEC_PROC_GENERIC_GUID))
537 r = bert_append_ia32x64(status);
Lijian Zhao94e49612021-02-06 12:18:12 +0800538 else if (!guidcmp(guid, &CPER_SEC_FW_ERR_REC_REF_GUID))
Francois Toguo522e0db2021-01-21 09:55:19 -0800539 r = bert_append_fw_err(status);
Marshall Dawson991467d2018-09-04 12:32:56 -0600540 /* else if other types not implemented */
541 else
542 r = NULL;
543
544 if (r)
545 return status;
546 return NULL;
547}
548
549/* Helper to add an MSR context to an existing IA32/X64-type error entry */
550cper_ia32x64_context_t *cper_new_ia32x64_context_msr(
551 acpi_generic_error_status_t *status,
552 cper_ia32x64_proc_error_section_t *x86err, u32 addr, int num)
553{
554 cper_ia32x64_context_t *ctx;
555 int i;
556 msr_t *dest;
557
558 ctx = new_cper_ia32x64_ctx(status, x86err, CPER_IA32X64_CTX_MSR, num);
559 if (!ctx)
560 return NULL;
561
562 /* already filled ctx->type = CPER_IA32X64_CTX_MSR; */
563 ctx->msr_addr = addr;
564 ctx->array_size = num * sizeof(msr_t);
565
566 dest = (msr_t *)((u8 *)(ctx + 1)); /* point to the Register Array */
567
568 for (i = 0 ; i < num ; i++)
569 *(dest + i) = rdmsr(addr + i);
570 return ctx;
571}
572
573/* The region must be in memory marked as reserved. If not implemented,
574 * skip generating the information in the region.
575 */
576__weak void bert_reserved_region(void **start, size_t *size)
577{
578 printk(BIOS_ERR, "Error: %s not implemented. BERT region generation disabled\n",
579 __func__);
580 *start = NULL;
581 *size = 0;
582}
583
584static void bert_storage_setup(int unused)
585{
586 /* Always start with a blank bert region. Make sure nothing is
587 * maintained across reboots or resumes.
588 */
Felix Held0a88c602021-05-27 20:16:45 +0200589 bert_region_broken = false;
Marshall Dawson991467d2018-09-04 12:32:56 -0600590 bert_region_used = 0;
591
592 bert_reserved_region(&bert_region_base, &bert_region_size);
593
594 if (!bert_region_base || !bert_region_size) {
595 printk(BIOS_ERR, "Bug: Can't find/add BERT storage area\n");
Felix Held0a88c602021-05-27 20:16:45 +0200596 bert_region_broken = true;
Marshall Dawson991467d2018-09-04 12:32:56 -0600597 return;
598 }
599
600 memset(bert_region_base, 0, bert_region_size);
601}
602
603RAMSTAGE_CBMEM_INIT_HOOK(bert_storage_setup)