summaryrefslogtreecommitdiffstats
path: root/src/soc/intel/common/block/acpi/acpi_bert.c
blob: 350d5d3c00948593d0f68f8a4e3f3b7c39f7b359 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/* SPDX-License-Identifier: GPL-2.0-or-later */

#include <acpi/acpi.h>
#include <acpi/acpigen.h>
#include <arch/bert_storage.h>
#include <console/console.h>
#include <intelblocks/acpi.h>
#include <intelblocks/crashlog.h>

static bool boot_error_src_present(void)
{
	if (!discover_crashlog()) {
		printk(BIOS_SPEW, "Crashlog discovery result: crashlog not found\n");
		return false;
	}

	collect_pmc_and_cpu_crashlog_from_srams();

	/* Discovery tables sizes can be larger than the actual valid collected data */
	u32 crashlog_size = cl_get_total_data_size();

	return (crashlog_size > 0);
}

static enum cb_err record_crashlog_into_bert(void **region, size_t *length)
{
	acpi_generic_error_status_t *status = NULL;
	size_t cpu_record_size, pmc_record_size;
	size_t gesb_header_size;
	void *cl_data = NULL;

	if (!boot_error_src_present()) {
		return CB_ERR;
	}

	if (!cl_get_total_data_size()) {
		printk(BIOS_ERR, "No crashlog record present\n");
		return CB_ERR;
	}

	status = bert_new_event(&CPER_SEC_FW_ERR_REC_REF_GUID);
	gesb_header_size = sizeof(*status);

	if (!status) {
		printk(BIOS_ERR, "unable to allocate GSB\n");
		return CB_ERR;
	}

	if (cl_get_total_data_size() > bert_storage_remaining()) {
		printk(BIOS_ERR, "Crashlog entry would exceed "
				"available region\n");
		return CB_ERR;
	}

	cpu_record_size = cl_get_cpu_record_size();
	if (cpu_record_size) {
		cl_data = new_cper_fw_error_crashlog(status, cpu_record_size);
		if (!cl_data) {
			printk(BIOS_ERR, "Crashlog CPU entry(size 0x%zx) "
				"would exceed available region\n",
				cpu_record_size);
			return CB_ERR;
		}
		printk(BIOS_DEBUG, "cl_data %p, cpu_record_size 0x%zx\n",
			cl_data, cpu_record_size);
		cl_fill_cpu_records(cl_data);
	}

	pmc_record_size = cl_get_pmc_record_size();
	if (pmc_record_size) {
		/* Allocate new FW ERR structure in case PMC crashlog is present */
		if (pmc_record_size && !bert_append_fw_err(status)) {
			printk(BIOS_ERR, "Crashlog PMC entry would "
				"exceed available region\n");
			return CB_ERR;
		}

		cl_data = new_cper_fw_error_crashlog(status, pmc_record_size);
		if (!cl_data) {
			printk(BIOS_ERR, "Crashlog PMC entry(size 0x%zx) "
				"would exceed available region\n",
				pmc_record_size);
			return CB_ERR;
		}
		printk(BIOS_DEBUG, "cl_data %p, pmc_record_size 0x%zx\n",
			cl_data, pmc_record_size);
		cl_fill_pmc_records(cl_data);
	}

	if (CONFIG(SOC_INTEL_IOE_DIE_SUPPORT)) {
		size_t ioe_record_size = cl_get_ioe_record_size();
		if (ioe_record_size) {
			/*  Allocate new FW ERR structure in case IOE crashlog is present */
			if (ioe_record_size && !bert_append_fw_err(status)) {
				printk(BIOS_ERR, "Crashlog IOE entry would "
						"exceed available region\n");
				return CB_ERR;
			}

			cl_data = new_cper_fw_error_crashlog(status, ioe_record_size);
			if (!cl_data) {
				printk(BIOS_ERR, "Crashlog IOE entry(size 0x%zx) "
						"would exceed available region\n",
						ioe_record_size);
				return CB_ERR;
			}
			printk(BIOS_DEBUG, "cl_data %p, ioe_record_size 0x%zx\n",
					cl_data, ioe_record_size);
			cl_fill_ioe_records(cl_data);
		}
	}

	*length = status->data_length + gesb_header_size;
	*region = (void *)status;

	return CB_SUCCESS;
}

enum cb_err acpi_soc_get_bert_region(void **region, size_t *length)
{
	if (CONFIG(SOC_INTEL_CRASHLOG)) {
		return record_crashlog_into_bert(region, length);
	} else {
		/* Check if MCA error has been added into BERT. */
		if (bert_should_generate_acpi_table()) {
			bert_errors_region(region, length);
			if (!*region) {
				printk(BIOS_ERR, "Can't find BERT storage area\n");
				return CB_ERR;
			}
		}
		return CB_SUCCESS;
	}
}