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