]> Joshua Wise's Git repositories - netwatch.git/blame_incremental - lib/state.c
Add support for the power button SMI.
[netwatch.git] / lib / state.c
... / ...
CommitLineData
1/* state.c
2 * SMM saved state manipulation functions.
3 * NetWatch system management mode administration console
4 *
5 * Copyright 2009, Google Inc.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 *
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * * Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following disclaimer
16 * in the documentation and/or other materials provided with the
17 * distribution.
18 * * Neither the name of Google Inc. nor the names of its
19 * contributors may be used to endorse or promote products derived from
20 * this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 */
34
35#include "state.h"
36#include <cpuid.h>
37#include <output.h>
38#include <minilib.h>
39
40/* Size flags. */
41#define SZ_BYTE 0x10000000
42#define SZ_WORD 0x20000000
43#define SZ_DWORD 0x40000000
44#define SZ_QWORD 0x80000000
45#define OFFSET_MASK 0x0FFFFFFF
46
47static const uint32_t offset_table_legacy[] = {
48 [STATE_REV] = 0xfefc | SZ_DWORD,
49 [STATE_REG_SMBASE] = 0xfef8 | SZ_DWORD,
50 [STATE_REG_IORESTART] = 0xff00 | SZ_WORD,
51 [STATE_REG_HALTRESTART] = 0xff02 | SZ_WORD,
52
53 [STATE_REG_EAX] = 0xffd0 | SZ_DWORD,
54 [STATE_REG_EBX] = 0xffdc | SZ_DWORD,
55 [STATE_REG_ECX] = 0xffd4 | SZ_DWORD,
56 [STATE_REG_EDX] = 0xffd8 | SZ_DWORD,
57 [STATE_REG_ESI] = 0xffe8 | SZ_DWORD,
58 [STATE_REG_EDI] = 0xffec | SZ_DWORD,
59 [STATE_REG_ESP] = 0xffe0 | SZ_DWORD,
60 [STATE_REG_EBP] = 0xffe4 | SZ_DWORD,
61 [STATE_REG_EIP] = 0xfff0 | SZ_DWORD,
62 [STATE_REG_EFLAGS] = 0xfff4 | SZ_DWORD,
63 [STATE_REG_CR0] = 0xfffc | SZ_DWORD,
64 [STATE_REG_CR3] = 0xfff8 | SZ_DWORD,
65
66 [STATE_REG_CS] = 0xffac | SZ_DWORD,
67 [STATE_REG_SS] = 0xffb0 | SZ_DWORD,
68 [STATE_REG_DS] = 0xffb4 | SZ_DWORD,
69 [STATE_REG_ES] = 0xffa8 | SZ_DWORD,
70 [STATE_REG_FS] = 0xffb8 | SZ_DWORD,
71 [STATE_REG_GS] = 0xffbc | SZ_DWORD,
72 [STATE_REG_IDT_BASE] = 0xff94 | SZ_DWORD
73};
74
75#define MAX_REG_LEGACY (sizeof(offset_table_legacy)/sizeof(offset_table_legacy[0]) - 1)
76
77static const uint32_t offset_table_amd64[] = {
78 [STATE_REV] = 0xfefc | SZ_DWORD,
79 [STATE_REG_SMBASE] = 0xff00 | SZ_DWORD,
80 [STATE_REG_IORESTART] = 0xfec8 | SZ_BYTE,
81 [STATE_REG_HALTRESTART] = 0xfec9 | SZ_BYTE,
82
83 [STATE_REG_EAX] = 0xfff8 | SZ_DWORD,
84 [STATE_REG_EBX] = 0xffe0 | SZ_DWORD,
85 [STATE_REG_ECX] = 0xfff0 | SZ_DWORD,
86 [STATE_REG_EDX] = 0xffe8 | SZ_DWORD,
87 [STATE_REG_ESI] = 0xffc8 | SZ_DWORD,
88 [STATE_REG_EDI] = 0xffc0 | SZ_DWORD,
89 [STATE_REG_ESP] = 0xffd8 | SZ_DWORD,
90 [STATE_REG_EBP] = 0xffd0 | SZ_DWORD,
91 [STATE_REG_EIP] = 0xff78 | SZ_DWORD,
92 [STATE_REG_EFLAGS] = 0xff70 | SZ_DWORD,
93 [STATE_REG_CR0] = 0xff58 | SZ_QWORD,
94 [STATE_REG_CR3] = 0xff50 | SZ_QWORD,
95
96 [STATE_REG_CS] = 0xfe10 | SZ_WORD,
97 [STATE_REG_CS_ATTRIB] = 0xfe12 | SZ_WORD,
98 [STATE_REG_CS_BASE] = 0xfe18 | SZ_QWORD,
99 [STATE_REG_CS_LIMIT] = 0xfe14 | SZ_DWORD,
100 [STATE_REG_SS] = 0xfe20 | SZ_WORD,
101 [STATE_REG_SS_ATTRIB] = 0xfe22 | SZ_WORD,
102 [STATE_REG_SS_BASE] = 0xfe28 | SZ_QWORD,
103 [STATE_REG_SS_LIMIT] = 0xfe24 | SZ_DWORD,
104 [STATE_REG_DS] = 0xfe30 | SZ_WORD,
105 [STATE_REG_DS_ATTRIB] = 0xfe32 | SZ_WORD,
106 [STATE_REG_DS_BASE] = 0xfe38 | SZ_QWORD,
107 [STATE_REG_DS_LIMIT] = 0xfe34 | SZ_DWORD,
108 [STATE_REG_ES] = 0xfe00 | SZ_WORD,
109 [STATE_REG_ES_ATTRIB] = 0xfe02 | SZ_WORD,
110 [STATE_REG_ES_BASE] = 0xfe08 | SZ_QWORD,
111 [STATE_REG_ES_LIMIT] = 0xfe04 | SZ_DWORD,
112 [STATE_REG_FS] = 0xfe40 | SZ_WORD,
113 [STATE_REG_FS_ATTRIB] = 0xfe42 | SZ_WORD,
114 [STATE_REG_FS_BASE] = 0xfe48 | SZ_QWORD,
115 [STATE_REG_FS_LIMIT] = 0xfe44 | SZ_DWORD,
116 [STATE_REG_GS] = 0xfe50 | SZ_WORD,
117 [STATE_REG_GS_ATTRIB] = 0xfe52 | SZ_WORD,
118 [STATE_REG_GS_BASE] = 0xfe58 | SZ_QWORD,
119 [STATE_REG_GS_LIMIT] = 0xfe54 | SZ_DWORD,
120 [STATE_REG_IDT_BASE] = 0xfe88 | SZ_QWORD,
121 [STATE_REG_IDT_LIMIT] = 0xfe84 | SZ_DWORD,
122
123 [STATE_REG_RAX] = 0xfff8 | SZ_QWORD,
124 [STATE_REG_RBX] = 0xffe0 | SZ_QWORD,
125 [STATE_REG_RCX] = 0xfff0 | SZ_QWORD,
126 [STATE_REG_RDX] = 0xffe8 | SZ_QWORD,
127 [STATE_REG_RSI] = 0xffc8 | SZ_QWORD,
128 [STATE_REG_RDI] = 0xffc0 | SZ_QWORD,
129 [STATE_REG_RSP] = 0xffd8 | SZ_QWORD,
130 [STATE_REG_RBP] = 0xffd0 | SZ_QWORD,
131 [STATE_REG_R8] = 0xffb8 | SZ_QWORD,
132 [STATE_REG_R9] = 0xffb0 | SZ_QWORD,
133 [STATE_REG_R10] = 0xffa8 | SZ_QWORD,
134 [STATE_REG_R11] = 0xffa0 | SZ_QWORD,
135 [STATE_REG_R12] = 0xff98 | SZ_QWORD,
136 [STATE_REG_R13] = 0xff90 | SZ_QWORD,
137 [STATE_REG_R14] = 0xff88 | SZ_QWORD,
138 [STATE_REG_R15] = 0xff80 | SZ_QWORD,
139 [STATE_REG_RIP] = 0xff78 | SZ_QWORD,
140 [STATE_REG_RFLAGS] = 0xff70 | SZ_QWORD,
141
142 [STATE_REG_EFER] = 0xfed0 | SZ_QWORD
143};
144
145static const char register_names[][4] = {
146 [STATE_REV] = "sREV",
147 [STATE_REG_SMBASE] = "sBSE",
148 [STATE_REG_IORESTART] = "IOrs",
149 [STATE_REG_HALTRESTART] = "HLrs",
150
151 [STATE_REG_EAX] = "%eax",
152 [STATE_REG_EBX] = "%ebx",
153 [STATE_REG_ECX] = "%ecx",
154 [STATE_REG_EDX] = "%edx",
155 [STATE_REG_ESI] = "%esi",
156 [STATE_REG_EDI] = "%edi",
157 [STATE_REG_ESP] = "%esp",
158 [STATE_REG_EBP] = "%ebp",
159 [STATE_REG_EIP] = "%eip",
160 [STATE_REG_EFLAGS] = "%eFL",
161 [STATE_REG_CR0] = "%cr0",
162 [STATE_REG_CR3] = "%cr3",
163
164 [STATE_REG_CS] = "%cs ",
165 [STATE_REG_CS_ATTRIB] = "csAT",
166 [STATE_REG_CS_BASE] = "csBA",
167 [STATE_REG_CS_LIMIT] = "csLI",
168 [STATE_REG_SS] = "%ss ",
169 [STATE_REG_SS_ATTRIB] = "ssAT",
170 [STATE_REG_SS_BASE] = "ssBA",
171 [STATE_REG_SS_LIMIT] = "ssLI",
172 [STATE_REG_DS] = "%ds ",
173 [STATE_REG_DS_ATTRIB] = "dsAT",
174 [STATE_REG_DS_BASE] = "dsBA",
175 [STATE_REG_DS_LIMIT] = "dsLI",
176 [STATE_REG_ES] = "%es ",
177 [STATE_REG_ES_ATTRIB] = "esAT",
178 [STATE_REG_ES_BASE] = "esBA",
179 [STATE_REG_ES_LIMIT] = "esLI",
180 [STATE_REG_FS] = "%fs ",
181 [STATE_REG_FS_ATTRIB] = "fsAT",
182 [STATE_REG_FS_BASE] = "fsBA",
183 [STATE_REG_FS_LIMIT] = "fsLI",
184 [STATE_REG_GS] = "%gs ",
185 [STATE_REG_GS_ATTRIB] = "gsAT",
186 [STATE_REG_GS_BASE] = "gsBA",
187 [STATE_REG_GS_LIMIT] = "gsLI",
188
189 [STATE_REG_RAX] = "%rax",
190 [STATE_REG_RBX] = "%rbx",
191 [STATE_REG_RCX] = "%rcx",
192 [STATE_REG_RDX] = "%rdx",
193 [STATE_REG_RSI] = "%rsi",
194 [STATE_REG_RDI] = "%rdi",
195 [STATE_REG_RSP] = "%rsp",
196 [STATE_REG_RBP] = "%rbp",
197 [STATE_REG_R8] = "%r8 ",
198 [STATE_REG_R9] = "%r9 ",
199 [STATE_REG_R10] = "%r10",
200 [STATE_REG_R11] = "%r11",
201 [STATE_REG_R12] = "%r12",
202 [STATE_REG_R13] = "%r13",
203 [STATE_REG_R14] = "%r14",
204 [STATE_REG_R15] = "%r15",
205 [STATE_REG_RIP] = "%rip",
206
207 [STATE_REG_EFER] = "EFER"
208};
209
210#define MAX_REG_AMD64 (sizeof(offset_table_amd64)/sizeof(offset_table_amd64[0]) - 1)
211
212static enum smm_type smm_type = SMM_TYPE_UNKNOWN;
213
214/* Probe CPUID to figure out what kind of processor this actually is.
215 * We memoize this in 'smm_type', so cpuid only needs to happen once.
216 */
217
218static void check_smm_type (void) {
219
220 struct cpuid_result r;
221
222 if (smm_type != SMM_TYPE_UNKNOWN)
223 return;
224
225 cpuid(0x80000000, &r);
226
227 if (r.eax < 0x80000001) {
228 smm_type = SMM_TYPE_32;
229 return;
230 }
231
232 cpuid(0x80000001, &r);
233
234 if (r.edx & 0x20000000) {
235 smm_type = SMM_TYPE_64;
236 } else {
237 smm_type = SMM_TYPE_32;
238 }
239}
240
241/* Get the offset of a register, by looking up in the appropriate table.
242 */
243
244static unsigned long get_offset(enum state_reg_t reg) {
245
246 check_smm_type();
247
248 if (smm_type == SMM_TYPE_32 && reg <= MAX_REG_LEGACY)
249 return offset_table_legacy[reg];
250 else if (smm_type == SMM_TYPE_64 && reg <= MAX_REG_AMD64)
251 return offset_table_amd64[reg];
252 else
253 return 0;
254}
255
256/* Which variety of processor are we running on?
257 */
258
259enum smm_type state_get_type (void) {
260 check_smm_type();
261 return smm_type;
262}
263
264/* Get a register.
265 *
266 * We assume that Aseg is always direct-mapped at 0xA0000. This may
267 * not be the case in the future, with multiple cores, but it is a
268 * safe assumption now.
269 */
270
271uint64_t state_get_reg (enum state_reg_t reg) {
272 unsigned long offset = get_offset(reg);
273 void * addr;
274 uint64_t value;
275
276 if (!offset)
277 return 0;
278
279 addr = (void *)((offset & OFFSET_MASK) + 0xA0000);
280
281 if (offset & SZ_BYTE)
282 value = *(uint8_t *)addr;
283 else if (offset & SZ_WORD)
284 value = *(uint16_t *)addr;
285 else if (offset & SZ_DWORD)
286 value = *(uint32_t *)addr;
287 else
288 value = *(uint64_t *)addr;
289
290 return value;
291}
292
293/* Get the size of a register, extracted from the saved state offset table.
294 */
295
296int state_reg_size (enum state_reg_t reg) {
297 unsigned long offset = get_offset(reg);
298
299 if (offset & SZ_BYTE) return 1;
300 else if (offset & SZ_WORD) return 2;
301 else if (offset & SZ_DWORD) return 4;
302 else if (offset & SZ_QWORD) return 8;
303 else return 0;
304}
305
306/* Modify a saved register.
307 *
308 * The same caveat about aseg's location applies here as well.
309 */
310
311int state_set_reg (enum state_reg_t reg, uint64_t value) {
312 unsigned long offset = get_offset(reg);
313 void * addr;
314
315 if (!offset)
316 return -1;
317
318 addr = (void *)((offset & OFFSET_MASK) + 0xA0000);
319
320 if (offset & SZ_BYTE)
321 *(uint8_t *)addr = (uint8_t) value;
322 else if (offset & SZ_WORD)
323 *(uint16_t *)addr = (uint16_t) value;
324 else if (offset & SZ_DWORD)
325 *(uint32_t *)addr = (uint32_t) value;
326 else
327 *(uint64_t *)addr = value;
328
329 return 0;
330}
331
332/* Dump the name and contents of a register to a string.
333 *
334 * Returns: The number of bytes written.
335 */
336
337int state_dump_reg(char * dest, int max, enum state_reg_t reg) {
338 const char const * name = register_names[reg];
339 switch (state_reg_size(reg)) {
340 case 1:
341 return snprintf(dest, max, "%.4s: 0x%02x\n",
342 name, (unsigned int)state_get_reg(reg));
343 case 2:
344 return snprintf(dest, max, "%.4s: 0x%04x\n",
345 name, (unsigned int)state_get_reg(reg));
346 case 4:
347 return snprintf(dest, max, "%.4s: 0x%08x\n",
348 name, (unsigned int)state_get_reg(reg));
349 case 8: {
350 uint64_t v = state_get_reg(reg);
351 return snprintf(dest, max, "%.4s: 0x%08x%08x\n",
352 name, (unsigned int)(v>>32), (unsigned int)v);
353 }
354 default:
355 return 0;
356 }
357}
358
359int state_num_regs() {
360 check_smm_type();
361
362 if (smm_type == SMM_TYPE_32)
363 return MAX_REG_LEGACY;
364 else if (smm_type == SMM_TYPE_64)
365 return MAX_REG_AMD64;
366 return 0;
367}
This page took 0.02993 seconds and 4 git commands to generate.