]>
Commit | Line | Data |
---|---|---|
30493a03 CF |
1 | /* |
2 | * x86 segmentation related helpers (user-mode code): | |
3 | * TSS, interrupts, system calls, jumps and call/task gates, descriptors | |
4 | * | |
5 | * Copyright (c) 2003 Fabrice Bellard | |
6 | * | |
7 | * This library is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU Lesser General Public | |
9 | * License as published by the Free Software Foundation; either | |
10 | * version 2.1 of the License, or (at your option) any later version. | |
11 | * | |
12 | * This library is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * Lesser General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU Lesser General Public | |
18 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. | |
19 | */ | |
20 | ||
21 | #include "qemu/osdep.h" | |
22 | #include "cpu.h" | |
23 | #include "exec/helper-proto.h" | |
24 | #include "exec/exec-all.h" | |
25 | #include "exec/cpu_ldst.h" | |
26 | #include "tcg/helper-tcg.h" | |
27 | #include "tcg/seg_helper.h" | |
28 | ||
30493a03 CF |
29 | void helper_syscall(CPUX86State *env, int next_eip_addend) |
30 | { | |
31 | CPUState *cs = env_cpu(env); | |
32 | ||
33 | cs->exception_index = EXCP_SYSCALL; | |
34 | env->exception_is_int = 0; | |
35 | env->exception_next_eip = env->eip + next_eip_addend; | |
36 | cpu_loop_exit(cs); | |
37 | } | |
30493a03 CF |
38 | |
39 | /* | |
40 | * fake user mode interrupt. is_int is TRUE if coming from the int | |
41 | * instruction. next_eip is the env->eip value AFTER the interrupt | |
42 | * instruction. It is only relevant if is_int is TRUE or if intno | |
43 | * is EXCP_SYSCALL. | |
44 | */ | |
45 | static void do_interrupt_user(CPUX86State *env, int intno, int is_int, | |
46 | int error_code, target_ulong next_eip) | |
47 | { | |
48 | if (is_int) { | |
49 | SegmentCache *dt; | |
50 | target_ulong ptr; | |
51 | int dpl, cpl, shift; | |
52 | uint32_t e2; | |
53 | ||
54 | dt = &env->idt; | |
55 | if (env->hflags & HF_LMA_MASK) { | |
56 | shift = 4; | |
57 | } else { | |
58 | shift = 3; | |
59 | } | |
60 | ptr = dt->base + (intno << shift); | |
61 | e2 = cpu_ldl_kernel(env, ptr + 4); | |
62 | ||
63 | dpl = (e2 >> DESC_DPL_SHIFT) & 3; | |
64 | cpl = env->hflags & HF_CPL_MASK; | |
65 | /* check privilege if software int */ | |
66 | if (dpl < cpl) { | |
67 | raise_exception_err(env, EXCP0D_GPF, (intno << shift) + 2); | |
68 | } | |
69 | } | |
70 | ||
71 | /* Since we emulate only user space, we cannot do more than | |
72 | exiting the emulation with the suitable exception and error | |
73 | code. So update EIP for INT 0x80 and EXCP_SYSCALL. */ | |
74 | if (is_int || intno == EXCP_SYSCALL) { | |
75 | env->eip = next_eip; | |
76 | } | |
77 | } | |
78 | ||
79 | void x86_cpu_do_interrupt(CPUState *cs) | |
80 | { | |
81 | X86CPU *cpu = X86_CPU(cs); | |
82 | CPUX86State *env = &cpu->env; | |
83 | ||
84 | /* if user mode only, we simulate a fake exception | |
85 | which will be handled outside the cpu execution | |
86 | loop */ | |
87 | do_interrupt_user(env, cs->exception_index, | |
88 | env->exception_is_int, | |
89 | env->error_code, | |
90 | env->exception_next_eip); | |
91 | /* successfully delivered */ | |
92 | env->old_exception = -1; | |
93 | } | |
94 | ||
95 | void cpu_x86_load_seg(CPUX86State *env, X86Seg seg_reg, int selector) | |
96 | { | |
97 | if (!(env->cr[0] & CR0_PE_MASK) || (env->eflags & VM_MASK)) { | |
98 | int dpl = (env->eflags & VM_MASK) ? 3 : 0; | |
99 | selector &= 0xffff; | |
100 | cpu_x86_load_seg_cache(env, seg_reg, selector, | |
101 | (selector << 4), 0xffff, | |
102 | DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | | |
103 | DESC_A_MASK | (dpl << DESC_DPL_SHIFT)); | |
104 | } else { | |
105 | helper_load_seg(env, seg_reg, selector); | |
106 | } | |
107 | } |