xref: /aosp_15_r20/external/coreboot/src/drivers/elog/gsmi.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <console/console.h>
4 #include <elog.h>
5 #include <stdint.h>
6 
7 #define GSMI_RET_SUCCESS		0x00
8 #define GSMI_RET_INVALID_PARAMETER	0x82
9 #define GSMI_RET_UNSUPPORTED		0x83
10 
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
16 
17 #define GSMI_HANDSHAKE_NONE		0x7f
18 #define GSMI_LOG_ENTRY_TYPE_KERNEL	0xDEAD
19 
20 struct gsmi_set_eventlog_param {
21 	u32 data_ptr;
22 	u32 data_len;
23 	u32 type;
24 } __packed;
25 
26 struct gsmi_set_eventlog_type1 {
27 	u16 type;
28 	u32 instance;
29 } __packed;
30 
31 struct gsmi_clear_eventlog_param {
32 	u32 percentage;
33 	u32 data_type;
34 } __packed;
35 
elog_gsmi_cb_platform_log_wake_source(void)36 void __weak elog_gsmi_cb_platform_log_wake_source(void)
37 {
38 	/* Default weak implementation, does nothing. */
39 }
40 
elog_gsmi_cb_mainboard_log_wake_source(void)41 void __weak elog_gsmi_cb_mainboard_log_wake_source(void)
42 {
43 	/* Default weak implementation, does nothing. */
44 }
45 
46 /* Param is usually EBX, ret in EAX */
gsmi_exec(u8 command,u32 * param)47 u32 gsmi_exec(u8 command, u32 *param)
48 {
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;
53 
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;
60 
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;
66 
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;
71 
72 		/* Event structure within the data buffer */
73 		type1 = (struct gsmi_set_eventlog_type1 *)(uintptr_t)(sel->data_ptr);
74 		if (!type1)
75 			break;
76 
77 		printk(BIOS_DEBUG, "GSMI Set Event Log "
78 		       "(type=0x%x instance=0x%x)\n",
79 		       type1->type, type1->instance);
80 
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);
88 		}
89 		ret = GSMI_RET_SUCCESS;
90 		break;
91 
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;
97 
98 		printk(BIOS_DEBUG, "GSMI Clear Event Log (%u%% type=%u)\n",
99 		       cel->percentage, cel->data_type);
100 
101 		if (elog_clear() == 0)
102 			ret = GSMI_RET_SUCCESS;
103 		break;
104 
105 	case GSMI_CMD_LOG_S0IX_SUSPEND:
106 	case GSMI_CMD_LOG_S0IX_RESUME:
107 		ret = GSMI_RET_SUCCESS;
108 
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();
115 		}
116 		break;
117 
118 	default:
119 		printk(BIOS_DEBUG, "GSMI Unknown: 0x%02x\n", command);
120 		break;
121 	}
122 
123 	return ret;
124 }
125