]> Joshua Wise's Git repositories - netwatch.git/blob - lib/state.c
368029b16bc9f4ed8bfa3609737c05841ab6efea
[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
38 /* Size flags. */
39 #define SZ_BYTE         0x10000000
40 #define SZ_WORD         0x20000000
41 #define SZ_DWORD        0x40000000
42 #define SZ_QWORD        0x80000000
43 #define OFFSET_MASK     0x0FFFFFFF
44
45 static const uint32_t offset_table_legacy[] = {
46         [STATE_REV]             = 0xfefc | SZ_DWORD,
47         [STATE_REG_SMBASE]      = 0xfef8 | SZ_DWORD,
48         [STATE_REG_IORESTART]   = 0xff00 | SZ_WORD,
49         [STATE_REG_HALTRESTART] = 0xff02 | SZ_WORD,
50
51         [STATE_REG_EAX]         = 0xffd0 | SZ_DWORD,
52         [STATE_REG_EBX]         = 0xffdc | SZ_DWORD,
53         [STATE_REG_ECX]         = 0xffd4 | SZ_DWORD,
54         [STATE_REG_EDX]         = 0xffd8 | SZ_DWORD,
55         [STATE_REG_ESI]         = 0xffe8 | SZ_DWORD,
56         [STATE_REG_EDI]         = 0xffec | SZ_DWORD,
57         [STATE_REG_ESP]         = 0xffe0 | SZ_DWORD,
58         [STATE_REG_EBP]         = 0xffe4 | SZ_DWORD,
59         [STATE_REG_EIP]         = 0xfff0 | SZ_DWORD,
60         [STATE_REG_EFLAGS]      = 0xfff4 | SZ_DWORD,
61         [STATE_REG_CR0]         = 0xfffc | SZ_DWORD,
62         [STATE_REG_CR3]         = 0xfff8 | SZ_DWORD,
63
64         [STATE_REG_CS]          = 0xffac | SZ_DWORD,
65         [STATE_REG_SS]          = 0xffb0 | SZ_DWORD,
66         [STATE_REG_DS]          = 0xffb4 | SZ_DWORD,
67         [STATE_REG_ES]          = 0xffa8 | SZ_DWORD,
68         [STATE_REG_FS]          = 0xffb8 | SZ_DWORD,
69         [STATE_REG_GS]          = 0xffbc | SZ_DWORD
70 };
71
72 #define MAX_REG_LEGACY (sizeof(offset_table_legacy)/sizeof(uint32_t))
73
74 static const uint32_t offset_table_amd64[] = {
75         [STATE_REV]             = 0xfefc | SZ_DWORD,
76         [STATE_REG_SMBASE]      = 0xff00 | SZ_DWORD,
77         [STATE_REG_IORESTART]   = 0xfec8 | SZ_BYTE,
78         [STATE_REG_HALTRESTART] = 0xfec9 | SZ_BYTE,
79
80         [STATE_REG_EAX]         = 0xfff8 | SZ_DWORD,
81         [STATE_REG_EBX]         = 0xffe0 | SZ_DWORD,
82         [STATE_REG_ECX]         = 0xfff0 | SZ_DWORD,
83         [STATE_REG_EDX]         = 0xffe8 | SZ_DWORD,
84         [STATE_REG_ESI]         = 0xffc8 | SZ_DWORD,
85         [STATE_REG_EDI]         = 0xffc0 | SZ_DWORD,
86         [STATE_REG_ESP]         = 0xffd8 | SZ_DWORD,
87         [STATE_REG_EBP]         = 0xffd0 | SZ_DWORD,
88         [STATE_REG_EIP]         = 0xff78 | SZ_DWORD,
89         [STATE_REG_EFLAGS]      = 0xff70 | SZ_DWORD,
90         [STATE_REG_CR0]         = 0xff58 | SZ_DWORD,
91         [STATE_REG_CR3]         = 0xff50 | SZ_DWORD,
92
93         [STATE_REG_CS]          = 0xfe10 | SZ_DWORD,
94         [STATE_REG_SS]          = 0xfe20 | SZ_DWORD,
95         [STATE_REG_DS]          = 0xfe30 | SZ_DWORD,
96         [STATE_REG_ES]          = 0xfe00 | SZ_DWORD,
97         [STATE_REG_FS]          = 0xfe40 | SZ_DWORD,
98         [STATE_REG_GS]          = 0xfe50 | SZ_DWORD,
99
100         [STATE_REG_RAX]         = 0xfff8 | SZ_QWORD,
101         [STATE_REG_RBX]         = 0xffe0 | SZ_QWORD,
102         [STATE_REG_RCX]         = 0xfff0 | SZ_QWORD,
103         [STATE_REG_RDX]         = 0xffe8 | SZ_QWORD,
104         [STATE_REG_RSI]         = 0xffc8 | SZ_QWORD,
105         [STATE_REG_RDI]         = 0xffc0 | SZ_QWORD,
106         [STATE_REG_RSP]         = 0xffd8 | SZ_QWORD,
107         [STATE_REG_RBP]         = 0xffd0 | SZ_QWORD,
108         [STATE_REG_R8]          = 0xffb8 | SZ_QWORD,
109         [STATE_REG_R9]          = 0xffb0 | SZ_QWORD,
110         [STATE_REG_R10]         = 0xffa8 | SZ_QWORD,
111         [STATE_REG_R11]         = 0xffa0 | SZ_QWORD,
112         [STATE_REG_R12]         = 0xff98 | SZ_QWORD,
113         [STATE_REG_R13]         = 0xff90 | SZ_QWORD,
114         [STATE_REG_R14]         = 0xff88 | SZ_QWORD,
115         [STATE_REG_R15]         = 0xff80 | SZ_QWORD,
116         [STATE_REG_RIP]         = 0xff78 | SZ_QWORD
117 };
118
119 #define MAX_REG_AMD64 (sizeof(offset_table_amd64)/sizeof(uint32_t))
120
121 static enum smm_type smm_type = SMM_TYPE_64;
122
123 /* Probe CPUID to figure out what kind of processor this actually is.
124  * We memoize this in 'smm_type', so cpuid only needs to happen once.
125  */
126
127 static void check_smm_type (void) {
128
129         struct cpuid_result r;
130
131         if (smm_type != SMM_TYPE_UNKNOWN)
132                 return;
133
134         cpuid(0x80000000, &r);
135
136         if (r.eax < 0x80000001) {
137                 smm_type = SMM_TYPE_32;
138                 return;
139         }
140
141         cpuid(0x80000001, &r);
142
143         if (r.edx & 0x20000000) {
144                 smm_type = SMM_TYPE_64;
145         } else {
146                 smm_type = SMM_TYPE_32;
147         }
148 }
149
150 /* Get the offset of a register, by looking up in the appropriate table.
151  */
152
153 static unsigned long get_offset(enum state_reg_t reg) {
154
155         check_smm_type();
156
157         if (smm_type == SMM_TYPE_32 && reg < MAX_REG_LEGACY)
158                 return offset_table_legacy[reg];
159         else if (smm_type == SMM_TYPE_64 && reg < MAX_REG_AMD64)
160                 return offset_table_amd64[reg];
161         else
162                 return 0;
163 }
164
165 /* Which variety of processor are we running on?
166  */
167
168 enum smm_type state_get_type (void) {
169         check_smm_type();
170         return smm_type;
171 }
172
173 /* Get a register.
174  *
175  * We assume that Aseg is always direct-mapped at 0xA0000. This may
176  * not be the case in the future, with multiple cores, but it is a
177  * safe assumption now.
178  */
179
180 uint64_t state_get_reg (enum state_reg_t reg) {
181         unsigned long offset = get_offset(reg);
182         void * addr;
183         uint64_t value;
184
185         if (!offset)
186                 return 0;
187
188         addr = (void *)((offset & OFFSET_MASK) + 0xA0000);
189
190         if (offset & SZ_BYTE)
191                 value = *(uint8_t *)addr;
192         else if (offset & SZ_WORD)
193                 value = *(uint16_t *)addr;
194         else if (offset & SZ_DWORD)
195                 value = *(uint32_t *)addr;
196         else
197                 value = *(uint64_t *)addr;
198
199         return value;
200 }
201
202 /* Get the size of a register, extracted from the saved state offset table.
203  */
204
205 int state_reg_size (enum state_reg_t reg) {
206         unsigned long offset = get_offset(reg);
207
208         if (offset & SZ_BYTE) return 1;
209         else if (offset & SZ_WORD) return 2;
210         else if (offset & SZ_DWORD) return 4;
211         else if (offset & SZ_QWORD) return 8;
212         else return 0;
213 }
214
215 /* Modify a saved register.
216  *
217  * The same caveat about aseg's location applies here as well.
218  */
219
220 int state_set_reg (enum state_reg_t reg, uint64_t value) {
221         unsigned long offset = get_offset(reg);
222         void * addr;
223
224         if (!offset)
225                 return -1;
226
227         addr = (void *)((offset & OFFSET_MASK) + 0xA0000);
228
229         if (offset & SZ_BYTE)
230                 *(uint8_t *)addr = (uint8_t) value;
231         else if (offset & SZ_WORD)
232                 *(uint16_t *)addr = (uint16_t) value;
233         else if (offset & SZ_DWORD)
234                 *(uint32_t *)addr = (uint32_t) value;
235         else
236                 *(uint64_t *)addr = value;
237
238         return 0;
239 }
This page took 0.031555 seconds and 2 git commands to generate.