]>
Commit | Line | Data |
---|---|---|
4e3ef36b JW |
1 | /* stub.c |
2 | * GDB stub system manipultion code. | |
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 <stdint.h> | |
36 | #include <msr.h> | |
37 | #include <output.h> | |
38 | #include <state.h> | |
39 | #include <demap.h> | |
40 | #include <paging.h> | |
41 | #include <reg-x86.h> | |
42 | #include "proto.h" | |
43 | #include "stub.h" | |
44 | ||
45 | void send_stop_packet(); | |
46 | ||
47 | uint16_t saved_db_entry; | |
48 | uint64_t saved_db_entry_location; | |
49 | enum operating_mode step_start_mode; | |
50 | uint64_t saved_cs_base; | |
51 | uint64_t saved_eflags; | |
52 | uint64_t saved_cs_limit; | |
53 | uint64_t saved_cs_attrib; | |
54 | uint64_t saved_ss_base; | |
55 | uint64_t saved_ss_limit; | |
56 | uint64_t saved_ss_attrib; | |
57 | ||
58 | #define P64(x) (uint32_t)((x)>>32), (uint32_t)(x) | |
59 | ||
60 | static const enum state_reg_t regs32 [] = { | |
61 | STATE_REG_EAX, STATE_REG_ECX, STATE_REG_EDX, STATE_REG_EBX, | |
62 | STATE_REG_ESP, STATE_REG_EBP, STATE_REG_ESI, STATE_REG_EDI, | |
63 | STATE_REG_EIP, STATE_REG_EFLAGS, STATE_REG_CS, STATE_REG_SS, | |
64 | STATE_REG_DS, STATE_REG_ES, STATE_REG_FS, STATE_REG_GS | |
65 | }; | |
66 | ||
67 | static enum run_mode run_mode; | |
68 | ||
69 | void set_run_mode(enum run_mode mode) { | |
70 | run_mode = mode; | |
71 | } | |
72 | ||
73 | int gdb_post_smi_hook() { | |
74 | uint64_t r, entry; | |
75 | uint32_t * ptr; | |
76 | uint16_t * entry_v; | |
77 | enum operating_mode m = get_operating_mode(); | |
78 | ||
79 | ||
80 | switch (run_mode) { | |
81 | case RM_STOPPED: | |
82 | /* Until we have a better way of handling this - spin in | |
83 | * a loop. */ | |
84 | return 1; | |
85 | ||
86 | case RM_STEPPING: | |
87 | /* First, set RFLAGS.TF */ | |
88 | r = state_get_reg(STATE_REG_EFLAGS); | |
89 | saved_eflags = r; | |
90 | r |= EFLAGS_TF; | |
91 | /* We also don't want to deal with interrupts. */ | |
92 | r &= ~EFLAGS_IF; | |
93 | state_set_reg(STATE_REG_EFLAGS, r); | |
94 | ||
95 | /* Track down the handler for a debug exception */ | |
96 | r = state_get_reg(STATE_REG_IDT_LIMIT); | |
97 | r = state_get_reg(STATE_REG_IDT_BASE); | |
98 | if (m == LONG_64BIT || m == LONG_COMPAT) { | |
99 | /* 64-bit. Index up 16 bytes to get to the | |
100 | * debug trap descriptor. */ | |
101 | ptr = demap(r + 16); | |
102 | entry = (*ptr) & 0xFFFF; | |
103 | ptr = demap(r + 20); | |
104 | entry |= (*ptr) & 0xFFFF0000; | |
105 | ptr = demap(r + 24); | |
106 | entry |= (uint64_t)(*ptr) << 32; | |
107 | } else { | |
108 | /* Assume 32-bit for now. */ | |
109 | ptr = demap(r + 8); | |
110 | entry = (*ptr) & 0xFFFF; | |
111 | ptr = demap(r + 12); | |
112 | entry |= (*ptr) & 0xFFFF0000; | |
113 | } | |
114 | ||
115 | outputf("entry is at %08x %08x", (uint32_t)(entry>>32), (uint32_t)entry); | |
116 | /* MAGIC_BREAK */ | |
117 | saved_db_entry_location = demap_phys(entry); | |
118 | entry_v = p2v(saved_db_entry_location); | |
119 | outputf("entry_v mapped to %08x", entry_v); | |
120 | if (!entry_v) { | |
121 | run_mode = RM_UNENCUMBERED; break; | |
122 | } | |
123 | saved_db_entry = *entry_v; | |
124 | *entry_v = 0xB2E6; /* "out %al, $0xb2" */ | |
125 | ||
126 | step_start_mode = m; | |
127 | ||
128 | /* Turn off the safety */ | |
129 | WRMSR(0xc0010054, 0x8002); | |
130 | outputf("Have fun!"); | |
131 | ||
132 | saved_cs_base = state_get_reg(STATE_REG_CS_BASE); | |
133 | saved_cs_limit = state_get_reg(STATE_REG_CS_LIMIT); | |
134 | saved_cs_attrib = state_get_reg(STATE_REG_CS_ATTRIB); | |
135 | saved_ss_base = state_get_reg(STATE_REG_SS_BASE); | |
136 | saved_ss_limit = state_get_reg(STATE_REG_SS_LIMIT); | |
137 | saved_ss_attrib = state_get_reg(STATE_REG_SS_ATTRIB); | |
138 | ||
139 | break; | |
140 | ||
141 | case RM_CONTINUING: | |
142 | /* There may be breakpoints. Fall through to | |
143 | * "unencumbered" for now. */ | |
144 | ||
145 | case RM_UNENCUMBERED: | |
146 | /* Nada. */ | |
147 | return 0; | |
148 | ||
149 | default: | |
150 | break; | |
151 | } | |
152 | ||
153 | return 0; | |
154 | } | |
155 | ||
156 | #define READ_QWORD(pa) ({ uint64_t * p = demap(pa); if (!p) goto fail; outputf("pq %08x %08x", (uint32_t)((*p)>>32), (uint32_t)(*p)); *p; }) | |
157 | #define READ_WORD(pa) ({ uint16_t * p = demap(pa); if (!p) goto fail; outputf("p %04x", (*p)); *p; }) | |
158 | ||
159 | static void gdb_unmangle_stepped_system() { | |
160 | uint64_t r; | |
161 | uint16_t * entry_v; | |
162 | ||
163 | /* The unexpected breakpoint has caused an interrupt stack to be | |
164 | * built, which we must get rid of. */ | |
165 | ||
166 | if (step_start_mode == LONG_64BIT) { | |
167 | r = state_get_reg(STATE_REG_RSP); | |
168 | state_set_reg(STATE_REG_RIP, READ_QWORD(r)); | |
169 | state_set_reg(STATE_REG_CS, READ_WORD(r+8)); | |
170 | state_set_reg(STATE_REG_RFLAGS, READ_QWORD(r+16)); | |
171 | state_set_reg(STATE_REG_RSP, READ_QWORD(r+24)); | |
172 | state_set_reg(STATE_REG_SS, READ_WORD(r+32)); | |
173 | } else if (step_start_mode == LONG_COMPAT) { | |
174 | r = state_get_reg(STATE_REG_RSP); | |
175 | state_set_reg(STATE_REG_RIP, READ_QWORD(r) & 0xFFFFFFFF); | |
176 | state_set_reg(STATE_REG_CS, READ_WORD(r+8)); | |
177 | state_set_reg(STATE_REG_RFLAGS, READ_QWORD(r+16) & 0xFFFFFFFF); | |
178 | state_set_reg(STATE_REG_RSP, READ_QWORD(r+24) & 0xFFFFFFFF); | |
179 | state_set_reg(STATE_REG_SS, READ_WORD(r+32)); | |
180 | } | |
181 | entry_v = p2v(saved_db_entry_location); | |
182 | *entry_v = saved_db_entry; | |
183 | ||
184 | state_set_reg(STATE_REG_CS_BASE, saved_cs_base); | |
185 | state_set_reg(STATE_REG_CS_LIMIT, saved_cs_limit); | |
186 | state_set_reg(STATE_REG_CS_ATTRIB, saved_cs_attrib); | |
187 | state_set_reg(STATE_REG_SS_BASE, saved_ss_base); | |
188 | state_set_reg(STATE_REG_SS_LIMIT, saved_ss_limit); | |
189 | state_set_reg(STATE_REG_SS_ATTRIB, saved_ss_attrib); | |
190 | state_set_reg(STATE_REG_EFLAGS, saved_eflags); | |
191 | ||
192 | /* Put us back in "stopped" mode, until the GDB server gets around | |
193 | * to handling the step. | |
194 | */ | |
195 | ||
196 | reset_operating_mode_memo(); | |
197 | run_mode = RM_STOPPED; | |
198 | ||
199 | send_stop_packet(); | |
200 | return; | |
201 | ||
202 | fail: | |
203 | outputf("ERROR: Failed to restore state!"); | |
204 | while(1); | |
205 | } | |
206 | ||
207 | void gdb_pre_smi_hook() { | |
208 | enum operating_mode m = get_operating_mode(); | |
209 | uint64_t ip, break_phys; | |
210 | ||
211 | if (run_mode == RM_STEPPING) { | |
212 | /* Oh hey, we are probably stopped on a breakpoint. | |
213 | * Let's check. */ | |
214 | ip = state_get_reg( | |
215 | (m == LONG_64BIT) | |
216 | ? STATE_REG_RIP : STATE_REG_EIP | |
217 | ); | |
218 | break_phys = demap_phys(ip); | |
219 | ||
220 | if ((break_phys != saved_db_entry_location) | |
221 | && (break_phys != saved_db_entry_location + 2)) { | |
222 | /* Some other event caused us to enter SMM. We'll deal | |
223 | * with the single step when we *actually* get to | |
224 | * the breakpoint. */ | |
225 | return; | |
226 | } | |
227 | ||
228 | /* Great. Now we have caused a debug exception. | |
229 | * | |
230 | * "Your problems just got worse. Think: what have you done?" | |
231 | * | |
232 | * We don't want the running system to know that anything | |
233 | * ever happened, so we manually unwind the stack and undo | |
234 | * everything that happened. | |
235 | */ | |
236 | ||
237 | gdb_unmangle_stepped_system(); | |
238 | } | |
239 | } | |
240 | ||
241 | ||
242 | void read_registers_32 (char * buf) { | |
243 | int i, r = 0, offset = 0, size = 0; | |
244 | ||
245 | /* Dump registers to buffer. */ | |
246 | ||
247 | for (i = 0; i < (sizeof(regs32) / sizeof(enum state_reg_t)); i++) { | |
248 | enum state_reg_t reg = regs32[i]; | |
249 | r = state_get_reg(reg); | |
250 | //size = state_reg_size(reg); | |
251 | size = 4; | |
252 | enhexificate(&r, buf + offset, size); | |
253 | offset += (size * 2); | |
254 | } | |
255 | ||
256 | /* XXX: The rest of the buffer "should be" filled with floating point | |
257 | stuff. We'll worry about that later. */ | |
258 | } | |
259 | ||
260 | void write_registers_32 (char * buf) { | |
261 | //int size = 0, i; | |
262 | ||
263 | ||
264 | uint32_t *ubuf = (uint32_t *)buf; | |
265 | ||
266 | state_set_reg(STATE_REG_EAX, ubuf[0]); | |
267 | state_set_reg(STATE_REG_ECX, ubuf[1]); | |
268 | state_set_reg(STATE_REG_EDX, ubuf[2]); | |
269 | state_set_reg(STATE_REG_EBX, ubuf[3]); | |
270 | state_set_reg(STATE_REG_ESP, ubuf[4]); | |
271 | state_set_reg(STATE_REG_EBP, ubuf[5]); | |
272 | state_set_reg(STATE_REG_ESI, ubuf[6]); | |
273 | state_set_reg(STATE_REG_EDI, ubuf[7]); | |
274 | ||
275 | state_set_reg(STATE_REG_EIP, ubuf[8]); | |
276 | state_set_reg(STATE_REG_EFLAGS, ubuf[9]); | |
277 | state_set_reg(STATE_REG_CS, ubuf[10]); | |
278 | state_set_reg(STATE_REG_SS, ubuf[11]); | |
279 | state_set_reg(STATE_REG_DS, ubuf[12]); | |
280 | state_set_reg(STATE_REG_ES, ubuf[13]); | |
281 | state_set_reg(STATE_REG_FS, ubuf[14]); | |
282 | state_set_reg(STATE_REG_GS, ubuf[15]); | |
283 | ||
284 | /* XXX: Again, need to deal with floating point. */ | |
285 | } |