summaryrefslogtreecommitdiff
path: root/src/drivers/elog/gsmi.c
blob: 42bfd0f183aff486c187be7f548e02c56215dec0 (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
135
136
137
138
139
/*
 * This file is part of the coreboot project.
 *
 * Copyright (C) 2012 The ChromiumOS Authors.  All rights reserved.
 *
 * 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 <compiler.h>
#include <arch/io.h>
#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 __attribute__((weak)) elog_gsmi_cb_platform_log_wake_source(void)
{
	/* Default weak implementation, does nothing. */
}

void __attribute__((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;
}