/root/src/xen/xen/arch/x86/pv/emul-gate-op.c
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * arch/x86/pv/emul-gate-op.c |
3 | | * |
4 | | * Emulate gate op for PV guests |
5 | | * |
6 | | * Modifications to Linux original are copyright (c) 2002-2004, K A Fraser |
7 | | * |
8 | | * This program is free software; you can redistribute it and/or modify |
9 | | * it under the terms of the GNU General Public License as published by |
10 | | * the Free Software Foundation; either version 2 of the License, or |
11 | | * (at your option) any later version. |
12 | | * |
13 | | * This program is distributed in the hope that it will be useful, |
14 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | | * GNU General Public License for more details. |
17 | | * |
18 | | * You should have received a copy of the GNU General Public License |
19 | | * along with this program; If not, see <http://www.gnu.org/licenses/>. |
20 | | */ |
21 | | |
22 | | #include <xen/errno.h> |
23 | | #include <xen/event.h> |
24 | | #include <xen/guest_access.h> |
25 | | #include <xen/iocap.h> |
26 | | #include <xen/spinlock.h> |
27 | | #include <xen/trace.h> |
28 | | |
29 | | #include <asm/apic.h> |
30 | | #include <asm/debugreg.h> |
31 | | #include <asm/hpet.h> |
32 | | #include <asm/hypercall.h> |
33 | | #include <asm/mc146818rtc.h> |
34 | | #include <asm/p2m.h> |
35 | | #include <asm/pv/traps.h> |
36 | | #include <asm/shared.h> |
37 | | #include <asm/traps.h> |
38 | | #include <asm/x86_emulate.h> |
39 | | |
40 | | #include <xsm/xsm.h> |
41 | | |
42 | | #include "emulate.h" |
43 | | |
44 | | /* Override macros from asm/page.h to make them work with mfn_t */ |
45 | | #undef mfn_to_page |
46 | | #define mfn_to_page(mfn) __mfn_to_page(mfn_x(mfn)) |
47 | | #undef page_to_mfn |
48 | | #define page_to_mfn(pg) _mfn(__page_to_mfn(pg)) |
49 | | |
50 | | static int read_gate_descriptor(unsigned int gate_sel, |
51 | | const struct vcpu *v, |
52 | | unsigned int *sel, |
53 | | unsigned long *off, |
54 | | unsigned int *ar) |
55 | 0 | { |
56 | 0 | struct desc_struct desc; |
57 | 0 | const struct desc_struct *pdesc; |
58 | 0 |
|
59 | 0 | pdesc = (const struct desc_struct *) |
60 | 0 | (!(gate_sel & 4) ? GDT_VIRT_START(v) : LDT_VIRT_START(v)) |
61 | 0 | + (gate_sel >> 3); |
62 | 0 | if ( (gate_sel < 4) || |
63 | 0 | ((gate_sel >= FIRST_RESERVED_GDT_BYTE) && !(gate_sel & 4)) || |
64 | 0 | __get_user(desc, pdesc) ) |
65 | 0 | return 0; |
66 | 0 |
|
67 | 0 | *sel = (desc.a >> 16) & 0x0000fffc; |
68 | 0 | *off = (desc.a & 0x0000ffff) | (desc.b & 0xffff0000); |
69 | 0 | *ar = desc.b & 0x0000ffff; |
70 | 0 |
|
71 | 0 | /* |
72 | 0 | * check_descriptor() clears the DPL field and stores the |
73 | 0 | * guest requested DPL in the selector's RPL field. |
74 | 0 | */ |
75 | 0 | if ( *ar & _SEGMENT_DPL ) |
76 | 0 | return 0; |
77 | 0 | *ar |= (desc.a >> (16 - 13)) & _SEGMENT_DPL; |
78 | 0 |
|
79 | 0 | if ( !is_pv_32bit_vcpu(v) ) |
80 | 0 | { |
81 | 0 | if ( (*ar & 0x1f00) != 0x0c00 || |
82 | 0 | (gate_sel >= FIRST_RESERVED_GDT_BYTE - 8 && !(gate_sel & 4)) || |
83 | 0 | __get_user(desc, pdesc + 1) || |
84 | 0 | (desc.b & 0x1f00) ) |
85 | 0 | return 0; |
86 | 0 |
|
87 | 0 | *off |= (unsigned long)desc.a << 32; |
88 | 0 | return 1; |
89 | 0 | } |
90 | 0 |
|
91 | 0 | switch ( *ar & 0x1f00 ) |
92 | 0 | { |
93 | 0 | case 0x0400: |
94 | 0 | *off &= 0xffff; |
95 | 0 | break; |
96 | 0 | case 0x0c00: |
97 | 0 | break; |
98 | 0 | default: |
99 | 0 | return 0; |
100 | 0 | } |
101 | 0 |
|
102 | 0 | return 1; |
103 | 0 | } |
104 | | |
105 | | static inline bool check_stack_limit(unsigned int ar, unsigned int limit, |
106 | | unsigned int esp, unsigned int decr) |
107 | 0 | { |
108 | 0 | return (((esp - decr) < (esp - 1)) && |
109 | 0 | (!(ar & _SEGMENT_EC) ? (esp - 1) <= limit : (esp - decr) > limit)); |
110 | 0 | } |
111 | | |
112 | | struct gate_op_ctxt { |
113 | | struct x86_emulate_ctxt ctxt; |
114 | | struct { |
115 | | unsigned long base, limit; |
116 | | } cs; |
117 | | bool insn_fetch; |
118 | | }; |
119 | | |
120 | | static int read_mem(enum x86_segment seg, unsigned long offset, void *p_data, |
121 | | unsigned int bytes, struct x86_emulate_ctxt *ctxt) |
122 | 0 | { |
123 | 0 | const struct gate_op_ctxt *goc = |
124 | 0 | container_of(ctxt, struct gate_op_ctxt, ctxt); |
125 | 0 | unsigned int rc = bytes, sel = 0; |
126 | 0 | unsigned long addr = offset, limit = 0; |
127 | 0 |
|
128 | 0 | switch ( seg ) |
129 | 0 | { |
130 | 0 | case x86_seg_cs: |
131 | 0 | addr += goc->cs.base; |
132 | 0 | limit = goc->cs.limit; |
133 | 0 | break; |
134 | 0 | case x86_seg_ds: |
135 | 0 | sel = read_sreg(ds); |
136 | 0 | break; |
137 | 0 | case x86_seg_es: |
138 | 0 | sel = read_sreg(es); |
139 | 0 | break; |
140 | 0 | case x86_seg_fs: |
141 | 0 | sel = read_sreg(fs); |
142 | 0 | break; |
143 | 0 | case x86_seg_gs: |
144 | 0 | sel = read_sreg(gs); |
145 | 0 | break; |
146 | 0 | case x86_seg_ss: |
147 | 0 | sel = ctxt->regs->ss; |
148 | 0 | break; |
149 | 0 | default: |
150 | 0 | return X86EMUL_UNHANDLEABLE; |
151 | 0 | } |
152 | 0 | if ( sel ) |
153 | 0 | { |
154 | 0 | unsigned int ar; |
155 | 0 |
|
156 | 0 | ASSERT(!goc->insn_fetch); |
157 | 0 | if ( !pv_emul_read_descriptor(sel, current, &addr, &limit, &ar, 0) || |
158 | 0 | !(ar & _SEGMENT_S) || |
159 | 0 | !(ar & _SEGMENT_P) || |
160 | 0 | ((ar & _SEGMENT_CODE) && !(ar & _SEGMENT_WR)) ) |
161 | 0 | return X86EMUL_UNHANDLEABLE; |
162 | 0 | addr += offset; |
163 | 0 | } |
164 | 0 | else if ( seg != x86_seg_cs ) |
165 | 0 | return X86EMUL_UNHANDLEABLE; |
166 | 0 |
|
167 | 0 | /* We don't mean to emulate any branches. */ |
168 | 0 | if ( limit < bytes - 1 || offset > limit - bytes + 1 ) |
169 | 0 | return X86EMUL_UNHANDLEABLE; |
170 | 0 |
|
171 | 0 | addr = (uint32_t)addr; |
172 | 0 |
|
173 | 0 | if ( (rc = __copy_from_user(p_data, (void *)addr, bytes)) ) |
174 | 0 | { |
175 | 0 | /* |
176 | 0 | * TODO: This should report PFEC_insn_fetch when goc->insn_fetch && |
177 | 0 | * cpu_has_nx, but we'd then need a "fetch" variant of |
178 | 0 | * __copy_from_user() respecting NX, SMEP, and protection keys. |
179 | 0 | */ |
180 | 0 | x86_emul_pagefault(0, addr + bytes - rc, ctxt); |
181 | 0 | return X86EMUL_EXCEPTION; |
182 | 0 | } |
183 | 0 |
|
184 | 0 | return X86EMUL_OKAY; |
185 | 0 | } |
186 | | |
187 | | void pv_emulate_gate_op(struct cpu_user_regs *regs) |
188 | 0 | { |
189 | 0 | struct vcpu *v = current; |
190 | 0 | unsigned int sel, ar, dpl, nparm, insn_len; |
191 | 0 | struct gate_op_ctxt ctxt = { .ctxt.regs = regs, .insn_fetch = true }; |
192 | 0 | struct x86_emulate_state *state; |
193 | 0 | unsigned long off, base, limit; |
194 | 0 | uint16_t opnd_sel = 0; |
195 | 0 | int jump = -1, rc = X86EMUL_OKAY; |
196 | 0 |
|
197 | 0 | /* Check whether this fault is due to the use of a call gate. */ |
198 | 0 | if ( !read_gate_descriptor(regs->error_code, v, &sel, &off, &ar) || |
199 | 0 | (((ar >> 13) & 3) < (regs->cs & 3)) || |
200 | 0 | ((ar & _SEGMENT_TYPE) != 0xc00) ) |
201 | 0 | { |
202 | 0 | pv_inject_hw_exception(TRAP_gp_fault, regs->error_code); |
203 | 0 | return; |
204 | 0 | } |
205 | 0 | if ( !(ar & _SEGMENT_P) ) |
206 | 0 | { |
207 | 0 | pv_inject_hw_exception(TRAP_no_segment, regs->error_code); |
208 | 0 | return; |
209 | 0 | } |
210 | 0 | dpl = (ar >> 13) & 3; |
211 | 0 | nparm = ar & 0x1f; |
212 | 0 |
|
213 | 0 | /* |
214 | 0 | * Decode instruction (and perhaps operand) to determine RPL, |
215 | 0 | * whether this is a jump or a call, and the call return offset. |
216 | 0 | */ |
217 | 0 | if ( !pv_emul_read_descriptor(regs->cs, v, &ctxt.cs.base, &ctxt.cs.limit, |
218 | 0 | &ar, 0) || |
219 | 0 | !(ar & _SEGMENT_S) || |
220 | 0 | !(ar & _SEGMENT_P) || |
221 | 0 | !(ar & _SEGMENT_CODE) ) |
222 | 0 | { |
223 | 0 | pv_inject_hw_exception(TRAP_gp_fault, regs->error_code); |
224 | 0 | return; |
225 | 0 | } |
226 | 0 |
|
227 | 0 | ctxt.ctxt.addr_size = ar & _SEGMENT_DB ? 32 : 16; |
228 | 0 | /* Leave zero in ctxt.ctxt.sp_size, as it's not needed for decoding. */ |
229 | 0 | state = x86_decode_insn(&ctxt.ctxt, read_mem); |
230 | 0 | ctxt.insn_fetch = false; |
231 | 0 | if ( IS_ERR_OR_NULL(state) ) |
232 | 0 | { |
233 | 0 | if ( PTR_ERR(state) == -X86EMUL_EXCEPTION ) |
234 | 0 | pv_inject_event(&ctxt.ctxt.event); |
235 | 0 | else |
236 | 0 | pv_inject_hw_exception(TRAP_gp_fault, regs->error_code); |
237 | 0 | return; |
238 | 0 | } |
239 | 0 |
|
240 | 0 | switch ( ctxt.ctxt.opcode ) |
241 | 0 | { |
242 | 0 | unsigned int modrm_345; |
243 | 0 |
|
244 | 0 | case 0xea: |
245 | 0 | ++jump; |
246 | 0 | /* fall through */ |
247 | 0 | case 0x9a: |
248 | 0 | ++jump; |
249 | 0 | opnd_sel = x86_insn_immediate(state, 1); |
250 | 0 | break; |
251 | 0 | case 0xff: |
252 | 0 | if ( x86_insn_modrm(state, NULL, &modrm_345) >= 3 ) |
253 | 0 | break; |
254 | 0 | switch ( modrm_345 & 7 ) |
255 | 0 | { |
256 | 0 | enum x86_segment seg; |
257 | 0 |
|
258 | 0 | case 5: |
259 | 0 | ++jump; |
260 | 0 | /* fall through */ |
261 | 0 | case 3: |
262 | 0 | ++jump; |
263 | 0 | base = x86_insn_operand_ea(state, &seg); |
264 | 0 | rc = read_mem(seg, base + (x86_insn_opsize(state) >> 3), |
265 | 0 | &opnd_sel, sizeof(opnd_sel), &ctxt.ctxt); |
266 | 0 | break; |
267 | 0 | } |
268 | 0 | break; |
269 | 0 | } |
270 | 0 |
|
271 | 0 | insn_len = x86_insn_length(state, &ctxt.ctxt); |
272 | 0 | x86_emulate_free_state(state); |
273 | 0 |
|
274 | 0 | if ( rc == X86EMUL_EXCEPTION ) |
275 | 0 | { |
276 | 0 | pv_inject_event(&ctxt.ctxt.event); |
277 | 0 | return; |
278 | 0 | } |
279 | 0 |
|
280 | 0 | if ( rc != X86EMUL_OKAY || |
281 | 0 | jump < 0 || |
282 | 0 | (opnd_sel & ~3) != regs->error_code || |
283 | 0 | dpl < (opnd_sel & 3) ) |
284 | 0 | { |
285 | 0 | pv_inject_hw_exception(TRAP_gp_fault, regs->error_code); |
286 | 0 | return; |
287 | 0 | } |
288 | 0 |
|
289 | 0 | if ( !pv_emul_read_descriptor(sel, v, &base, &limit, &ar, 0) || |
290 | 0 | !(ar & _SEGMENT_S) || |
291 | 0 | !(ar & _SEGMENT_CODE) || |
292 | 0 | (!jump || (ar & _SEGMENT_EC) ? |
293 | 0 | ((ar >> 13) & 3) > (regs->cs & 3) : |
294 | 0 | ((ar >> 13) & 3) != (regs->cs & 3)) ) |
295 | 0 | { |
296 | 0 | pv_inject_hw_exception(TRAP_gp_fault, sel); |
297 | 0 | return; |
298 | 0 | } |
299 | 0 | if ( !(ar & _SEGMENT_P) ) |
300 | 0 | { |
301 | 0 | pv_inject_hw_exception(TRAP_no_segment, sel); |
302 | 0 | return; |
303 | 0 | } |
304 | 0 | if ( off > limit ) |
305 | 0 | { |
306 | 0 | pv_inject_hw_exception(TRAP_gp_fault, 0); |
307 | 0 | return; |
308 | 0 | } |
309 | 0 |
|
310 | 0 | if ( !jump ) |
311 | 0 | { |
312 | 0 | unsigned int ss, esp, *stkp; |
313 | 0 | int rc; |
314 | 0 | #define push(item) do \ |
315 | 0 | { \ |
316 | 0 | --stkp; \ |
317 | 0 | esp -= 4; \ |
318 | 0 | rc = __put_user(item, stkp); \ |
319 | 0 | if ( rc ) \ |
320 | 0 | { \ |
321 | 0 | pv_inject_page_fault(PFEC_write_access, \ |
322 | 0 | (unsigned long)(stkp + 1) - rc); \ |
323 | 0 | return; \ |
324 | 0 | } \ |
325 | 0 | } while ( 0 ) |
326 | 0 |
|
327 | 0 | if ( ((ar >> 13) & 3) < (regs->cs & 3) ) |
328 | 0 | { |
329 | 0 | sel |= (ar >> 13) & 3; |
330 | 0 | /* Inner stack known only for kernel ring. */ |
331 | 0 | if ( (sel & 3) != GUEST_KERNEL_RPL(v->domain) ) |
332 | 0 | { |
333 | 0 | pv_inject_hw_exception(TRAP_gp_fault, regs->error_code); |
334 | 0 | return; |
335 | 0 | } |
336 | 0 | esp = v->arch.pv_vcpu.kernel_sp; |
337 | 0 | ss = v->arch.pv_vcpu.kernel_ss; |
338 | 0 | if ( (ss & 3) != (sel & 3) || |
339 | 0 | !pv_emul_read_descriptor(ss, v, &base, &limit, &ar, 0) || |
340 | 0 | ((ar >> 13) & 3) != (sel & 3) || |
341 | 0 | !(ar & _SEGMENT_S) || |
342 | 0 | (ar & _SEGMENT_CODE) || |
343 | 0 | !(ar & _SEGMENT_WR) ) |
344 | 0 | { |
345 | 0 | pv_inject_hw_exception(TRAP_invalid_tss, ss & ~3); |
346 | 0 | return; |
347 | 0 | } |
348 | 0 | if ( !(ar & _SEGMENT_P) || |
349 | 0 | !check_stack_limit(ar, limit, esp, (4 + nparm) * 4) ) |
350 | 0 | { |
351 | 0 | pv_inject_hw_exception(TRAP_stack_error, ss & ~3); |
352 | 0 | return; |
353 | 0 | } |
354 | 0 | stkp = (unsigned int *)(unsigned long)((unsigned int)base + esp); |
355 | 0 | if ( !compat_access_ok(stkp - 4 - nparm, 16 + nparm * 4) ) |
356 | 0 | { |
357 | 0 | pv_inject_hw_exception(TRAP_gp_fault, regs->error_code); |
358 | 0 | return; |
359 | 0 | } |
360 | 0 | push(regs->ss); |
361 | 0 | push(regs->rsp); |
362 | 0 | if ( nparm ) |
363 | 0 | { |
364 | 0 | const unsigned int *ustkp; |
365 | 0 |
|
366 | 0 | if ( !pv_emul_read_descriptor(regs->ss, v, &base, |
367 | 0 | &limit, &ar, 0) || |
368 | 0 | ((ar >> 13) & 3) != (regs->cs & 3) || |
369 | 0 | !(ar & _SEGMENT_S) || |
370 | 0 | (ar & _SEGMENT_CODE) || |
371 | 0 | !(ar & _SEGMENT_WR) || |
372 | 0 | !check_stack_limit(ar, limit, esp + nparm * 4, nparm * 4) ) |
373 | 0 | return pv_inject_hw_exception(TRAP_gp_fault, regs->error_code); |
374 | 0 | ustkp = (unsigned int *)(unsigned long) |
375 | 0 | ((unsigned int)base + regs->esp + nparm * 4); |
376 | 0 | if ( !compat_access_ok(ustkp - nparm, 0 + nparm * 4) ) |
377 | 0 | { |
378 | 0 | pv_inject_hw_exception(TRAP_gp_fault, regs->error_code); |
379 | 0 | return; |
380 | 0 | } |
381 | 0 | do |
382 | 0 | { |
383 | 0 | unsigned int parm; |
384 | 0 |
|
385 | 0 | --ustkp; |
386 | 0 | rc = __get_user(parm, ustkp); |
387 | 0 | if ( rc ) |
388 | 0 | { |
389 | 0 | pv_inject_page_fault(0, (unsigned long)(ustkp + 1) - rc); |
390 | 0 | return; |
391 | 0 | } |
392 | 0 | push(parm); |
393 | 0 | } while ( --nparm ); |
394 | 0 | } |
395 | 0 | } |
396 | 0 | else |
397 | 0 | { |
398 | 0 | sel |= (regs->cs & 3); |
399 | 0 | esp = regs->rsp; |
400 | 0 | ss = regs->ss; |
401 | 0 | if ( !pv_emul_read_descriptor(ss, v, &base, &limit, &ar, 0) || |
402 | 0 | ((ar >> 13) & 3) != (sel & 3) ) |
403 | 0 | { |
404 | 0 | pv_inject_hw_exception(TRAP_gp_fault, regs->error_code); |
405 | 0 | return; |
406 | 0 | } |
407 | 0 | if ( !check_stack_limit(ar, limit, esp, 2 * 4) ) |
408 | 0 | { |
409 | 0 | pv_inject_hw_exception(TRAP_stack_error, 0); |
410 | 0 | return; |
411 | 0 | } |
412 | 0 | stkp = (unsigned int *)(unsigned long)((unsigned int)base + esp); |
413 | 0 | if ( !compat_access_ok(stkp - 2, 2 * 4) ) |
414 | 0 | { |
415 | 0 | pv_inject_hw_exception(TRAP_gp_fault, regs->error_code); |
416 | 0 | return; |
417 | 0 | } |
418 | 0 | } |
419 | 0 | push(regs->cs); |
420 | 0 | push(regs->rip + insn_len); |
421 | 0 | #undef push |
422 | 0 | regs->rsp = esp; |
423 | 0 | regs->ss = ss; |
424 | 0 | } |
425 | 0 | else |
426 | 0 | sel |= (regs->cs & 3); |
427 | 0 |
|
428 | 0 | regs->cs = sel; |
429 | 0 | pv_emul_instruction_done(regs, off); |
430 | 0 | } |
431 | | |
432 | | /* |
433 | | * Local variables: |
434 | | * mode: C |
435 | | * c-file-style: "BSD" |
436 | | * c-basic-offset: 4 |
437 | | * tab-width: 4 |
438 | | * indent-tabs-mode: nil |
439 | | * End: |
440 | | */ |