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