libpayload: configs: Add new config.featuretest to broaden CI
[coreboot.git] / src / drivers / elog / gsmi.c
blob36940f1c76367033ee13c88a225a40516f61cb94
1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #include <console/console.h>
4 #include <elog.h>
5 #include <stdint.h>
7 #define GSMI_RET_SUCCESS 0x00
8 #define GSMI_RET_INVALID_PARAMETER 0x82
9 #define GSMI_RET_UNSUPPORTED 0x83
11 #define GSMI_CMD_SET_EVENT_LOG 0x08
12 #define GSMI_CMD_CLEAR_EVENT_LOG 0x09
13 #define GSMI_CMD_LOG_S0IX_SUSPEND 0x0a
14 #define GSMI_CMD_LOG_S0IX_RESUME 0x0b
15 #define GSMI_CMD_HANDSHAKE_TYPE 0xc1
17 #define GSMI_HANDSHAKE_NONE 0x7f
18 #define GSMI_LOG_ENTRY_TYPE_KERNEL 0xDEAD
20 struct gsmi_set_eventlog_param {
21 u32 data_ptr;
22 u32 data_len;
23 u32 type;
24 } __packed;
26 struct gsmi_set_eventlog_type1 {
27 u16 type;
28 u32 instance;
29 } __packed;
31 struct gsmi_clear_eventlog_param {
32 u32 percentage;
33 u32 data_type;
34 } __packed;
36 void __weak elog_gsmi_cb_platform_log_wake_source(void)
38 /* Default weak implementation, does nothing. */
41 void __weak elog_gsmi_cb_mainboard_log_wake_source(void)
43 /* Default weak implementation, does nothing. */
46 /* Param is usually EBX, ret in EAX */
47 u32 gsmi_exec(u8 command, u32 *param)
49 struct gsmi_set_eventlog_param *sel;
50 struct gsmi_set_eventlog_type1 *type1;
51 struct gsmi_clear_eventlog_param *cel;
52 u32 ret = GSMI_RET_UNSUPPORTED;
54 switch (command) {
55 case GSMI_CMD_HANDSHAKE_TYPE:
56 /* Used by kernel to verify basic SMI functionality */
57 printk(BIOS_DEBUG, "GSMI Handshake\n");
58 ret = GSMI_HANDSHAKE_NONE;
59 break;
61 case GSMI_CMD_SET_EVENT_LOG:
62 /* Look for a type1 event */
63 sel = (struct gsmi_set_eventlog_param *)(uintptr_t)(*param);
64 if (!sel)
65 break;
67 /* Make sure the input is usable */
68 if (sel->type != 1 && sel->data_ptr != 0 &&
69 sel->data_len != sizeof(struct gsmi_set_eventlog_type1))
70 break;
72 /* Event structure within the data buffer */
73 type1 = (struct gsmi_set_eventlog_type1 *)(uintptr_t)(sel->data_ptr);
74 if (!type1)
75 break;
77 printk(BIOS_DEBUG, "GSMI Set Event Log "
78 "(type=0x%x instance=0x%x)\n",
79 type1->type, type1->instance);
81 if (type1->type == GSMI_LOG_ENTRY_TYPE_KERNEL) {
82 /* Special case for linux kernel shutdown reason */
83 elog_add_event_dword(ELOG_TYPE_OS_EVENT,
84 type1->instance);
85 } else {
86 /* Add other events that may be used for testing */
87 elog_add_event_dword(type1->type, type1->instance);
89 ret = GSMI_RET_SUCCESS;
90 break;
92 case GSMI_CMD_CLEAR_EVENT_LOG:
93 /* Get parameter buffer even though we don't use it */
94 cel = (struct gsmi_clear_eventlog_param *)(uintptr_t)(*param);
95 if (!cel)
96 break;
98 printk(BIOS_DEBUG, "GSMI Clear Event Log (%u%% type=%u)\n",
99 cel->percentage, cel->data_type);
101 if (elog_clear() == 0)
102 ret = GSMI_RET_SUCCESS;
103 break;
105 case GSMI_CMD_LOG_S0IX_SUSPEND:
106 case GSMI_CMD_LOG_S0IX_RESUME:
107 ret = GSMI_RET_SUCCESS;
109 if (command == GSMI_CMD_LOG_S0IX_SUSPEND)
110 elog_add_event(ELOG_TYPE_S0IX_ENTER);
111 else {
112 elog_add_event(ELOG_TYPE_S0IX_EXIT);
113 elog_gsmi_cb_platform_log_wake_source();
114 elog_gsmi_cb_mainboard_log_wake_source();
116 break;
118 default:
119 printk(BIOS_DEBUG, "GSMI Unknown: 0x%02x\n", command);
120 break;
123 return ret;