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
135
|
/*
* This file is part of the coreboot project.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <console/console.h>
#include <cpu/x86/smm.h>
#include <elog.h>
#define GSMI_RET_SUCCESS 0x00
#define GSMI_RET_INVALID_PARAMETER 0x82
#define GSMI_RET_UNSUPPORTED 0x83
#define GSMI_CMD_SET_EVENT_LOG 0x08
#define GSMI_CMD_CLEAR_EVENT_LOG 0x09
#define GSMI_CMD_LOG_S0IX_SUSPEND 0x0a
#define GSMI_CMD_LOG_S0IX_RESUME 0x0b
#define GSMI_CMD_HANDSHAKE_TYPE 0xc1
#define GSMI_HANDSHAKE_NONE 0x7f
#define GSMI_LOG_ENTRY_TYPE_KERNEL 0xDEAD
struct gsmi_set_eventlog_param {
u32 data_ptr;
u32 data_len;
u32 type;
} __packed;
struct gsmi_set_eventlog_type1 {
u16 type;
u32 instance;
} __packed;
struct gsmi_clear_eventlog_param {
u32 percentage;
u32 data_type;
} __packed;
void __weak elog_gsmi_cb_platform_log_wake_source(void)
{
/* Default weak implementation, does nothing. */
}
void __weak elog_gsmi_cb_mainboard_log_wake_source(void)
{
/* Default weak implementation, does nothing. */
}
/* Param is usually EBX, ret in EAX */
u32 gsmi_exec(u8 command, u32 *param)
{
struct gsmi_set_eventlog_param *sel;
struct gsmi_set_eventlog_type1 *type1;
struct gsmi_clear_eventlog_param *cel;
u32 ret = GSMI_RET_UNSUPPORTED;
switch (command) {
case GSMI_CMD_HANDSHAKE_TYPE:
/* Used by kernel to verify basic SMI functionality */
printk(BIOS_DEBUG, "GSMI Handshake\n");
ret = GSMI_HANDSHAKE_NONE;
break;
case GSMI_CMD_SET_EVENT_LOG:
/* Look for a type1 event */
sel = (struct gsmi_set_eventlog_param *)(*param);
if (!sel)
break;
/* Make sure the input is usable */
if (sel->type != 1 && sel->data_ptr != 0 &&
sel->data_len != sizeof(struct gsmi_set_eventlog_type1))
break;
/* Event structure within the data buffer */
type1 = (struct gsmi_set_eventlog_type1 *)(sel->data_ptr);
if (!type1)
break;
printk(BIOS_DEBUG, "GSMI Set Event Log "
"(type=0x%x instance=0x%x)\n",
type1->type, type1->instance);
if (type1->type == GSMI_LOG_ENTRY_TYPE_KERNEL) {
/* Special case for linux kernel shutdown reason */
elog_add_event_dword(ELOG_TYPE_OS_EVENT,
type1->instance);
} else {
/* Add other events that may be used for testing */
elog_add_event_dword(type1->type, type1->instance);
}
ret = GSMI_RET_SUCCESS;
break;
case GSMI_CMD_CLEAR_EVENT_LOG:
/* Get parameter buffer even though we don't use it */
cel = (struct gsmi_clear_eventlog_param *)(*param);
if (!cel)
break;
printk(BIOS_DEBUG, "GSMI Clear Event Log (%u%% type=%u)\n",
cel->percentage, cel->data_type);
if (elog_clear() == 0)
ret = GSMI_RET_SUCCESS;
break;
case GSMI_CMD_LOG_S0IX_SUSPEND:
case GSMI_CMD_LOG_S0IX_RESUME:
ret = GSMI_RET_SUCCESS;
if (command == GSMI_CMD_LOG_S0IX_SUSPEND)
elog_add_event(ELOG_TYPE_S0IX_ENTER);
else {
elog_add_event(ELOG_TYPE_S0IX_EXIT);
elog_gsmi_cb_platform_log_wake_source();
elog_gsmi_cb_mainboard_log_wake_source();
}
break;
default:
printk(BIOS_DEBUG, "GSMI Unknown: 0x%02x\n", command);
break;
}
return ret;
}
|