]> Joshua Wise's Git repositories - netwatch.git/blob - lib/state.c
Add support for the power button SMI.
[netwatch.git] / lib / state.c
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
47 static 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
77 static 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
145 static 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
212 static 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
218 static 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
244 static 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
259 enum 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
271 uint64_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
296 int 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
311 int 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
337 int 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
359 int 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.046653 seconds and 4 git commands to generate.