blob: a7061fa7c5c59147ce7c9eac1f3c995abb5dba49 [file] [log] [blame]
Patrick Georgi11f00792020-03-04 15:10:45 +01001/* SPDX-License-Identifier: GPL-2.0-only */
2/* This file is part of the coreboot project. */
Marshall Dawson991467d2018-09-04 12:32:56 -06003
4#include <bootstate.h>
5#include <cbmem.h>
6#include <console/console.h>
7#include <cpu/x86/name.h>
8#include <cpu/x86/msr.h>
9#include <cpu/x86/lapic.h>
10#include <arch/acpi.h>
11#include <arch/bert_storage.h>
12#include <string.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 */
24static int bert_region_broken;
25static void *bert_region_base;
26static size_t bert_region_size;
27static 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 */
32size_t bert_storage_remaining(void)
33{
34 return bert_region_broken ? 0 : bert_region_size - bert_region_used;
35}
36
37int bert_errors_present(void)
38{
39 return bert_region_broken ? 0 : !!bert_region_used;
40}
41
42void 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
55static 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 */
76static 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 */
100static 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 */
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);
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 */
185static 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 ... sizeof(structures not yet defined) */
192
193 printk(BIOS_ERR, "Error: Requested size of unrecognized CPER GUID\n");
194 return 0;
195}
196
197/* Append a new ACPI Generic Error Data Entry plus CPER Error Section to an
198 * existing ACPI Generic Error Status Block. The caller is responsible for
199 * the setting the status and entry severity, as well as populating all fields
200 * of the error section.
201 */
202acpi_hest_generic_data_v300_t *bert_append_error_datasection(
203 acpi_generic_error_status_t *status, guid_t *guid)
204{
205 acpi_hest_generic_data_v300_t *entry;
206 void *sect;
207 size_t sect_size;
208
209 sect_size = sizeof_error_section(guid);
210 if (!sect_size)
211 return NULL; /* Don't allocate structure if bad GUID passed */
212
213 if (sizeof(*entry) + sect_size > bert_storage_remaining())
214 return NULL;
215
216 entry = new_generic_error_entry(status);
217 if (!entry)
218 return NULL;
219
220 /* error section immediately follows the Generic Error Data Entry */
221 sect = bert_allocate_storage(sect_size);
222 if (!sect)
223 return NULL;
224
225 revise_error_sizes(status, sect_size);
226
227 guidcpy(&entry->section_type, guid);
228 return entry;
229}
230
231/* Helper to append an ACPI Generic Error Data Entry plus a CPER Processor
232 * Generic Error Section. As many fields are populated as possible for the
233 * caller.
234 */
235acpi_hest_generic_data_v300_t *bert_append_genproc(
236 acpi_generic_error_status_t *status)
237{
238 acpi_hest_generic_data_v300_t *entry;
239 cper_proc_generic_error_section_t *ges;
240
241 entry = bert_append_error_datasection(status,
242 &CPER_SEC_PROC_GENERIC_GUID);
243 if (!entry)
244 return NULL;
245
246 status->block_status |= GENERIC_ERR_STS_UNCORRECTABLE_VALID;
247 status->error_severity = ACPI_GENERROR_SEV_FATAL;
248
249 entry->error_severity = ACPI_GENERROR_SEV_FATAL;
250
251 ges = section_of_acpientry(ges, entry);
252
253 ges->proc_type = GENPROC_PROCTYPE_IA32X64;
254 ges->validation |= GENPROC_VALID_PROC_TYPE;
255
256 ges->cpu_version = cpuid_eax(1);
257 ges->validation |= GENPROC_VALID_CPU_VERSION;
258
259 fill_processor_name(ges->cpu_brand_string);
260 ges->validation |= GENPROC_VALID_CPU_BRAND;
261
262 ges->proc_id = lapicid();
263 ges->validation |= GENPROC_VALID_CPU_ID;
264
265 return entry;
266}
267
268/* Add a new IA32/X64 Processor Context Structure (Table 261), following any
269 * other contexts, to an existing Processor Error Section (Table 255). Contexts
270 * may only be added after the entire Processor Error Info array has been
271 * created.
272 *
273 * This function fills only the minimal amount of information required to parse
274 * or step through the contexts. The type is filled and PROC_CONTEXT_INFO_NUM
275 * is updated.
276 *
277 * type is one of:
278 * CPER_IA32X64_CTX_UNCL
279 * CPER_IA32X64_CTX_MSR
280 * CPER_IA32X64_CTX_32BIT_EX
281 * CPER_IA32X64_CTX_64BIT_EX
282 * CPER_IA32X64_CTX_FXSAVE
283 * CPER_IA32X64_CTX_32BIT_DBG
284 * CPER_IA32X64_CTX_64BIT_DBG
285 * CPER_IA32X64_CTX_MEMMAPPED
286 * num is the number of bytes eventually used to fill the context's register
287 * array, e.g. 4 MSRs * sizeof(msr_t)
288 *
289 * status and entry data_length values are updated.
290 */
291cper_ia32x64_context_t *new_cper_ia32x64_ctx(
292 acpi_generic_error_status_t *status,
293 cper_ia32x64_proc_error_section_t *x86err, int type, int num)
294{
295 size_t size;
296 cper_ia32x64_context_t *ctx;
297 static const char * const ctx_names[] = {
298 "Unclassified Data",
299 "MSR Registers",
300 "32-bit Mode Execution",
301 "64-bit Mode Execution",
Richard Spiegelc75f2d82018-09-14 08:27:50 -0700302 "FXSAVE",
303 "32-bit Mode Debug",
304 "64-bit Mode Debug",
Marshall Dawson991467d2018-09-04 12:32:56 -0600305 "Memory Mapped"
306 };
307
308 if (type > CPER_IA32X64_CTX_MEMMAPPED)
309 return NULL;
310
311 if (cper_ia32x64_proc_num_ctxs(x86err) == I32X64SEC_VALID_CTXNUM_MAX) {
312 printk(BIOS_ERR, "Error: New IA32X64 %s context entry would exceed max allowable contexts\n",
313 ctx_names[type]);
314 return NULL;
315 }
316
317 size = cper_ia32x64_ctx_sz_bytype(type, num);
318 ctx = bert_allocate_storage(size);
319 if (!ctx) {
320 printk(BIOS_ERR, "Error: New IA32X64 %s context entry would exceed available region\n",
321 ctx_names[type]);
322 return NULL;
323 }
324
325 revise_error_sizes(status, size);
326
327 ctx->type = type;
328 ctx->array_size = num;
329 cper_bump_ia32x64_ctx_count(x86err);
330
331 return ctx;
332}
333
334/* Add a new IA32/X64 Processor Error Information Structure (Table 256),
335 * following any other errors, to an existing Processor Error Section
336 * (Table 255). All error structures must be added before any contexts are
337 * added.
338 *
339 * This function fills only the minimal amount of information required to parse
340 * or step through the errors. The type is filled and PROC_ERR_INFO_NUM is
341 * updated.
342 */
343cper_ia32x64_proc_error_info_t *new_cper_ia32x64_check(
344 acpi_generic_error_status_t *status,
345 cper_ia32x64_proc_error_section_t *x86err,
346 enum cper_x86_check_type type)
347{
348 cper_ia32x64_proc_error_info_t *check;
349 static const char * const check_names[] = {
350 "cache",
351 "TLB",
352 "bus",
353 "MS"
354 };
355 const guid_t check_guids[] = {
356 X86_PROCESSOR_CACHE_CHK_ERROR_GUID,
357 X86_PROCESSOR_TLB_CHK_ERROR_GUID,
358 X86_PROCESSOR_BUS_CHK_ERROR_GUID,
359 X86_PROCESSOR_MS_CHK_ERROR_GUID
360 };
361
362 if (type > X86_PROCESSOR_CHK_MAX)
363 return NULL;
364
365 if (cper_ia32x64_proc_num_chks(x86err) == I32X64SEC_VALID_ERRNUM_MAX) {
366 printk(BIOS_ERR, "Error: New IA32X64 %s check entry would exceed max allowable errors\n",
367 check_names[type]);
368 return NULL;
369 }
370
371 check = bert_allocate_storage(sizeof(*check));
372 if (!check) {
373 printk(BIOS_ERR, "Error: New IA32X64 %s check entry would exceed available region\n",
374 check_names[type]);
375 return NULL;
376 }
377
378 revise_error_sizes(status, sizeof(*check));
379
380 guidcpy(&check->type, &check_guids[type]);
381 cper_bump_ia32x64_chk_count(x86err);
382
383 return check;
384}
385
386/* Helper to append an ACPI Generic Error Data Entry plus a CPER IA32/X64
387 * Processor Error Section. As many fields are populated as possible for the
388 * caller.
389 */
390acpi_hest_generic_data_v300_t *bert_append_ia32x64(
391 acpi_generic_error_status_t *status)
392{
393 acpi_hest_generic_data_v300_t *entry;
394 cper_ia32x64_proc_error_section_t *ipe;
395 struct cpuid_result id;
396
397 entry = bert_append_error_datasection(status,
398 &CPER_SEC_PROC_IA32X64_GUID);
399 if (!entry)
400 return NULL;
401
402 status->block_status |= GENERIC_ERR_STS_UNCORRECTABLE_VALID;
403 status->error_severity = ACPI_GENERROR_SEV_FATAL;
404
405 entry->error_severity = ACPI_GENERROR_SEV_FATAL;
406
407 ipe = section_of_acpientry(ipe, entry);
408
409 ipe->apicid = lapicid();
410 ipe->validation |= I32X64SEC_VALID_LAPIC;
411
412 id = cpuid(1);
413 ipe->cpuid[0] = id.eax;
414 ipe->cpuid[1] = id.ebx;
415 ipe->cpuid[2] = id.ecx;
416 ipe->cpuid[3] = id.edx;
417 ipe->validation |= I32X64SEC_VALID_CPUID;
418
419 return entry;
420}
421
422static const char * const generic_error_types[] = {
423 "PROCESSOR_GENERIC",
424 "PROCESSOR_SPECIFIC_X86",
425 "PROCESSOR_SPECIFIC_ARM",
426 "PLATFORM_MEMORY",
427 "PLATFORM_MEMORY2",
428 "PCIE",
429 "FW_ERROR_RECORD",
430 "PCI_PCIX_BUS",
431 "PCI_DEVICE",
432 "DMAR_GENERIC",
433 "DIRECTED_IO_DMAR",
434 "IOMMU_DMAR",
435 "UNRECOGNIZED"
436};
437
438static const char *generic_error_name(guid_t *guid)
439{
440 if (!guidcmp(guid, &CPER_SEC_PROC_GENERIC_GUID))
441 return generic_error_types[0];
442 if (!guidcmp(guid, &CPER_SEC_PROC_IA32X64_GUID))
443 return generic_error_types[1];
444 if (!guidcmp(guid, &CPER_SEC_PROC_ARM_GUID))
445 return generic_error_types[2];
446 if (!guidcmp(guid, &CPER_SEC_PLATFORM_MEM_GUID))
447 return generic_error_types[3];
448 if (!guidcmp(guid, &CPER_SEC_PLATFORM_MEM2_GUID))
449 return generic_error_types[4];
450 if (!guidcmp(guid, &CPER_SEC_PCIE_GUID))
451 return generic_error_types[5];
452 if (!guidcmp(guid, &CPER_SEC_FW_ERR_REC_REF_GUID))
453 return generic_error_types[6];
454 if (!guidcmp(guid, &CPER_SEC_PCI_X_BUS_GUID))
455 return generic_error_types[7];
456 if (!guidcmp(guid, &CPER_SEC_PCI_DEV_GUID))
457 return generic_error_types[8];
458 if (!guidcmp(guid, &CPER_SEC_DMAR_GENERIC_GUID))
459 return generic_error_types[9];
460 if (!guidcmp(guid, &CPER_SEC_DMAR_VT_GUID))
461 return generic_error_types[10];
462 if (!guidcmp(guid, &CPER_SEC_DMAR_IOMMU_GUID))
463 return generic_error_types[11];
464 return generic_error_types[12];
465}
466
467/* Add a new event to the BERT region. An event consists of an ACPI Error
468 * Status Block, a Generic Error Data Entry, and an associated CPER Error
469 * Section.
470 */
471acpi_generic_error_status_t *bert_new_event(guid_t *guid)
472{
473 size_t size;
474 acpi_generic_error_status_t *status;
475 acpi_hest_generic_data_v300_t *entry, *r;
476
477 size = sizeof(*status);
478 size += sizeof(*entry);
479 size += sizeof_error_section(guid);
480
481 if (size > bert_storage_remaining()) {
482 printk(BIOS_ERR, "Error: Not enough BERT region space to add event for type %s\n",
483 generic_error_name(guid));
484 return NULL;
485 }
486
487 status = new_bert_status();
488 if (!status)
489 return NULL;
490
491 if (!guidcmp(guid, &CPER_SEC_PROC_GENERIC_GUID))
492 r = bert_append_genproc(status);
493 else if (!guidcmp(guid, &CPER_SEC_PROC_GENERIC_GUID))
494 r = bert_append_ia32x64(status);
495 /* else if other types not implemented */
496 else
497 r = NULL;
498
499 if (r)
500 return status;
501 return NULL;
502}
503
504/* Helper to add an MSR context to an existing IA32/X64-type error entry */
505cper_ia32x64_context_t *cper_new_ia32x64_context_msr(
506 acpi_generic_error_status_t *status,
507 cper_ia32x64_proc_error_section_t *x86err, u32 addr, int num)
508{
509 cper_ia32x64_context_t *ctx;
510 int i;
511 msr_t *dest;
512
513 ctx = new_cper_ia32x64_ctx(status, x86err, CPER_IA32X64_CTX_MSR, num);
514 if (!ctx)
515 return NULL;
516
517 /* already filled ctx->type = CPER_IA32X64_CTX_MSR; */
518 ctx->msr_addr = addr;
519 ctx->array_size = num * sizeof(msr_t);
520
521 dest = (msr_t *)((u8 *)(ctx + 1)); /* point to the Register Array */
522
523 for (i = 0 ; i < num ; i++)
524 *(dest + i) = rdmsr(addr + i);
525 return ctx;
526}
527
528/* The region must be in memory marked as reserved. If not implemented,
529 * skip generating the information in the region.
530 */
531__weak void bert_reserved_region(void **start, size_t *size)
532{
533 printk(BIOS_ERR, "Error: %s not implemented. BERT region generation disabled\n",
534 __func__);
535 *start = NULL;
536 *size = 0;
537}
538
539static void bert_storage_setup(int unused)
540{
541 /* Always start with a blank bert region. Make sure nothing is
542 * maintained across reboots or resumes.
543 */
544 bert_region_broken = 0;
545 bert_region_used = 0;
546
547 bert_reserved_region(&bert_region_base, &bert_region_size);
548
549 if (!bert_region_base || !bert_region_size) {
550 printk(BIOS_ERR, "Bug: Can't find/add BERT storage area\n");
551 bert_region_broken = 1;
552 return;
553 }
554
555 memset(bert_region_base, 0, bert_region_size);
556}
557
558RAMSTAGE_CBMEM_INIT_HOOK(bert_storage_setup)