2 * SMM saved state manipulation functions.
3 * NetWatch system management mode administration console
5 * Copyright 2009, Google Inc.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
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
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.
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.
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
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,
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,
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
75 #define MAX_REG_LEGACY (sizeof(offset_table_legacy)/sizeof(uint32_t))
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,
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,
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,
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,
142 [STATE_REG_EFER] = 0xfed0 | SZ_QWORD
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",
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",
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",
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",
207 [STATE_REG_EFER] = "EFER"
210 #define MAX_REG_AMD64 (sizeof(offset_table_amd64)/sizeof(uint32_t))
212 static enum smm_type smm_type = SMM_TYPE_64;
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.
218 static void check_smm_type (void) {
220 struct cpuid_result r;
222 if (smm_type != SMM_TYPE_UNKNOWN)
225 cpuid(0x80000000, &r);
227 if (r.eax < 0x80000001) {
228 smm_type = SMM_TYPE_32;
232 cpuid(0x80000001, &r);
234 if (r.edx & 0x20000000) {
235 smm_type = SMM_TYPE_64;
237 smm_type = SMM_TYPE_32;
241 /* Get the offset of a register, by looking up in the appropriate table.
244 static unsigned long get_offset(enum state_reg_t reg) {
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];
256 /* Which variety of processor are we running on?
259 enum smm_type state_get_type (void) {
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.
271 uint64_t state_get_reg (enum state_reg_t reg) {
272 unsigned long offset = get_offset(reg);
279 addr = (void *)((offset & OFFSET_MASK) + 0xA0000);
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;
288 value = *(uint64_t *)addr;
293 /* Get the size of a register, extracted from the saved state offset table.
296 int state_reg_size (enum state_reg_t reg) {
297 unsigned long offset = get_offset(reg);
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;
306 /* Modify a saved register.
308 * The same caveat about aseg's location applies here as well.
311 int state_set_reg (enum state_reg_t reg, uint64_t value) {
312 unsigned long offset = get_offset(reg);
318 addr = (void *)((offset & OFFSET_MASK) + 0xA0000);
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;
327 *(uint64_t *)addr = value;
332 /* Dump the name and contents of a register to a string.
334 * Returns: The number of bytes written.
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)) {
341 return snprintf(dest, max, "%.4s: 0x%02x\n",
342 name, (unsigned int)state_get_reg(reg));
344 return snprintf(dest, max, "%.4s: 0x%04x\n",
345 name, (unsigned int)state_get_reg(reg));
347 return snprintf(dest, max, "%.4s: 0x%08x\n",
348 name, (unsigned int)state_get_reg(reg));
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);