xref: /aosp_15_r20/external/coreboot/src/soc/intel/common/block/acpi/acpi_bert.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
3 #include <acpi/acpi.h>
4 #include <acpi/acpigen.h>
5 #include <arch/bert_storage.h>
6 #include <console/console.h>
7 #include <intelblocks/acpi.h>
8 #include <intelblocks/crashlog.h>
9 
boot_error_src_present(cl_node_t * head)10 static bool boot_error_src_present(cl_node_t *head)
11 {
12 	if (!discover_crashlog()) {
13 		printk(BIOS_SPEW, "Crashlog discovery result: crashlog not found\n");
14 		return false;
15 	}
16 
17 	collect_pmc_and_cpu_crashlog_from_srams(head);
18 
19 	/* Discovery tables sizes can be larger than the actual valid collected data */
20 	u32 crashlog_size = cl_get_total_data_size();
21 
22 	return (crashlog_size > 0);
23 }
24 
record_crashlog_into_bert(void ** region,size_t * length)25 static enum cb_err record_crashlog_into_bert(void **region, size_t *length)
26 {
27 	acpi_generic_error_status_t *status = NULL;
28 	size_t gesb_header_size;
29 	void *cl_acpi_data = NULL;
30 	cl_node_t cl_list_head = {.size = 0, .data = NULL, .next = NULL};
31 
32 	if (!boot_error_src_present(&cl_list_head)) {
33 		return CB_ERR;
34 	}
35 
36 	if (!cl_get_total_data_size()) {
37 		printk(BIOS_ERR, "No crashlog record present\n");
38 		return CB_ERR;
39 	}
40 
41 	status = bert_new_event(&CPER_SEC_FW_ERR_REC_REF_GUID);
42 	gesb_header_size = sizeof(*status);
43 
44 	if (!status) {
45 		printk(BIOS_ERR, "unable to allocate GSB\n");
46 		return CB_ERR;
47 	}
48 
49 	if (cl_get_total_data_size() > bert_storage_remaining()) {
50 		printk(BIOS_ERR, "Crashlog entry would exceed available region\n");
51 		return CB_ERR;
52 	}
53 
54 	bool multi_entry = false;
55 	cl_node_t *cl_node = cl_list_head.next;
56 	while (cl_node) {
57 		if ((cl_node->size <= 0) || (!(cl_node->data))) {
58 			cl_node = cl_node->next;
59 			continue;
60 		}
61 
62 		if (multi_entry) {
63 			if (!bert_append_fw_err(status)) {
64 				printk(BIOS_ERR, "Crashlog entry would exceed available region\n");
65 				return CB_ERR;
66 			}
67 		}
68 
69 		cl_acpi_data = new_cper_fw_error_crashlog(status, cl_node->size);
70 		if (!cl_acpi_data) {
71 			printk(BIOS_ERR, "Crashlog entry(size 0x%x) would exceed available region\n",
72 					cl_node->size);
73 			return CB_ERR;
74 		}
75 		memcpy(cl_acpi_data, (void *) cl_node->data, cl_node->size);
76 
77 		cl_node_t *temp = cl_node;
78 		cl_node = cl_node->next;
79 		free_cl_node(temp);
80 		multi_entry = true;
81 	}
82 
83 	*length = status->data_length + gesb_header_size;
84 	*region = (void *)status;
85 
86 	return CB_SUCCESS;
87 }
88 
acpi_soc_get_bert_region(void ** region,size_t * length)89 enum cb_err acpi_soc_get_bert_region(void **region, size_t *length)
90 {
91 	if (CONFIG(SOC_INTEL_CRASHLOG)) {
92 		return record_crashlog_into_bert(region, length);
93 	} else {
94 		/* Check if MCA error has been added into BERT. */
95 		if (bert_should_generate_acpi_table()) {
96 			bert_errors_region(region, length);
97 			if (!*region) {
98 				printk(BIOS_ERR, "Can't find BERT storage area\n");
99 				return CB_ERR;
100 			}
101 		}
102 		return CB_SUCCESS;
103 	}
104 }
105