xref: /qemu/target/i386/tcg/seg_helper.c (revision 21ffbdc908c347d2e3c7d49abb664dcd554a63ac)
1eaa728eeSbellard /*
210774999SBlue Swirl  *  x86 segmentation related helpers:
310774999SBlue Swirl  *  TSS, interrupts, system calls, jumps and call/task gates, descriptors
4eaa728eeSbellard  *
5eaa728eeSbellard  *  Copyright (c) 2003 Fabrice Bellard
6eaa728eeSbellard  *
7eaa728eeSbellard  * This library is free software; you can redistribute it and/or
8eaa728eeSbellard  * modify it under the terms of the GNU Lesser General Public
9eaa728eeSbellard  * License as published by the Free Software Foundation; either
10eaa728eeSbellard  * version 2 of the License, or (at your option) any later version.
11eaa728eeSbellard  *
12eaa728eeSbellard  * This library is distributed in the hope that it will be useful,
13eaa728eeSbellard  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14eaa728eeSbellard  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15eaa728eeSbellard  * Lesser General Public License for more details.
16eaa728eeSbellard  *
17eaa728eeSbellard  * You should have received a copy of the GNU Lesser General Public
188167ee88SBlue Swirl  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
19eaa728eeSbellard  */
2083dae095SPaolo Bonzini 
21b6a0aa05SPeter Maydell #include "qemu/osdep.h"
223e457172SBlue Swirl #include "cpu.h"
231de7afc9SPaolo Bonzini #include "qemu/log.h"
242ef6175aSRichard Henderson #include "exec/helper-proto.h"
2563c91552SPaolo Bonzini #include "exec/exec-all.h"
26f08b6170SPaolo Bonzini #include "exec/cpu_ldst.h"
27508127e2SPaolo Bonzini #include "exec/log.h"
283e457172SBlue Swirl 
29eaa728eeSbellard //#define DEBUG_PCALL
30eaa728eeSbellard 
31d12d51d5Saliguori #ifdef DEBUG_PCALL
3293fcfe39Saliguori # define LOG_PCALL(...) qemu_log_mask(CPU_LOG_PCALL, ## __VA_ARGS__)
338995b7a0SAndreas Färber # define LOG_PCALL_STATE(cpu)                                  \
348995b7a0SAndreas Färber     log_cpu_state_mask(CPU_LOG_PCALL, (cpu), CPU_DUMP_CCOP)
35d12d51d5Saliguori #else
36d12d51d5Saliguori # define LOG_PCALL(...) do { } while (0)
378995b7a0SAndreas Färber # define LOG_PCALL_STATE(cpu) do { } while (0)
38d12d51d5Saliguori #endif
39d12d51d5Saliguori 
4021ffbdc9SRichard Henderson /*
4121ffbdc9SRichard Henderson  * TODO: Convert callers to compute cpu_mmu_index_kernel once
4221ffbdc9SRichard Henderson  * and use *_mmuidx_ra directly.
4321ffbdc9SRichard Henderson  */
4421ffbdc9SRichard Henderson #define cpu_ldub_kernel_ra(e, p, r) \
4521ffbdc9SRichard Henderson     cpu_ldub_mmuidx_ra(e, p, cpu_mmu_index_kernel(e), r)
4621ffbdc9SRichard Henderson #define cpu_lduw_kernel_ra(e, p, r) \
4721ffbdc9SRichard Henderson     cpu_lduw_mmuidx_ra(e, p, cpu_mmu_index_kernel(e), r)
4821ffbdc9SRichard Henderson #define cpu_ldl_kernel_ra(e, p, r) \
4921ffbdc9SRichard Henderson     cpu_ldl_mmuidx_ra(e, p, cpu_mmu_index_kernel(e), r)
5021ffbdc9SRichard Henderson #define cpu_ldq_kernel_ra(e, p, r) \
5121ffbdc9SRichard Henderson     cpu_ldq_mmuidx_ra(e, p, cpu_mmu_index_kernel(e), r)
529220fe54SPeter Maydell 
5321ffbdc9SRichard Henderson #define cpu_stb_kernel_ra(e, p, v, r) \
5421ffbdc9SRichard Henderson     cpu_stb_mmuidx_ra(e, p, v, cpu_mmu_index_kernel(e), r)
5521ffbdc9SRichard Henderson #define cpu_stw_kernel_ra(e, p, v, r) \
5621ffbdc9SRichard Henderson     cpu_stw_mmuidx_ra(e, p, v, cpu_mmu_index_kernel(e), r)
5721ffbdc9SRichard Henderson #define cpu_stl_kernel_ra(e, p, v, r) \
5821ffbdc9SRichard Henderson     cpu_stl_mmuidx_ra(e, p, v, cpu_mmu_index_kernel(e), r)
5921ffbdc9SRichard Henderson #define cpu_stq_kernel_ra(e, p, v, r) \
6021ffbdc9SRichard Henderson     cpu_stq_mmuidx_ra(e, p, v, cpu_mmu_index_kernel(e), r)
619220fe54SPeter Maydell 
6221ffbdc9SRichard Henderson #define cpu_ldub_kernel(e, p)    cpu_ldub_kernel_ra(e, p, 0)
6321ffbdc9SRichard Henderson #define cpu_lduw_kernel(e, p)    cpu_lduw_kernel_ra(e, p, 0)
6421ffbdc9SRichard Henderson #define cpu_ldl_kernel(e, p)     cpu_ldl_kernel_ra(e, p, 0)
6521ffbdc9SRichard Henderson #define cpu_ldq_kernel(e, p)     cpu_ldq_kernel_ra(e, p, 0)
669220fe54SPeter Maydell 
6721ffbdc9SRichard Henderson #define cpu_stb_kernel(e, p, v)  cpu_stb_kernel_ra(e, p, v, 0)
6821ffbdc9SRichard Henderson #define cpu_stw_kernel(e, p, v)  cpu_stw_kernel_ra(e, p, v, 0)
6921ffbdc9SRichard Henderson #define cpu_stl_kernel(e, p, v)  cpu_stl_kernel_ra(e, p, v, 0)
7021ffbdc9SRichard Henderson #define cpu_stq_kernel(e, p, v)  cpu_stq_kernel_ra(e, p, v, 0)
718a201bd4SPaolo Bonzini 
72eaa728eeSbellard /* return non zero if error */
73100ec099SPavel Dovgalyuk static inline int load_segment_ra(CPUX86State *env, uint32_t *e1_ptr,
74100ec099SPavel Dovgalyuk                                uint32_t *e2_ptr, int selector,
75100ec099SPavel Dovgalyuk                                uintptr_t retaddr)
76eaa728eeSbellard {
77eaa728eeSbellard     SegmentCache *dt;
78eaa728eeSbellard     int index;
79eaa728eeSbellard     target_ulong ptr;
80eaa728eeSbellard 
8120054ef0SBlue Swirl     if (selector & 0x4) {
82eaa728eeSbellard         dt = &env->ldt;
8320054ef0SBlue Swirl     } else {
84eaa728eeSbellard         dt = &env->gdt;
8520054ef0SBlue Swirl     }
86eaa728eeSbellard     index = selector & ~7;
8720054ef0SBlue Swirl     if ((index + 7) > dt->limit) {
88eaa728eeSbellard         return -1;
8920054ef0SBlue Swirl     }
90eaa728eeSbellard     ptr = dt->base + index;
91100ec099SPavel Dovgalyuk     *e1_ptr = cpu_ldl_kernel_ra(env, ptr, retaddr);
92100ec099SPavel Dovgalyuk     *e2_ptr = cpu_ldl_kernel_ra(env, ptr + 4, retaddr);
93eaa728eeSbellard     return 0;
94eaa728eeSbellard }
95eaa728eeSbellard 
96100ec099SPavel Dovgalyuk static inline int load_segment(CPUX86State *env, uint32_t *e1_ptr,
97100ec099SPavel Dovgalyuk                                uint32_t *e2_ptr, int selector)
98100ec099SPavel Dovgalyuk {
99100ec099SPavel Dovgalyuk     return load_segment_ra(env, e1_ptr, e2_ptr, selector, 0);
100100ec099SPavel Dovgalyuk }
101100ec099SPavel Dovgalyuk 
102eaa728eeSbellard static inline unsigned int get_seg_limit(uint32_t e1, uint32_t e2)
103eaa728eeSbellard {
104eaa728eeSbellard     unsigned int limit;
10520054ef0SBlue Swirl 
106eaa728eeSbellard     limit = (e1 & 0xffff) | (e2 & 0x000f0000);
10720054ef0SBlue Swirl     if (e2 & DESC_G_MASK) {
108eaa728eeSbellard         limit = (limit << 12) | 0xfff;
10920054ef0SBlue Swirl     }
110eaa728eeSbellard     return limit;
111eaa728eeSbellard }
112eaa728eeSbellard 
113eaa728eeSbellard static inline uint32_t get_seg_base(uint32_t e1, uint32_t e2)
114eaa728eeSbellard {
11520054ef0SBlue Swirl     return (e1 >> 16) | ((e2 & 0xff) << 16) | (e2 & 0xff000000);
116eaa728eeSbellard }
117eaa728eeSbellard 
11820054ef0SBlue Swirl static inline void load_seg_cache_raw_dt(SegmentCache *sc, uint32_t e1,
11920054ef0SBlue Swirl                                          uint32_t e2)
120eaa728eeSbellard {
121eaa728eeSbellard     sc->base = get_seg_base(e1, e2);
122eaa728eeSbellard     sc->limit = get_seg_limit(e1, e2);
123eaa728eeSbellard     sc->flags = e2;
124eaa728eeSbellard }
125eaa728eeSbellard 
126eaa728eeSbellard /* init the segment cache in vm86 mode. */
1272999a0b2SBlue Swirl static inline void load_seg_vm(CPUX86State *env, int seg, int selector)
128eaa728eeSbellard {
129eaa728eeSbellard     selector &= 0xffff;
130b98dbc90SPaolo Bonzini 
131b98dbc90SPaolo Bonzini     cpu_x86_load_seg_cache(env, seg, selector, (selector << 4), 0xffff,
132b98dbc90SPaolo Bonzini                            DESC_P_MASK | DESC_S_MASK | DESC_W_MASK |
133b98dbc90SPaolo Bonzini                            DESC_A_MASK | (3 << DESC_DPL_SHIFT));
134eaa728eeSbellard }
135eaa728eeSbellard 
1362999a0b2SBlue Swirl static inline void get_ss_esp_from_tss(CPUX86State *env, uint32_t *ss_ptr,
137100ec099SPavel Dovgalyuk                                        uint32_t *esp_ptr, int dpl,
138100ec099SPavel Dovgalyuk                                        uintptr_t retaddr)
139eaa728eeSbellard {
1406aa9e42fSRichard Henderson     X86CPU *cpu = env_archcpu(env);
141eaa728eeSbellard     int type, index, shift;
142eaa728eeSbellard 
143eaa728eeSbellard #if 0
144eaa728eeSbellard     {
145eaa728eeSbellard         int i;
146eaa728eeSbellard         printf("TR: base=%p limit=%x\n", env->tr.base, env->tr.limit);
147eaa728eeSbellard         for (i = 0; i < env->tr.limit; i++) {
148eaa728eeSbellard             printf("%02x ", env->tr.base[i]);
14920054ef0SBlue Swirl             if ((i & 7) == 7) {
15020054ef0SBlue Swirl                 printf("\n");
15120054ef0SBlue Swirl             }
152eaa728eeSbellard         }
153eaa728eeSbellard         printf("\n");
154eaa728eeSbellard     }
155eaa728eeSbellard #endif
156eaa728eeSbellard 
15720054ef0SBlue Swirl     if (!(env->tr.flags & DESC_P_MASK)) {
158a47dddd7SAndreas Färber         cpu_abort(CPU(cpu), "invalid tss");
15920054ef0SBlue Swirl     }
160eaa728eeSbellard     type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf;
16120054ef0SBlue Swirl     if ((type & 7) != 1) {
162a47dddd7SAndreas Färber         cpu_abort(CPU(cpu), "invalid tss type");
16320054ef0SBlue Swirl     }
164eaa728eeSbellard     shift = type >> 3;
165eaa728eeSbellard     index = (dpl * 4 + 2) << shift;
16620054ef0SBlue Swirl     if (index + (4 << shift) - 1 > env->tr.limit) {
167100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0A_TSS, env->tr.selector & 0xfffc, retaddr);
16820054ef0SBlue Swirl     }
169eaa728eeSbellard     if (shift == 0) {
170100ec099SPavel Dovgalyuk         *esp_ptr = cpu_lduw_kernel_ra(env, env->tr.base + index, retaddr);
171100ec099SPavel Dovgalyuk         *ss_ptr = cpu_lduw_kernel_ra(env, env->tr.base + index + 2, retaddr);
172eaa728eeSbellard     } else {
173100ec099SPavel Dovgalyuk         *esp_ptr = cpu_ldl_kernel_ra(env, env->tr.base + index, retaddr);
174100ec099SPavel Dovgalyuk         *ss_ptr = cpu_lduw_kernel_ra(env, env->tr.base + index + 4, retaddr);
175eaa728eeSbellard     }
176eaa728eeSbellard }
177eaa728eeSbellard 
178100ec099SPavel Dovgalyuk static void tss_load_seg(CPUX86State *env, int seg_reg, int selector, int cpl,
179100ec099SPavel Dovgalyuk                          uintptr_t retaddr)
180eaa728eeSbellard {
181eaa728eeSbellard     uint32_t e1, e2;
182d3b54918SPaolo Bonzini     int rpl, dpl;
183eaa728eeSbellard 
184eaa728eeSbellard     if ((selector & 0xfffc) != 0) {
185100ec099SPavel Dovgalyuk         if (load_segment_ra(env, &e1, &e2, selector, retaddr) != 0) {
186100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr);
18720054ef0SBlue Swirl         }
18820054ef0SBlue Swirl         if (!(e2 & DESC_S_MASK)) {
189100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr);
19020054ef0SBlue Swirl         }
191eaa728eeSbellard         rpl = selector & 3;
192eaa728eeSbellard         dpl = (e2 >> DESC_DPL_SHIFT) & 3;
193eaa728eeSbellard         if (seg_reg == R_CS) {
19420054ef0SBlue Swirl             if (!(e2 & DESC_CS_MASK)) {
195100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr);
19620054ef0SBlue Swirl             }
19720054ef0SBlue Swirl             if (dpl != rpl) {
198100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr);
19920054ef0SBlue Swirl             }
200eaa728eeSbellard         } else if (seg_reg == R_SS) {
201eaa728eeSbellard             /* SS must be writable data */
20220054ef0SBlue Swirl             if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK)) {
203100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr);
20420054ef0SBlue Swirl             }
20520054ef0SBlue Swirl             if (dpl != cpl || dpl != rpl) {
206100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr);
20720054ef0SBlue Swirl             }
208eaa728eeSbellard         } else {
209eaa728eeSbellard             /* not readable code */
21020054ef0SBlue Swirl             if ((e2 & DESC_CS_MASK) && !(e2 & DESC_R_MASK)) {
211100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr);
21220054ef0SBlue Swirl             }
213eaa728eeSbellard             /* if data or non conforming code, checks the rights */
214eaa728eeSbellard             if (((e2 >> DESC_TYPE_SHIFT) & 0xf) < 12) {
21520054ef0SBlue Swirl                 if (dpl < cpl || dpl < rpl) {
216100ec099SPavel Dovgalyuk                     raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr);
217eaa728eeSbellard                 }
218eaa728eeSbellard             }
21920054ef0SBlue Swirl         }
22020054ef0SBlue Swirl         if (!(e2 & DESC_P_MASK)) {
221100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0B_NOSEG, selector & 0xfffc, retaddr);
22220054ef0SBlue Swirl         }
223eaa728eeSbellard         cpu_x86_load_seg_cache(env, seg_reg, selector,
224eaa728eeSbellard                                get_seg_base(e1, e2),
225eaa728eeSbellard                                get_seg_limit(e1, e2),
226eaa728eeSbellard                                e2);
227eaa728eeSbellard     } else {
22820054ef0SBlue Swirl         if (seg_reg == R_SS || seg_reg == R_CS) {
229100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr);
230eaa728eeSbellard         }
231eaa728eeSbellard     }
23220054ef0SBlue Swirl }
233eaa728eeSbellard 
234eaa728eeSbellard #define SWITCH_TSS_JMP  0
235eaa728eeSbellard #define SWITCH_TSS_IRET 1
236eaa728eeSbellard #define SWITCH_TSS_CALL 2
237eaa728eeSbellard 
238eaa728eeSbellard /* XXX: restore CPU state in registers (PowerPC case) */
239100ec099SPavel Dovgalyuk static void switch_tss_ra(CPUX86State *env, int tss_selector,
240eaa728eeSbellard                           uint32_t e1, uint32_t e2, int source,
241100ec099SPavel Dovgalyuk                           uint32_t next_eip, uintptr_t retaddr)
242eaa728eeSbellard {
243eaa728eeSbellard     int tss_limit, tss_limit_max, type, old_tss_limit_max, old_type, v1, v2, i;
244eaa728eeSbellard     target_ulong tss_base;
245eaa728eeSbellard     uint32_t new_regs[8], new_segs[6];
246eaa728eeSbellard     uint32_t new_eflags, new_eip, new_cr3, new_ldt, new_trap;
247eaa728eeSbellard     uint32_t old_eflags, eflags_mask;
248eaa728eeSbellard     SegmentCache *dt;
249eaa728eeSbellard     int index;
250eaa728eeSbellard     target_ulong ptr;
251eaa728eeSbellard 
252eaa728eeSbellard     type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
25320054ef0SBlue Swirl     LOG_PCALL("switch_tss: sel=0x%04x type=%d src=%d\n", tss_selector, type,
25420054ef0SBlue Swirl               source);
255eaa728eeSbellard 
256eaa728eeSbellard     /* if task gate, we read the TSS segment and we load it */
257eaa728eeSbellard     if (type == 5) {
25820054ef0SBlue Swirl         if (!(e2 & DESC_P_MASK)) {
259100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0B_NOSEG, tss_selector & 0xfffc, retaddr);
26020054ef0SBlue Swirl         }
261eaa728eeSbellard         tss_selector = e1 >> 16;
26220054ef0SBlue Swirl         if (tss_selector & 4) {
263100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, retaddr);
26420054ef0SBlue Swirl         }
265100ec099SPavel Dovgalyuk         if (load_segment_ra(env, &e1, &e2, tss_selector, retaddr) != 0) {
266100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, tss_selector & 0xfffc, retaddr);
267eaa728eeSbellard         }
26820054ef0SBlue Swirl         if (e2 & DESC_S_MASK) {
269100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, tss_selector & 0xfffc, retaddr);
27020054ef0SBlue Swirl         }
27120054ef0SBlue Swirl         type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
27220054ef0SBlue Swirl         if ((type & 7) != 1) {
273100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, tss_selector & 0xfffc, retaddr);
27420054ef0SBlue Swirl         }
27520054ef0SBlue Swirl     }
276eaa728eeSbellard 
27720054ef0SBlue Swirl     if (!(e2 & DESC_P_MASK)) {
278100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0B_NOSEG, tss_selector & 0xfffc, retaddr);
27920054ef0SBlue Swirl     }
280eaa728eeSbellard 
28120054ef0SBlue Swirl     if (type & 8) {
282eaa728eeSbellard         tss_limit_max = 103;
28320054ef0SBlue Swirl     } else {
284eaa728eeSbellard         tss_limit_max = 43;
28520054ef0SBlue Swirl     }
286eaa728eeSbellard     tss_limit = get_seg_limit(e1, e2);
287eaa728eeSbellard     tss_base = get_seg_base(e1, e2);
288eaa728eeSbellard     if ((tss_selector & 4) != 0 ||
28920054ef0SBlue Swirl         tss_limit < tss_limit_max) {
290100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, retaddr);
29120054ef0SBlue Swirl     }
292eaa728eeSbellard     old_type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf;
29320054ef0SBlue Swirl     if (old_type & 8) {
294eaa728eeSbellard         old_tss_limit_max = 103;
29520054ef0SBlue Swirl     } else {
296eaa728eeSbellard         old_tss_limit_max = 43;
29720054ef0SBlue Swirl     }
298eaa728eeSbellard 
299eaa728eeSbellard     /* read all the registers from the new TSS */
300eaa728eeSbellard     if (type & 8) {
301eaa728eeSbellard         /* 32 bit */
302100ec099SPavel Dovgalyuk         new_cr3 = cpu_ldl_kernel_ra(env, tss_base + 0x1c, retaddr);
303100ec099SPavel Dovgalyuk         new_eip = cpu_ldl_kernel_ra(env, tss_base + 0x20, retaddr);
304100ec099SPavel Dovgalyuk         new_eflags = cpu_ldl_kernel_ra(env, tss_base + 0x24, retaddr);
30520054ef0SBlue Swirl         for (i = 0; i < 8; i++) {
306100ec099SPavel Dovgalyuk             new_regs[i] = cpu_ldl_kernel_ra(env, tss_base + (0x28 + i * 4),
307100ec099SPavel Dovgalyuk                                             retaddr);
30820054ef0SBlue Swirl         }
30920054ef0SBlue Swirl         for (i = 0; i < 6; i++) {
310100ec099SPavel Dovgalyuk             new_segs[i] = cpu_lduw_kernel_ra(env, tss_base + (0x48 + i * 4),
311100ec099SPavel Dovgalyuk                                              retaddr);
31220054ef0SBlue Swirl         }
313100ec099SPavel Dovgalyuk         new_ldt = cpu_lduw_kernel_ra(env, tss_base + 0x60, retaddr);
314100ec099SPavel Dovgalyuk         new_trap = cpu_ldl_kernel_ra(env, tss_base + 0x64, retaddr);
315eaa728eeSbellard     } else {
316eaa728eeSbellard         /* 16 bit */
317eaa728eeSbellard         new_cr3 = 0;
318100ec099SPavel Dovgalyuk         new_eip = cpu_lduw_kernel_ra(env, tss_base + 0x0e, retaddr);
319100ec099SPavel Dovgalyuk         new_eflags = cpu_lduw_kernel_ra(env, tss_base + 0x10, retaddr);
32020054ef0SBlue Swirl         for (i = 0; i < 8; i++) {
321100ec099SPavel Dovgalyuk             new_regs[i] = cpu_lduw_kernel_ra(env, tss_base + (0x12 + i * 2),
322100ec099SPavel Dovgalyuk                                              retaddr) | 0xffff0000;
32320054ef0SBlue Swirl         }
32420054ef0SBlue Swirl         for (i = 0; i < 4; i++) {
325100ec099SPavel Dovgalyuk             new_segs[i] = cpu_lduw_kernel_ra(env, tss_base + (0x22 + i * 4),
326100ec099SPavel Dovgalyuk                                              retaddr);
32720054ef0SBlue Swirl         }
328100ec099SPavel Dovgalyuk         new_ldt = cpu_lduw_kernel_ra(env, tss_base + 0x2a, retaddr);
329eaa728eeSbellard         new_segs[R_FS] = 0;
330eaa728eeSbellard         new_segs[R_GS] = 0;
331eaa728eeSbellard         new_trap = 0;
332eaa728eeSbellard     }
3334581cbcdSBlue Swirl     /* XXX: avoid a compiler warning, see
3344581cbcdSBlue Swirl      http://support.amd.com/us/Processor_TechDocs/24593.pdf
3354581cbcdSBlue Swirl      chapters 12.2.5 and 13.2.4 on how to implement TSS Trap bit */
3364581cbcdSBlue Swirl     (void)new_trap;
337eaa728eeSbellard 
338eaa728eeSbellard     /* NOTE: we must avoid memory exceptions during the task switch,
339eaa728eeSbellard        so we make dummy accesses before */
340eaa728eeSbellard     /* XXX: it can still fail in some cases, so a bigger hack is
341eaa728eeSbellard        necessary to valid the TLB after having done the accesses */
342eaa728eeSbellard 
343100ec099SPavel Dovgalyuk     v1 = cpu_ldub_kernel_ra(env, env->tr.base, retaddr);
344100ec099SPavel Dovgalyuk     v2 = cpu_ldub_kernel_ra(env, env->tr.base + old_tss_limit_max, retaddr);
345100ec099SPavel Dovgalyuk     cpu_stb_kernel_ra(env, env->tr.base, v1, retaddr);
346100ec099SPavel Dovgalyuk     cpu_stb_kernel_ra(env, env->tr.base + old_tss_limit_max, v2, retaddr);
347eaa728eeSbellard 
348eaa728eeSbellard     /* clear busy bit (it is restartable) */
349eaa728eeSbellard     if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_IRET) {
350eaa728eeSbellard         target_ulong ptr;
351eaa728eeSbellard         uint32_t e2;
35220054ef0SBlue Swirl 
353eaa728eeSbellard         ptr = env->gdt.base + (env->tr.selector & ~7);
354100ec099SPavel Dovgalyuk         e2 = cpu_ldl_kernel_ra(env, ptr + 4, retaddr);
355eaa728eeSbellard         e2 &= ~DESC_TSS_BUSY_MASK;
356100ec099SPavel Dovgalyuk         cpu_stl_kernel_ra(env, ptr + 4, e2, retaddr);
357eaa728eeSbellard     }
358997ff0d9SBlue Swirl     old_eflags = cpu_compute_eflags(env);
35920054ef0SBlue Swirl     if (source == SWITCH_TSS_IRET) {
360eaa728eeSbellard         old_eflags &= ~NT_MASK;
36120054ef0SBlue Swirl     }
362eaa728eeSbellard 
363eaa728eeSbellard     /* save the current state in the old TSS */
364eaa728eeSbellard     if (type & 8) {
365eaa728eeSbellard         /* 32 bit */
366100ec099SPavel Dovgalyuk         cpu_stl_kernel_ra(env, env->tr.base + 0x20, next_eip, retaddr);
367100ec099SPavel Dovgalyuk         cpu_stl_kernel_ra(env, env->tr.base + 0x24, old_eflags, retaddr);
368100ec099SPavel Dovgalyuk         cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 0 * 4), env->regs[R_EAX], retaddr);
369100ec099SPavel Dovgalyuk         cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 1 * 4), env->regs[R_ECX], retaddr);
370100ec099SPavel Dovgalyuk         cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 2 * 4), env->regs[R_EDX], retaddr);
371100ec099SPavel Dovgalyuk         cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 3 * 4), env->regs[R_EBX], retaddr);
372100ec099SPavel Dovgalyuk         cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 4 * 4), env->regs[R_ESP], retaddr);
373100ec099SPavel Dovgalyuk         cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 5 * 4), env->regs[R_EBP], retaddr);
374100ec099SPavel Dovgalyuk         cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 6 * 4), env->regs[R_ESI], retaddr);
375100ec099SPavel Dovgalyuk         cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 7 * 4), env->regs[R_EDI], retaddr);
37620054ef0SBlue Swirl         for (i = 0; i < 6; i++) {
377100ec099SPavel Dovgalyuk             cpu_stw_kernel_ra(env, env->tr.base + (0x48 + i * 4),
378100ec099SPavel Dovgalyuk                               env->segs[i].selector, retaddr);
37920054ef0SBlue Swirl         }
380eaa728eeSbellard     } else {
381eaa728eeSbellard         /* 16 bit */
382100ec099SPavel Dovgalyuk         cpu_stw_kernel_ra(env, env->tr.base + 0x0e, next_eip, retaddr);
383100ec099SPavel Dovgalyuk         cpu_stw_kernel_ra(env, env->tr.base + 0x10, old_eflags, retaddr);
384100ec099SPavel Dovgalyuk         cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 0 * 2), env->regs[R_EAX], retaddr);
385100ec099SPavel Dovgalyuk         cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 1 * 2), env->regs[R_ECX], retaddr);
386100ec099SPavel Dovgalyuk         cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 2 * 2), env->regs[R_EDX], retaddr);
387100ec099SPavel Dovgalyuk         cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 3 * 2), env->regs[R_EBX], retaddr);
388100ec099SPavel Dovgalyuk         cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 4 * 2), env->regs[R_ESP], retaddr);
389100ec099SPavel Dovgalyuk         cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 5 * 2), env->regs[R_EBP], retaddr);
390100ec099SPavel Dovgalyuk         cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 6 * 2), env->regs[R_ESI], retaddr);
391100ec099SPavel Dovgalyuk         cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 7 * 2), env->regs[R_EDI], retaddr);
39220054ef0SBlue Swirl         for (i = 0; i < 4; i++) {
393100ec099SPavel Dovgalyuk             cpu_stw_kernel_ra(env, env->tr.base + (0x22 + i * 4),
394100ec099SPavel Dovgalyuk                               env->segs[i].selector, retaddr);
395eaa728eeSbellard         }
39620054ef0SBlue Swirl     }
397eaa728eeSbellard 
398eaa728eeSbellard     /* now if an exception occurs, it will occurs in the next task
399eaa728eeSbellard        context */
400eaa728eeSbellard 
401eaa728eeSbellard     if (source == SWITCH_TSS_CALL) {
402100ec099SPavel Dovgalyuk         cpu_stw_kernel_ra(env, tss_base, env->tr.selector, retaddr);
403eaa728eeSbellard         new_eflags |= NT_MASK;
404eaa728eeSbellard     }
405eaa728eeSbellard 
406eaa728eeSbellard     /* set busy bit */
407eaa728eeSbellard     if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_CALL) {
408eaa728eeSbellard         target_ulong ptr;
409eaa728eeSbellard         uint32_t e2;
41020054ef0SBlue Swirl 
411eaa728eeSbellard         ptr = env->gdt.base + (tss_selector & ~7);
412100ec099SPavel Dovgalyuk         e2 = cpu_ldl_kernel_ra(env, ptr + 4, retaddr);
413eaa728eeSbellard         e2 |= DESC_TSS_BUSY_MASK;
414100ec099SPavel Dovgalyuk         cpu_stl_kernel_ra(env, ptr + 4, e2, retaddr);
415eaa728eeSbellard     }
416eaa728eeSbellard 
417eaa728eeSbellard     /* set the new CPU state */
418eaa728eeSbellard     /* from this point, any exception which occurs can give problems */
419eaa728eeSbellard     env->cr[0] |= CR0_TS_MASK;
420eaa728eeSbellard     env->hflags |= HF_TS_MASK;
421eaa728eeSbellard     env->tr.selector = tss_selector;
422eaa728eeSbellard     env->tr.base = tss_base;
423eaa728eeSbellard     env->tr.limit = tss_limit;
424eaa728eeSbellard     env->tr.flags = e2 & ~DESC_TSS_BUSY_MASK;
425eaa728eeSbellard 
426eaa728eeSbellard     if ((type & 8) && (env->cr[0] & CR0_PG_MASK)) {
427eaa728eeSbellard         cpu_x86_update_cr3(env, new_cr3);
428eaa728eeSbellard     }
429eaa728eeSbellard 
430eaa728eeSbellard     /* load all registers without an exception, then reload them with
431eaa728eeSbellard        possible exception */
432eaa728eeSbellard     env->eip = new_eip;
433eaa728eeSbellard     eflags_mask = TF_MASK | AC_MASK | ID_MASK |
434eaa728eeSbellard         IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | NT_MASK;
43520054ef0SBlue Swirl     if (!(type & 8)) {
436eaa728eeSbellard         eflags_mask &= 0xffff;
43720054ef0SBlue Swirl     }
438997ff0d9SBlue Swirl     cpu_load_eflags(env, new_eflags, eflags_mask);
439eaa728eeSbellard     /* XXX: what to do in 16 bit case? */
4404b34e3adSliguang     env->regs[R_EAX] = new_regs[0];
441a4165610Sliguang     env->regs[R_ECX] = new_regs[1];
44200f5e6f2Sliguang     env->regs[R_EDX] = new_regs[2];
44370b51365Sliguang     env->regs[R_EBX] = new_regs[3];
44408b3ded6Sliguang     env->regs[R_ESP] = new_regs[4];
445c12dddd7Sliguang     env->regs[R_EBP] = new_regs[5];
44678c3c6d3Sliguang     env->regs[R_ESI] = new_regs[6];
447cf75c597Sliguang     env->regs[R_EDI] = new_regs[7];
448eaa728eeSbellard     if (new_eflags & VM_MASK) {
44920054ef0SBlue Swirl         for (i = 0; i < 6; i++) {
4502999a0b2SBlue Swirl             load_seg_vm(env, i, new_segs[i]);
45120054ef0SBlue Swirl         }
452eaa728eeSbellard     } else {
453eaa728eeSbellard         /* first just selectors as the rest may trigger exceptions */
45420054ef0SBlue Swirl         for (i = 0; i < 6; i++) {
455eaa728eeSbellard             cpu_x86_load_seg_cache(env, i, new_segs[i], 0, 0, 0);
456eaa728eeSbellard         }
45720054ef0SBlue Swirl     }
458eaa728eeSbellard 
459eaa728eeSbellard     env->ldt.selector = new_ldt & ~4;
460eaa728eeSbellard     env->ldt.base = 0;
461eaa728eeSbellard     env->ldt.limit = 0;
462eaa728eeSbellard     env->ldt.flags = 0;
463eaa728eeSbellard 
464eaa728eeSbellard     /* load the LDT */
46520054ef0SBlue Swirl     if (new_ldt & 4) {
466100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0A_TSS, new_ldt & 0xfffc, retaddr);
46720054ef0SBlue Swirl     }
468eaa728eeSbellard 
469eaa728eeSbellard     if ((new_ldt & 0xfffc) != 0) {
470eaa728eeSbellard         dt = &env->gdt;
471eaa728eeSbellard         index = new_ldt & ~7;
47220054ef0SBlue Swirl         if ((index + 7) > dt->limit) {
473100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0A_TSS, new_ldt & 0xfffc, retaddr);
47420054ef0SBlue Swirl         }
475eaa728eeSbellard         ptr = dt->base + index;
476100ec099SPavel Dovgalyuk         e1 = cpu_ldl_kernel_ra(env, ptr, retaddr);
477100ec099SPavel Dovgalyuk         e2 = cpu_ldl_kernel_ra(env, ptr + 4, retaddr);
47820054ef0SBlue Swirl         if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2) {
479100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0A_TSS, new_ldt & 0xfffc, retaddr);
48020054ef0SBlue Swirl         }
48120054ef0SBlue Swirl         if (!(e2 & DESC_P_MASK)) {
482100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0A_TSS, new_ldt & 0xfffc, retaddr);
48320054ef0SBlue Swirl         }
484eaa728eeSbellard         load_seg_cache_raw_dt(&env->ldt, e1, e2);
485eaa728eeSbellard     }
486eaa728eeSbellard 
487eaa728eeSbellard     /* load the segments */
488eaa728eeSbellard     if (!(new_eflags & VM_MASK)) {
489d3b54918SPaolo Bonzini         int cpl = new_segs[R_CS] & 3;
490100ec099SPavel Dovgalyuk         tss_load_seg(env, R_CS, new_segs[R_CS], cpl, retaddr);
491100ec099SPavel Dovgalyuk         tss_load_seg(env, R_SS, new_segs[R_SS], cpl, retaddr);
492100ec099SPavel Dovgalyuk         tss_load_seg(env, R_ES, new_segs[R_ES], cpl, retaddr);
493100ec099SPavel Dovgalyuk         tss_load_seg(env, R_DS, new_segs[R_DS], cpl, retaddr);
494100ec099SPavel Dovgalyuk         tss_load_seg(env, R_FS, new_segs[R_FS], cpl, retaddr);
495100ec099SPavel Dovgalyuk         tss_load_seg(env, R_GS, new_segs[R_GS], cpl, retaddr);
496eaa728eeSbellard     }
497eaa728eeSbellard 
498a78d0eabSliguang     /* check that env->eip is in the CS segment limits */
499eaa728eeSbellard     if (new_eip > env->segs[R_CS].limit) {
500eaa728eeSbellard         /* XXX: different exception if CALL? */
501100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0D_GPF, 0, retaddr);
502eaa728eeSbellard     }
50301df040bSaliguori 
50401df040bSaliguori #ifndef CONFIG_USER_ONLY
50501df040bSaliguori     /* reset local breakpoints */
506428065ceSliguang     if (env->dr[7] & DR7_LOCAL_BP_MASK) {
50793d00d0fSRichard Henderson         cpu_x86_update_dr7(env, env->dr[7] & ~DR7_LOCAL_BP_MASK);
50801df040bSaliguori     }
50901df040bSaliguori #endif
510eaa728eeSbellard }
511eaa728eeSbellard 
512100ec099SPavel Dovgalyuk static void switch_tss(CPUX86State *env, int tss_selector,
513100ec099SPavel Dovgalyuk                        uint32_t e1, uint32_t e2, int source,
514100ec099SPavel Dovgalyuk                         uint32_t next_eip)
515100ec099SPavel Dovgalyuk {
516100ec099SPavel Dovgalyuk     switch_tss_ra(env, tss_selector, e1, e2, source, next_eip, 0);
517100ec099SPavel Dovgalyuk }
518100ec099SPavel Dovgalyuk 
519eaa728eeSbellard static inline unsigned int get_sp_mask(unsigned int e2)
520eaa728eeSbellard {
5210aca0605SAndrew Oates #ifdef TARGET_X86_64
5220aca0605SAndrew Oates     if (e2 & DESC_L_MASK) {
5230aca0605SAndrew Oates         return 0;
5240aca0605SAndrew Oates     } else
5250aca0605SAndrew Oates #endif
52620054ef0SBlue Swirl     if (e2 & DESC_B_MASK) {
527eaa728eeSbellard         return 0xffffffff;
52820054ef0SBlue Swirl     } else {
529eaa728eeSbellard         return 0xffff;
530eaa728eeSbellard     }
53120054ef0SBlue Swirl }
532eaa728eeSbellard 
53320054ef0SBlue Swirl static int exception_has_error_code(int intno)
5342ed51f5bSaliguori {
5352ed51f5bSaliguori     switch (intno) {
5362ed51f5bSaliguori     case 8:
5372ed51f5bSaliguori     case 10:
5382ed51f5bSaliguori     case 11:
5392ed51f5bSaliguori     case 12:
5402ed51f5bSaliguori     case 13:
5412ed51f5bSaliguori     case 14:
5422ed51f5bSaliguori     case 17:
5432ed51f5bSaliguori         return 1;
5442ed51f5bSaliguori     }
5452ed51f5bSaliguori     return 0;
5462ed51f5bSaliguori }
5472ed51f5bSaliguori 
548eaa728eeSbellard #ifdef TARGET_X86_64
549eaa728eeSbellard #define SET_ESP(val, sp_mask)                                   \
550eaa728eeSbellard     do {                                                        \
55120054ef0SBlue Swirl         if ((sp_mask) == 0xffff) {                              \
55208b3ded6Sliguang             env->regs[R_ESP] = (env->regs[R_ESP] & ~0xffff) |   \
55308b3ded6Sliguang                 ((val) & 0xffff);                               \
55420054ef0SBlue Swirl         } else if ((sp_mask) == 0xffffffffLL) {                 \
55508b3ded6Sliguang             env->regs[R_ESP] = (uint32_t)(val);                 \
55620054ef0SBlue Swirl         } else {                                                \
55708b3ded6Sliguang             env->regs[R_ESP] = (val);                           \
55820054ef0SBlue Swirl         }                                                       \
559eaa728eeSbellard     } while (0)
560eaa728eeSbellard #else
56120054ef0SBlue Swirl #define SET_ESP(val, sp_mask)                                   \
56220054ef0SBlue Swirl     do {                                                        \
56308b3ded6Sliguang         env->regs[R_ESP] = (env->regs[R_ESP] & ~(sp_mask)) |    \
56408b3ded6Sliguang             ((val) & (sp_mask));                                \
56520054ef0SBlue Swirl     } while (0)
566eaa728eeSbellard #endif
567eaa728eeSbellard 
568c0a04f0eSaliguori /* in 64-bit machines, this can overflow. So this segment addition macro
569c0a04f0eSaliguori  * can be used to trim the value to 32-bit whenever needed */
570c0a04f0eSaliguori #define SEG_ADDL(ssp, sp, sp_mask) ((uint32_t)((ssp) + (sp & (sp_mask))))
571c0a04f0eSaliguori 
572eaa728eeSbellard /* XXX: add a is_user flag to have proper security support */
573100ec099SPavel Dovgalyuk #define PUSHW_RA(ssp, sp, sp_mask, val, ra)                      \
574eaa728eeSbellard     {                                                            \
575eaa728eeSbellard         sp -= 2;                                                 \
576100ec099SPavel Dovgalyuk         cpu_stw_kernel_ra(env, (ssp) + (sp & (sp_mask)), (val), ra); \
577eaa728eeSbellard     }
578eaa728eeSbellard 
579100ec099SPavel Dovgalyuk #define PUSHL_RA(ssp, sp, sp_mask, val, ra)                             \
580eaa728eeSbellard     {                                                                   \
581eaa728eeSbellard         sp -= 4;                                                        \
582100ec099SPavel Dovgalyuk         cpu_stl_kernel_ra(env, SEG_ADDL(ssp, sp, sp_mask), (uint32_t)(val), ra); \
583eaa728eeSbellard     }
584eaa728eeSbellard 
585100ec099SPavel Dovgalyuk #define POPW_RA(ssp, sp, sp_mask, val, ra)                       \
586eaa728eeSbellard     {                                                            \
587100ec099SPavel Dovgalyuk         val = cpu_lduw_kernel_ra(env, (ssp) + (sp & (sp_mask)), ra); \
588eaa728eeSbellard         sp += 2;                                                 \
589eaa728eeSbellard     }
590eaa728eeSbellard 
591100ec099SPavel Dovgalyuk #define POPL_RA(ssp, sp, sp_mask, val, ra)                              \
592eaa728eeSbellard     {                                                                   \
593100ec099SPavel Dovgalyuk         val = (uint32_t)cpu_ldl_kernel_ra(env, SEG_ADDL(ssp, sp, sp_mask), ra); \
594eaa728eeSbellard         sp += 4;                                                        \
595eaa728eeSbellard     }
596eaa728eeSbellard 
597100ec099SPavel Dovgalyuk #define PUSHW(ssp, sp, sp_mask, val) PUSHW_RA(ssp, sp, sp_mask, val, 0)
598100ec099SPavel Dovgalyuk #define PUSHL(ssp, sp, sp_mask, val) PUSHL_RA(ssp, sp, sp_mask, val, 0)
599100ec099SPavel Dovgalyuk #define POPW(ssp, sp, sp_mask, val) POPW_RA(ssp, sp, sp_mask, val, 0)
600100ec099SPavel Dovgalyuk #define POPL(ssp, sp, sp_mask, val) POPL_RA(ssp, sp, sp_mask, val, 0)
601100ec099SPavel Dovgalyuk 
602eaa728eeSbellard /* protected mode interrupt */
6032999a0b2SBlue Swirl static void do_interrupt_protected(CPUX86State *env, int intno, int is_int,
6042999a0b2SBlue Swirl                                    int error_code, unsigned int next_eip,
6052999a0b2SBlue Swirl                                    int is_hw)
606eaa728eeSbellard {
607eaa728eeSbellard     SegmentCache *dt;
608eaa728eeSbellard     target_ulong ptr, ssp;
609eaa728eeSbellard     int type, dpl, selector, ss_dpl, cpl;
610eaa728eeSbellard     int has_error_code, new_stack, shift;
6111c918ebaSblueswir1     uint32_t e1, e2, offset, ss = 0, esp, ss_e1 = 0, ss_e2 = 0;
612eaa728eeSbellard     uint32_t old_eip, sp_mask;
61387446327SKevin O'Connor     int vm86 = env->eflags & VM_MASK;
614eaa728eeSbellard 
615eaa728eeSbellard     has_error_code = 0;
61620054ef0SBlue Swirl     if (!is_int && !is_hw) {
61720054ef0SBlue Swirl         has_error_code = exception_has_error_code(intno);
61820054ef0SBlue Swirl     }
61920054ef0SBlue Swirl     if (is_int) {
620eaa728eeSbellard         old_eip = next_eip;
62120054ef0SBlue Swirl     } else {
622eaa728eeSbellard         old_eip = env->eip;
62320054ef0SBlue Swirl     }
624eaa728eeSbellard 
625eaa728eeSbellard     dt = &env->idt;
62620054ef0SBlue Swirl     if (intno * 8 + 7 > dt->limit) {
62777b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0D_GPF, intno * 8 + 2);
62820054ef0SBlue Swirl     }
629eaa728eeSbellard     ptr = dt->base + intno * 8;
630329e607dSBlue Swirl     e1 = cpu_ldl_kernel(env, ptr);
631329e607dSBlue Swirl     e2 = cpu_ldl_kernel(env, ptr + 4);
632eaa728eeSbellard     /* check gate type */
633eaa728eeSbellard     type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
634eaa728eeSbellard     switch (type) {
635eaa728eeSbellard     case 5: /* task gate */
636eaa728eeSbellard         /* must do that check here to return the correct error code */
63720054ef0SBlue Swirl         if (!(e2 & DESC_P_MASK)) {
63877b2bc2cSBlue Swirl             raise_exception_err(env, EXCP0B_NOSEG, intno * 8 + 2);
63920054ef0SBlue Swirl         }
6402999a0b2SBlue Swirl         switch_tss(env, intno * 8, e1, e2, SWITCH_TSS_CALL, old_eip);
641eaa728eeSbellard         if (has_error_code) {
642eaa728eeSbellard             int type;
643eaa728eeSbellard             uint32_t mask;
64420054ef0SBlue Swirl 
645eaa728eeSbellard             /* push the error code */
646eaa728eeSbellard             type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf;
647eaa728eeSbellard             shift = type >> 3;
64820054ef0SBlue Swirl             if (env->segs[R_SS].flags & DESC_B_MASK) {
649eaa728eeSbellard                 mask = 0xffffffff;
65020054ef0SBlue Swirl             } else {
651eaa728eeSbellard                 mask = 0xffff;
65220054ef0SBlue Swirl             }
65308b3ded6Sliguang             esp = (env->regs[R_ESP] - (2 << shift)) & mask;
654eaa728eeSbellard             ssp = env->segs[R_SS].base + esp;
65520054ef0SBlue Swirl             if (shift) {
656329e607dSBlue Swirl                 cpu_stl_kernel(env, ssp, error_code);
65720054ef0SBlue Swirl             } else {
658329e607dSBlue Swirl                 cpu_stw_kernel(env, ssp, error_code);
65920054ef0SBlue Swirl             }
660eaa728eeSbellard             SET_ESP(esp, mask);
661eaa728eeSbellard         }
662eaa728eeSbellard         return;
663eaa728eeSbellard     case 6: /* 286 interrupt gate */
664eaa728eeSbellard     case 7: /* 286 trap gate */
665eaa728eeSbellard     case 14: /* 386 interrupt gate */
666eaa728eeSbellard     case 15: /* 386 trap gate */
667eaa728eeSbellard         break;
668eaa728eeSbellard     default:
66977b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0D_GPF, intno * 8 + 2);
670eaa728eeSbellard         break;
671eaa728eeSbellard     }
672eaa728eeSbellard     dpl = (e2 >> DESC_DPL_SHIFT) & 3;
673eaa728eeSbellard     cpl = env->hflags & HF_CPL_MASK;
6741235fc06Sths     /* check privilege if software int */
67520054ef0SBlue Swirl     if (is_int && dpl < cpl) {
67677b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0D_GPF, intno * 8 + 2);
67720054ef0SBlue Swirl     }
678eaa728eeSbellard     /* check valid bit */
67920054ef0SBlue Swirl     if (!(e2 & DESC_P_MASK)) {
68077b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0B_NOSEG, intno * 8 + 2);
68120054ef0SBlue Swirl     }
682eaa728eeSbellard     selector = e1 >> 16;
683eaa728eeSbellard     offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff);
68420054ef0SBlue Swirl     if ((selector & 0xfffc) == 0) {
68577b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0D_GPF, 0);
68620054ef0SBlue Swirl     }
6872999a0b2SBlue Swirl     if (load_segment(env, &e1, &e2, selector) != 0) {
68877b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
68920054ef0SBlue Swirl     }
69020054ef0SBlue Swirl     if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK))) {
69177b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
69220054ef0SBlue Swirl     }
693eaa728eeSbellard     dpl = (e2 >> DESC_DPL_SHIFT) & 3;
69420054ef0SBlue Swirl     if (dpl > cpl) {
69577b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
69620054ef0SBlue Swirl     }
69720054ef0SBlue Swirl     if (!(e2 & DESC_P_MASK)) {
69877b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0B_NOSEG, selector & 0xfffc);
69920054ef0SBlue Swirl     }
7001110bfe6SPaolo Bonzini     if (e2 & DESC_C_MASK) {
7011110bfe6SPaolo Bonzini         dpl = cpl;
7021110bfe6SPaolo Bonzini     }
7031110bfe6SPaolo Bonzini     if (dpl < cpl) {
704eaa728eeSbellard         /* to inner privilege */
705100ec099SPavel Dovgalyuk         get_ss_esp_from_tss(env, &ss, &esp, dpl, 0);
70620054ef0SBlue Swirl         if ((ss & 0xfffc) == 0) {
70777b2bc2cSBlue Swirl             raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc);
70820054ef0SBlue Swirl         }
70920054ef0SBlue Swirl         if ((ss & 3) != dpl) {
71077b2bc2cSBlue Swirl             raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc);
71120054ef0SBlue Swirl         }
7122999a0b2SBlue Swirl         if (load_segment(env, &ss_e1, &ss_e2, ss) != 0) {
71377b2bc2cSBlue Swirl             raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc);
71420054ef0SBlue Swirl         }
715eaa728eeSbellard         ss_dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3;
71620054ef0SBlue Swirl         if (ss_dpl != dpl) {
71777b2bc2cSBlue Swirl             raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc);
71820054ef0SBlue Swirl         }
719eaa728eeSbellard         if (!(ss_e2 & DESC_S_MASK) ||
720eaa728eeSbellard             (ss_e2 & DESC_CS_MASK) ||
72120054ef0SBlue Swirl             !(ss_e2 & DESC_W_MASK)) {
72277b2bc2cSBlue Swirl             raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc);
72320054ef0SBlue Swirl         }
72420054ef0SBlue Swirl         if (!(ss_e2 & DESC_P_MASK)) {
72577b2bc2cSBlue Swirl             raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc);
72620054ef0SBlue Swirl         }
727eaa728eeSbellard         new_stack = 1;
728eaa728eeSbellard         sp_mask = get_sp_mask(ss_e2);
729eaa728eeSbellard         ssp = get_seg_base(ss_e1, ss_e2);
7301110bfe6SPaolo Bonzini     } else  {
731eaa728eeSbellard         /* to same privilege */
73287446327SKevin O'Connor         if (vm86) {
73377b2bc2cSBlue Swirl             raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
73420054ef0SBlue Swirl         }
735eaa728eeSbellard         new_stack = 0;
736eaa728eeSbellard         sp_mask = get_sp_mask(env->segs[R_SS].flags);
737eaa728eeSbellard         ssp = env->segs[R_SS].base;
73808b3ded6Sliguang         esp = env->regs[R_ESP];
739eaa728eeSbellard     }
740eaa728eeSbellard 
741eaa728eeSbellard     shift = type >> 3;
742eaa728eeSbellard 
743eaa728eeSbellard #if 0
744eaa728eeSbellard     /* XXX: check that enough room is available */
745eaa728eeSbellard     push_size = 6 + (new_stack << 2) + (has_error_code << 1);
74687446327SKevin O'Connor     if (vm86) {
747eaa728eeSbellard         push_size += 8;
74820054ef0SBlue Swirl     }
749eaa728eeSbellard     push_size <<= shift;
750eaa728eeSbellard #endif
751eaa728eeSbellard     if (shift == 1) {
752eaa728eeSbellard         if (new_stack) {
75387446327SKevin O'Connor             if (vm86) {
754eaa728eeSbellard                 PUSHL(ssp, esp, sp_mask, env->segs[R_GS].selector);
755eaa728eeSbellard                 PUSHL(ssp, esp, sp_mask, env->segs[R_FS].selector);
756eaa728eeSbellard                 PUSHL(ssp, esp, sp_mask, env->segs[R_DS].selector);
757eaa728eeSbellard                 PUSHL(ssp, esp, sp_mask, env->segs[R_ES].selector);
758eaa728eeSbellard             }
759eaa728eeSbellard             PUSHL(ssp, esp, sp_mask, env->segs[R_SS].selector);
76008b3ded6Sliguang             PUSHL(ssp, esp, sp_mask, env->regs[R_ESP]);
761eaa728eeSbellard         }
762997ff0d9SBlue Swirl         PUSHL(ssp, esp, sp_mask, cpu_compute_eflags(env));
763eaa728eeSbellard         PUSHL(ssp, esp, sp_mask, env->segs[R_CS].selector);
764eaa728eeSbellard         PUSHL(ssp, esp, sp_mask, old_eip);
765eaa728eeSbellard         if (has_error_code) {
766eaa728eeSbellard             PUSHL(ssp, esp, sp_mask, error_code);
767eaa728eeSbellard         }
768eaa728eeSbellard     } else {
769eaa728eeSbellard         if (new_stack) {
77087446327SKevin O'Connor             if (vm86) {
771eaa728eeSbellard                 PUSHW(ssp, esp, sp_mask, env->segs[R_GS].selector);
772eaa728eeSbellard                 PUSHW(ssp, esp, sp_mask, env->segs[R_FS].selector);
773eaa728eeSbellard                 PUSHW(ssp, esp, sp_mask, env->segs[R_DS].selector);
774eaa728eeSbellard                 PUSHW(ssp, esp, sp_mask, env->segs[R_ES].selector);
775eaa728eeSbellard             }
776eaa728eeSbellard             PUSHW(ssp, esp, sp_mask, env->segs[R_SS].selector);
77708b3ded6Sliguang             PUSHW(ssp, esp, sp_mask, env->regs[R_ESP]);
778eaa728eeSbellard         }
779997ff0d9SBlue Swirl         PUSHW(ssp, esp, sp_mask, cpu_compute_eflags(env));
780eaa728eeSbellard         PUSHW(ssp, esp, sp_mask, env->segs[R_CS].selector);
781eaa728eeSbellard         PUSHW(ssp, esp, sp_mask, old_eip);
782eaa728eeSbellard         if (has_error_code) {
783eaa728eeSbellard             PUSHW(ssp, esp, sp_mask, error_code);
784eaa728eeSbellard         }
785eaa728eeSbellard     }
786eaa728eeSbellard 
787fd460606SKevin O'Connor     /* interrupt gate clear IF mask */
788fd460606SKevin O'Connor     if ((type & 1) == 0) {
789fd460606SKevin O'Connor         env->eflags &= ~IF_MASK;
790fd460606SKevin O'Connor     }
791fd460606SKevin O'Connor     env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK);
792fd460606SKevin O'Connor 
793eaa728eeSbellard     if (new_stack) {
79487446327SKevin O'Connor         if (vm86) {
795eaa728eeSbellard             cpu_x86_load_seg_cache(env, R_ES, 0, 0, 0, 0);
796eaa728eeSbellard             cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0, 0);
797eaa728eeSbellard             cpu_x86_load_seg_cache(env, R_FS, 0, 0, 0, 0);
798eaa728eeSbellard             cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0, 0);
799eaa728eeSbellard         }
800eaa728eeSbellard         ss = (ss & ~3) | dpl;
801eaa728eeSbellard         cpu_x86_load_seg_cache(env, R_SS, ss,
802eaa728eeSbellard                                ssp, get_seg_limit(ss_e1, ss_e2), ss_e2);
803eaa728eeSbellard     }
804eaa728eeSbellard     SET_ESP(esp, sp_mask);
805eaa728eeSbellard 
806eaa728eeSbellard     selector = (selector & ~3) | dpl;
807eaa728eeSbellard     cpu_x86_load_seg_cache(env, R_CS, selector,
808eaa728eeSbellard                    get_seg_base(e1, e2),
809eaa728eeSbellard                    get_seg_limit(e1, e2),
810eaa728eeSbellard                    e2);
811eaa728eeSbellard     env->eip = offset;
812eaa728eeSbellard }
813eaa728eeSbellard 
814eaa728eeSbellard #ifdef TARGET_X86_64
815eaa728eeSbellard 
816100ec099SPavel Dovgalyuk #define PUSHQ_RA(sp, val, ra)                   \
817eaa728eeSbellard     {                                           \
818eaa728eeSbellard         sp -= 8;                                \
819100ec099SPavel Dovgalyuk         cpu_stq_kernel_ra(env, sp, (val), ra);  \
820eaa728eeSbellard     }
821eaa728eeSbellard 
822100ec099SPavel Dovgalyuk #define POPQ_RA(sp, val, ra)                    \
823eaa728eeSbellard     {                                           \
824100ec099SPavel Dovgalyuk         val = cpu_ldq_kernel_ra(env, sp, ra);   \
825eaa728eeSbellard         sp += 8;                                \
826eaa728eeSbellard     }
827eaa728eeSbellard 
828100ec099SPavel Dovgalyuk #define PUSHQ(sp, val) PUSHQ_RA(sp, val, 0)
829100ec099SPavel Dovgalyuk #define POPQ(sp, val) POPQ_RA(sp, val, 0)
830100ec099SPavel Dovgalyuk 
8312999a0b2SBlue Swirl static inline target_ulong get_rsp_from_tss(CPUX86State *env, int level)
832eaa728eeSbellard {
8336aa9e42fSRichard Henderson     X86CPU *cpu = env_archcpu(env);
834eaa728eeSbellard     int index;
835eaa728eeSbellard 
836eaa728eeSbellard #if 0
837eaa728eeSbellard     printf("TR: base=" TARGET_FMT_lx " limit=%x\n",
838eaa728eeSbellard            env->tr.base, env->tr.limit);
839eaa728eeSbellard #endif
840eaa728eeSbellard 
84120054ef0SBlue Swirl     if (!(env->tr.flags & DESC_P_MASK)) {
842a47dddd7SAndreas Färber         cpu_abort(CPU(cpu), "invalid tss");
84320054ef0SBlue Swirl     }
844eaa728eeSbellard     index = 8 * level + 4;
84520054ef0SBlue Swirl     if ((index + 7) > env->tr.limit) {
84677b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0A_TSS, env->tr.selector & 0xfffc);
84720054ef0SBlue Swirl     }
848329e607dSBlue Swirl     return cpu_ldq_kernel(env, env->tr.base + index);
849eaa728eeSbellard }
850eaa728eeSbellard 
851eaa728eeSbellard /* 64 bit interrupt */
8522999a0b2SBlue Swirl static void do_interrupt64(CPUX86State *env, int intno, int is_int,
8532999a0b2SBlue Swirl                            int error_code, target_ulong next_eip, int is_hw)
854eaa728eeSbellard {
855eaa728eeSbellard     SegmentCache *dt;
856eaa728eeSbellard     target_ulong ptr;
857eaa728eeSbellard     int type, dpl, selector, cpl, ist;
858eaa728eeSbellard     int has_error_code, new_stack;
859eaa728eeSbellard     uint32_t e1, e2, e3, ss;
860eaa728eeSbellard     target_ulong old_eip, esp, offset;
861eaa728eeSbellard 
862eaa728eeSbellard     has_error_code = 0;
86320054ef0SBlue Swirl     if (!is_int && !is_hw) {
86420054ef0SBlue Swirl         has_error_code = exception_has_error_code(intno);
86520054ef0SBlue Swirl     }
86620054ef0SBlue Swirl     if (is_int) {
867eaa728eeSbellard         old_eip = next_eip;
86820054ef0SBlue Swirl     } else {
869eaa728eeSbellard         old_eip = env->eip;
87020054ef0SBlue Swirl     }
871eaa728eeSbellard 
872eaa728eeSbellard     dt = &env->idt;
87320054ef0SBlue Swirl     if (intno * 16 + 15 > dt->limit) {
87477b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0D_GPF, intno * 16 + 2);
87520054ef0SBlue Swirl     }
876eaa728eeSbellard     ptr = dt->base + intno * 16;
877329e607dSBlue Swirl     e1 = cpu_ldl_kernel(env, ptr);
878329e607dSBlue Swirl     e2 = cpu_ldl_kernel(env, ptr + 4);
879329e607dSBlue Swirl     e3 = cpu_ldl_kernel(env, ptr + 8);
880eaa728eeSbellard     /* check gate type */
881eaa728eeSbellard     type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
882eaa728eeSbellard     switch (type) {
883eaa728eeSbellard     case 14: /* 386 interrupt gate */
884eaa728eeSbellard     case 15: /* 386 trap gate */
885eaa728eeSbellard         break;
886eaa728eeSbellard     default:
88777b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0D_GPF, intno * 16 + 2);
888eaa728eeSbellard         break;
889eaa728eeSbellard     }
890eaa728eeSbellard     dpl = (e2 >> DESC_DPL_SHIFT) & 3;
891eaa728eeSbellard     cpl = env->hflags & HF_CPL_MASK;
8921235fc06Sths     /* check privilege if software int */
89320054ef0SBlue Swirl     if (is_int && dpl < cpl) {
89477b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0D_GPF, intno * 16 + 2);
89520054ef0SBlue Swirl     }
896eaa728eeSbellard     /* check valid bit */
89720054ef0SBlue Swirl     if (!(e2 & DESC_P_MASK)) {
89877b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0B_NOSEG, intno * 16 + 2);
89920054ef0SBlue Swirl     }
900eaa728eeSbellard     selector = e1 >> 16;
901eaa728eeSbellard     offset = ((target_ulong)e3 << 32) | (e2 & 0xffff0000) | (e1 & 0x0000ffff);
902eaa728eeSbellard     ist = e2 & 7;
90320054ef0SBlue Swirl     if ((selector & 0xfffc) == 0) {
90477b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0D_GPF, 0);
90520054ef0SBlue Swirl     }
906eaa728eeSbellard 
9072999a0b2SBlue Swirl     if (load_segment(env, &e1, &e2, selector) != 0) {
90877b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
90920054ef0SBlue Swirl     }
91020054ef0SBlue Swirl     if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK))) {
91177b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
91220054ef0SBlue Swirl     }
913eaa728eeSbellard     dpl = (e2 >> DESC_DPL_SHIFT) & 3;
91420054ef0SBlue Swirl     if (dpl > cpl) {
91577b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
91620054ef0SBlue Swirl     }
91720054ef0SBlue Swirl     if (!(e2 & DESC_P_MASK)) {
91877b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0B_NOSEG, selector & 0xfffc);
91920054ef0SBlue Swirl     }
92020054ef0SBlue Swirl     if (!(e2 & DESC_L_MASK) || (e2 & DESC_B_MASK)) {
92177b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
92220054ef0SBlue Swirl     }
9231110bfe6SPaolo Bonzini     if (e2 & DESC_C_MASK) {
9241110bfe6SPaolo Bonzini         dpl = cpl;
9251110bfe6SPaolo Bonzini     }
9261110bfe6SPaolo Bonzini     if (dpl < cpl || ist != 0) {
927eaa728eeSbellard         /* to inner privilege */
928eaa728eeSbellard         new_stack = 1;
929ae67dc72SPaolo Bonzini         esp = get_rsp_from_tss(env, ist != 0 ? ist + 3 : dpl);
930ae67dc72SPaolo Bonzini         ss = 0;
9311110bfe6SPaolo Bonzini     } else {
932eaa728eeSbellard         /* to same privilege */
93320054ef0SBlue Swirl         if (env->eflags & VM_MASK) {
93477b2bc2cSBlue Swirl             raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
93520054ef0SBlue Swirl         }
936eaa728eeSbellard         new_stack = 0;
93708b3ded6Sliguang         esp = env->regs[R_ESP];
938e95e9b88SWu Xiang     }
939ae67dc72SPaolo Bonzini     esp &= ~0xfLL; /* align stack */
940eaa728eeSbellard 
941eaa728eeSbellard     PUSHQ(esp, env->segs[R_SS].selector);
94208b3ded6Sliguang     PUSHQ(esp, env->regs[R_ESP]);
943997ff0d9SBlue Swirl     PUSHQ(esp, cpu_compute_eflags(env));
944eaa728eeSbellard     PUSHQ(esp, env->segs[R_CS].selector);
945eaa728eeSbellard     PUSHQ(esp, old_eip);
946eaa728eeSbellard     if (has_error_code) {
947eaa728eeSbellard         PUSHQ(esp, error_code);
948eaa728eeSbellard     }
949eaa728eeSbellard 
950fd460606SKevin O'Connor     /* interrupt gate clear IF mask */
951fd460606SKevin O'Connor     if ((type & 1) == 0) {
952fd460606SKevin O'Connor         env->eflags &= ~IF_MASK;
953fd460606SKevin O'Connor     }
954fd460606SKevin O'Connor     env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK);
955fd460606SKevin O'Connor 
956eaa728eeSbellard     if (new_stack) {
957eaa728eeSbellard         ss = 0 | dpl;
958e95e9b88SWu Xiang         cpu_x86_load_seg_cache(env, R_SS, ss, 0, 0, dpl << DESC_DPL_SHIFT);
959eaa728eeSbellard     }
96008b3ded6Sliguang     env->regs[R_ESP] = esp;
961eaa728eeSbellard 
962eaa728eeSbellard     selector = (selector & ~3) | dpl;
963eaa728eeSbellard     cpu_x86_load_seg_cache(env, R_CS, selector,
964eaa728eeSbellard                    get_seg_base(e1, e2),
965eaa728eeSbellard                    get_seg_limit(e1, e2),
966eaa728eeSbellard                    e2);
967eaa728eeSbellard     env->eip = offset;
968eaa728eeSbellard }
969eaa728eeSbellard #endif
970eaa728eeSbellard 
971d9957a8bSblueswir1 #ifdef TARGET_X86_64
972eaa728eeSbellard #if defined(CONFIG_USER_ONLY)
9732999a0b2SBlue Swirl void helper_syscall(CPUX86State *env, int next_eip_addend)
974eaa728eeSbellard {
9756aa9e42fSRichard Henderson     CPUState *cs = env_cpu(env);
97627103424SAndreas Färber 
97727103424SAndreas Färber     cs->exception_index = EXCP_SYSCALL;
978eaa728eeSbellard     env->exception_next_eip = env->eip + next_eip_addend;
9795638d180SAndreas Färber     cpu_loop_exit(cs);
980eaa728eeSbellard }
981eaa728eeSbellard #else
9822999a0b2SBlue Swirl void helper_syscall(CPUX86State *env, int next_eip_addend)
983eaa728eeSbellard {
984eaa728eeSbellard     int selector;
985eaa728eeSbellard 
986eaa728eeSbellard     if (!(env->efer & MSR_EFER_SCE)) {
987100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC());
988eaa728eeSbellard     }
989eaa728eeSbellard     selector = (env->star >> 32) & 0xffff;
990eaa728eeSbellard     if (env->hflags & HF_LMA_MASK) {
991eaa728eeSbellard         int code64;
992eaa728eeSbellard 
993a4165610Sliguang         env->regs[R_ECX] = env->eip + next_eip_addend;
9941a1435ddSRudolf Marek         env->regs[11] = cpu_compute_eflags(env) & ~RF_MASK;
995eaa728eeSbellard 
996eaa728eeSbellard         code64 = env->hflags & HF_CS64_MASK;
997eaa728eeSbellard 
9981a1435ddSRudolf Marek         env->eflags &= ~(env->fmask | RF_MASK);
999fd460606SKevin O'Connor         cpu_load_eflags(env, env->eflags, 0);
1000eaa728eeSbellard         cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc,
1001eaa728eeSbellard                            0, 0xffffffff,
1002eaa728eeSbellard                                DESC_G_MASK | DESC_P_MASK |
1003eaa728eeSbellard                                DESC_S_MASK |
100420054ef0SBlue Swirl                                DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK |
100520054ef0SBlue Swirl                                DESC_L_MASK);
1006eaa728eeSbellard         cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc,
1007eaa728eeSbellard                                0, 0xffffffff,
1008eaa728eeSbellard                                DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1009eaa728eeSbellard                                DESC_S_MASK |
1010eaa728eeSbellard                                DESC_W_MASK | DESC_A_MASK);
101120054ef0SBlue Swirl         if (code64) {
1012eaa728eeSbellard             env->eip = env->lstar;
101320054ef0SBlue Swirl         } else {
1014eaa728eeSbellard             env->eip = env->cstar;
101520054ef0SBlue Swirl         }
1016d9957a8bSblueswir1     } else {
1017a4165610Sliguang         env->regs[R_ECX] = (uint32_t)(env->eip + next_eip_addend);
1018eaa728eeSbellard 
1019fd460606SKevin O'Connor         env->eflags &= ~(IF_MASK | RF_MASK | VM_MASK);
1020eaa728eeSbellard         cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc,
1021eaa728eeSbellard                            0, 0xffffffff,
1022eaa728eeSbellard                                DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1023eaa728eeSbellard                                DESC_S_MASK |
1024eaa728eeSbellard                                DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
1025eaa728eeSbellard         cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc,
1026eaa728eeSbellard                                0, 0xffffffff,
1027eaa728eeSbellard                                DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1028eaa728eeSbellard                                DESC_S_MASK |
1029eaa728eeSbellard                                DESC_W_MASK | DESC_A_MASK);
1030eaa728eeSbellard         env->eip = (uint32_t)env->star;
1031eaa728eeSbellard     }
1032eaa728eeSbellard }
1033eaa728eeSbellard #endif
1034d9957a8bSblueswir1 #endif
1035eaa728eeSbellard 
1036d9957a8bSblueswir1 #ifdef TARGET_X86_64
10372999a0b2SBlue Swirl void helper_sysret(CPUX86State *env, int dflag)
1038eaa728eeSbellard {
1039eaa728eeSbellard     int cpl, selector;
1040eaa728eeSbellard 
1041eaa728eeSbellard     if (!(env->efer & MSR_EFER_SCE)) {
1042100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC());
1043eaa728eeSbellard     }
1044eaa728eeSbellard     cpl = env->hflags & HF_CPL_MASK;
1045eaa728eeSbellard     if (!(env->cr[0] & CR0_PE_MASK) || cpl != 0) {
1046100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC());
1047eaa728eeSbellard     }
1048eaa728eeSbellard     selector = (env->star >> 48) & 0xffff;
1049eaa728eeSbellard     if (env->hflags & HF_LMA_MASK) {
1050fd460606SKevin O'Connor         cpu_load_eflags(env, (uint32_t)(env->regs[11]), TF_MASK | AC_MASK
1051fd460606SKevin O'Connor                         | ID_MASK | IF_MASK | IOPL_MASK | VM_MASK | RF_MASK |
1052fd460606SKevin O'Connor                         NT_MASK);
1053eaa728eeSbellard         if (dflag == 2) {
1054eaa728eeSbellard             cpu_x86_load_seg_cache(env, R_CS, (selector + 16) | 3,
1055eaa728eeSbellard                                    0, 0xffffffff,
1056eaa728eeSbellard                                    DESC_G_MASK | DESC_P_MASK |
1057eaa728eeSbellard                                    DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
1058eaa728eeSbellard                                    DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK |
1059eaa728eeSbellard                                    DESC_L_MASK);
1060a4165610Sliguang             env->eip = env->regs[R_ECX];
1061eaa728eeSbellard         } else {
1062eaa728eeSbellard             cpu_x86_load_seg_cache(env, R_CS, selector | 3,
1063eaa728eeSbellard                                    0, 0xffffffff,
1064eaa728eeSbellard                                    DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1065eaa728eeSbellard                                    DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
1066eaa728eeSbellard                                    DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
1067a4165610Sliguang             env->eip = (uint32_t)env->regs[R_ECX];
1068eaa728eeSbellard         }
1069ac576229SBill Paul         cpu_x86_load_seg_cache(env, R_SS, (selector + 8) | 3,
1070eaa728eeSbellard                                0, 0xffffffff,
1071eaa728eeSbellard                                DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1072eaa728eeSbellard                                DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
1073eaa728eeSbellard                                DESC_W_MASK | DESC_A_MASK);
1074d9957a8bSblueswir1     } else {
1075fd460606SKevin O'Connor         env->eflags |= IF_MASK;
1076eaa728eeSbellard         cpu_x86_load_seg_cache(env, R_CS, selector | 3,
1077eaa728eeSbellard                                0, 0xffffffff,
1078eaa728eeSbellard                                DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1079eaa728eeSbellard                                DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
1080eaa728eeSbellard                                DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
1081a4165610Sliguang         env->eip = (uint32_t)env->regs[R_ECX];
1082ac576229SBill Paul         cpu_x86_load_seg_cache(env, R_SS, (selector + 8) | 3,
1083eaa728eeSbellard                                0, 0xffffffff,
1084eaa728eeSbellard                                DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1085eaa728eeSbellard                                DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
1086eaa728eeSbellard                                DESC_W_MASK | DESC_A_MASK);
1087eaa728eeSbellard     }
1088eaa728eeSbellard }
1089d9957a8bSblueswir1 #endif
1090eaa728eeSbellard 
1091eaa728eeSbellard /* real mode interrupt */
10922999a0b2SBlue Swirl static void do_interrupt_real(CPUX86State *env, int intno, int is_int,
10932999a0b2SBlue Swirl                               int error_code, unsigned int next_eip)
1094eaa728eeSbellard {
1095eaa728eeSbellard     SegmentCache *dt;
1096eaa728eeSbellard     target_ulong ptr, ssp;
1097eaa728eeSbellard     int selector;
1098eaa728eeSbellard     uint32_t offset, esp;
1099eaa728eeSbellard     uint32_t old_cs, old_eip;
1100eaa728eeSbellard 
1101eaa728eeSbellard     /* real mode (simpler!) */
1102eaa728eeSbellard     dt = &env->idt;
110320054ef0SBlue Swirl     if (intno * 4 + 3 > dt->limit) {
110477b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0D_GPF, intno * 8 + 2);
110520054ef0SBlue Swirl     }
1106eaa728eeSbellard     ptr = dt->base + intno * 4;
1107329e607dSBlue Swirl     offset = cpu_lduw_kernel(env, ptr);
1108329e607dSBlue Swirl     selector = cpu_lduw_kernel(env, ptr + 2);
110908b3ded6Sliguang     esp = env->regs[R_ESP];
1110eaa728eeSbellard     ssp = env->segs[R_SS].base;
111120054ef0SBlue Swirl     if (is_int) {
1112eaa728eeSbellard         old_eip = next_eip;
111320054ef0SBlue Swirl     } else {
1114eaa728eeSbellard         old_eip = env->eip;
111520054ef0SBlue Swirl     }
1116eaa728eeSbellard     old_cs = env->segs[R_CS].selector;
1117eaa728eeSbellard     /* XXX: use SS segment size? */
1118997ff0d9SBlue Swirl     PUSHW(ssp, esp, 0xffff, cpu_compute_eflags(env));
1119eaa728eeSbellard     PUSHW(ssp, esp, 0xffff, old_cs);
1120eaa728eeSbellard     PUSHW(ssp, esp, 0xffff, old_eip);
1121eaa728eeSbellard 
1122eaa728eeSbellard     /* update processor state */
112308b3ded6Sliguang     env->regs[R_ESP] = (env->regs[R_ESP] & ~0xffff) | (esp & 0xffff);
1124eaa728eeSbellard     env->eip = offset;
1125eaa728eeSbellard     env->segs[R_CS].selector = selector;
1126eaa728eeSbellard     env->segs[R_CS].base = (selector << 4);
1127eaa728eeSbellard     env->eflags &= ~(IF_MASK | TF_MASK | AC_MASK | RF_MASK);
1128eaa728eeSbellard }
1129eaa728eeSbellard 
1130e694d4e2SBlue Swirl #if defined(CONFIG_USER_ONLY)
113133271823SPeter Maydell /* fake user mode interrupt. is_int is TRUE if coming from the int
113233271823SPeter Maydell  * instruction. next_eip is the env->eip value AFTER the interrupt
113333271823SPeter Maydell  * instruction. It is only relevant if is_int is TRUE or if intno
113433271823SPeter Maydell  * is EXCP_SYSCALL.
113533271823SPeter Maydell  */
11362999a0b2SBlue Swirl static void do_interrupt_user(CPUX86State *env, int intno, int is_int,
11372999a0b2SBlue Swirl                               int error_code, target_ulong next_eip)
1138eaa728eeSbellard {
1139885b7c44SStanislav Shmarov     if (is_int) {
1140eaa728eeSbellard         SegmentCache *dt;
1141eaa728eeSbellard         target_ulong ptr;
1142eaa728eeSbellard         int dpl, cpl, shift;
1143eaa728eeSbellard         uint32_t e2;
1144eaa728eeSbellard 
1145eaa728eeSbellard         dt = &env->idt;
1146eaa728eeSbellard         if (env->hflags & HF_LMA_MASK) {
1147eaa728eeSbellard             shift = 4;
1148eaa728eeSbellard         } else {
1149eaa728eeSbellard             shift = 3;
1150eaa728eeSbellard         }
1151eaa728eeSbellard         ptr = dt->base + (intno << shift);
1152329e607dSBlue Swirl         e2 = cpu_ldl_kernel(env, ptr + 4);
1153eaa728eeSbellard 
1154eaa728eeSbellard         dpl = (e2 >> DESC_DPL_SHIFT) & 3;
1155eaa728eeSbellard         cpl = env->hflags & HF_CPL_MASK;
11561235fc06Sths         /* check privilege if software int */
1157885b7c44SStanislav Shmarov         if (dpl < cpl) {
115877b2bc2cSBlue Swirl             raise_exception_err(env, EXCP0D_GPF, (intno << shift) + 2);
115920054ef0SBlue Swirl         }
1160885b7c44SStanislav Shmarov     }
1161eaa728eeSbellard 
1162eaa728eeSbellard     /* Since we emulate only user space, we cannot do more than
1163eaa728eeSbellard        exiting the emulation with the suitable exception and error
116447575997SJincheng Miao        code. So update EIP for INT 0x80 and EXCP_SYSCALL. */
116547575997SJincheng Miao     if (is_int || intno == EXCP_SYSCALL) {
1166a78d0eabSliguang         env->eip = next_eip;
1167eaa728eeSbellard     }
116820054ef0SBlue Swirl }
1169eaa728eeSbellard 
1170e694d4e2SBlue Swirl #else
1171e694d4e2SBlue Swirl 
11722999a0b2SBlue Swirl static void handle_even_inj(CPUX86State *env, int intno, int is_int,
11732999a0b2SBlue Swirl                             int error_code, int is_hw, int rm)
11742ed51f5bSaliguori {
11756aa9e42fSRichard Henderson     CPUState *cs = env_cpu(env);
1176b216aa6cSPaolo Bonzini     uint32_t event_inj = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb,
117720054ef0SBlue Swirl                                                           control.event_inj));
117820054ef0SBlue Swirl 
11792ed51f5bSaliguori     if (!(event_inj & SVM_EVTINJ_VALID)) {
11802ed51f5bSaliguori         int type;
118120054ef0SBlue Swirl 
118220054ef0SBlue Swirl         if (is_int) {
11832ed51f5bSaliguori             type = SVM_EVTINJ_TYPE_SOFT;
118420054ef0SBlue Swirl         } else {
11852ed51f5bSaliguori             type = SVM_EVTINJ_TYPE_EXEPT;
11862ed51f5bSaliguori         }
118720054ef0SBlue Swirl         event_inj = intno | type | SVM_EVTINJ_VALID;
118820054ef0SBlue Swirl         if (!rm && exception_has_error_code(intno)) {
118920054ef0SBlue Swirl             event_inj |= SVM_EVTINJ_VALID_ERR;
1190b216aa6cSPaolo Bonzini             x86_stl_phys(cs, env->vm_vmcb + offsetof(struct vmcb,
119120054ef0SBlue Swirl                                              control.event_inj_err),
119220054ef0SBlue Swirl                      error_code);
119320054ef0SBlue Swirl         }
1194b216aa6cSPaolo Bonzini         x86_stl_phys(cs,
1195ab1da857SEdgar E. Iglesias                  env->vm_vmcb + offsetof(struct vmcb, control.event_inj),
119620054ef0SBlue Swirl                  event_inj);
11972ed51f5bSaliguori     }
11982ed51f5bSaliguori }
119900ea18d1Saliguori #endif
12002ed51f5bSaliguori 
1201eaa728eeSbellard /*
1202eaa728eeSbellard  * Begin execution of an interruption. is_int is TRUE if coming from
1203a78d0eabSliguang  * the int instruction. next_eip is the env->eip value AFTER the interrupt
1204eaa728eeSbellard  * instruction. It is only relevant if is_int is TRUE.
1205eaa728eeSbellard  */
1206ca4c810aSAndreas Färber static void do_interrupt_all(X86CPU *cpu, int intno, int is_int,
12072999a0b2SBlue Swirl                              int error_code, target_ulong next_eip, int is_hw)
1208eaa728eeSbellard {
1209ca4c810aSAndreas Färber     CPUX86State *env = &cpu->env;
1210ca4c810aSAndreas Färber 
12118fec2b8cSaliguori     if (qemu_loglevel_mask(CPU_LOG_INT)) {
1212eaa728eeSbellard         if ((env->cr[0] & CR0_PE_MASK)) {
1213eaa728eeSbellard             static int count;
121420054ef0SBlue Swirl 
121520054ef0SBlue Swirl             qemu_log("%6d: v=%02x e=%04x i=%d cpl=%d IP=%04x:" TARGET_FMT_lx
121620054ef0SBlue Swirl                      " pc=" TARGET_FMT_lx " SP=%04x:" TARGET_FMT_lx,
1217eaa728eeSbellard                      count, intno, error_code, is_int,
1218eaa728eeSbellard                      env->hflags & HF_CPL_MASK,
1219a78d0eabSliguang                      env->segs[R_CS].selector, env->eip,
1220a78d0eabSliguang                      (int)env->segs[R_CS].base + env->eip,
122108b3ded6Sliguang                      env->segs[R_SS].selector, env->regs[R_ESP]);
1222eaa728eeSbellard             if (intno == 0x0e) {
122393fcfe39Saliguori                 qemu_log(" CR2=" TARGET_FMT_lx, env->cr[2]);
1224eaa728eeSbellard             } else {
12254b34e3adSliguang                 qemu_log(" env->regs[R_EAX]=" TARGET_FMT_lx, env->regs[R_EAX]);
1226eaa728eeSbellard             }
122793fcfe39Saliguori             qemu_log("\n");
1228a0762859SAndreas Färber             log_cpu_state(CPU(cpu), CPU_DUMP_CCOP);
1229eaa728eeSbellard #if 0
1230eaa728eeSbellard             {
1231eaa728eeSbellard                 int i;
12329bd5494eSAdam Lackorzynski                 target_ulong ptr;
123320054ef0SBlue Swirl 
123493fcfe39Saliguori                 qemu_log("       code=");
1235eaa728eeSbellard                 ptr = env->segs[R_CS].base + env->eip;
1236eaa728eeSbellard                 for (i = 0; i < 16; i++) {
123793fcfe39Saliguori                     qemu_log(" %02x", ldub(ptr + i));
1238eaa728eeSbellard                 }
123993fcfe39Saliguori                 qemu_log("\n");
1240eaa728eeSbellard             }
1241eaa728eeSbellard #endif
1242eaa728eeSbellard             count++;
1243eaa728eeSbellard         }
1244eaa728eeSbellard     }
1245eaa728eeSbellard     if (env->cr[0] & CR0_PE_MASK) {
124600ea18d1Saliguori #if !defined(CONFIG_USER_ONLY)
1247f8dc4c64SPaolo Bonzini         if (env->hflags & HF_GUEST_MASK) {
12482999a0b2SBlue Swirl             handle_even_inj(env, intno, is_int, error_code, is_hw, 0);
124920054ef0SBlue Swirl         }
125000ea18d1Saliguori #endif
1251eb38c52cSblueswir1 #ifdef TARGET_X86_64
1252eaa728eeSbellard         if (env->hflags & HF_LMA_MASK) {
12532999a0b2SBlue Swirl             do_interrupt64(env, intno, is_int, error_code, next_eip, is_hw);
1254eaa728eeSbellard         } else
1255eaa728eeSbellard #endif
1256eaa728eeSbellard         {
12572999a0b2SBlue Swirl             do_interrupt_protected(env, intno, is_int, error_code, next_eip,
12582999a0b2SBlue Swirl                                    is_hw);
1259eaa728eeSbellard         }
1260eaa728eeSbellard     } else {
126100ea18d1Saliguori #if !defined(CONFIG_USER_ONLY)
1262f8dc4c64SPaolo Bonzini         if (env->hflags & HF_GUEST_MASK) {
12632999a0b2SBlue Swirl             handle_even_inj(env, intno, is_int, error_code, is_hw, 1);
126420054ef0SBlue Swirl         }
126500ea18d1Saliguori #endif
12662999a0b2SBlue Swirl         do_interrupt_real(env, intno, is_int, error_code, next_eip);
1267eaa728eeSbellard     }
12682ed51f5bSaliguori 
126900ea18d1Saliguori #if !defined(CONFIG_USER_ONLY)
1270f8dc4c64SPaolo Bonzini     if (env->hflags & HF_GUEST_MASK) {
1271fdfba1a2SEdgar E. Iglesias         CPUState *cs = CPU(cpu);
1272b216aa6cSPaolo Bonzini         uint32_t event_inj = x86_ldl_phys(cs, env->vm_vmcb +
127320054ef0SBlue Swirl                                       offsetof(struct vmcb,
127420054ef0SBlue Swirl                                                control.event_inj));
127520054ef0SBlue Swirl 
1276b216aa6cSPaolo Bonzini         x86_stl_phys(cs,
1277ab1da857SEdgar E. Iglesias                  env->vm_vmcb + offsetof(struct vmcb, control.event_inj),
127820054ef0SBlue Swirl                  event_inj & ~SVM_EVTINJ_VALID);
12792ed51f5bSaliguori     }
128000ea18d1Saliguori #endif
1281eaa728eeSbellard }
1282eaa728eeSbellard 
128397a8ea5aSAndreas Färber void x86_cpu_do_interrupt(CPUState *cs)
1284e694d4e2SBlue Swirl {
128597a8ea5aSAndreas Färber     X86CPU *cpu = X86_CPU(cs);
128697a8ea5aSAndreas Färber     CPUX86State *env = &cpu->env;
128797a8ea5aSAndreas Färber 
1288e694d4e2SBlue Swirl #if defined(CONFIG_USER_ONLY)
1289e694d4e2SBlue Swirl     /* if user mode only, we simulate a fake exception
1290e694d4e2SBlue Swirl        which will be handled outside the cpu execution
1291e694d4e2SBlue Swirl        loop */
129227103424SAndreas Färber     do_interrupt_user(env, cs->exception_index,
1293e694d4e2SBlue Swirl                       env->exception_is_int,
1294e694d4e2SBlue Swirl                       env->error_code,
1295e694d4e2SBlue Swirl                       env->exception_next_eip);
1296e694d4e2SBlue Swirl     /* successfully delivered */
1297e694d4e2SBlue Swirl     env->old_exception = -1;
1298e694d4e2SBlue Swirl #else
129910cde894SPaolo Bonzini     if (cs->exception_index >= EXCP_VMEXIT) {
130010cde894SPaolo Bonzini         assert(env->old_exception == -1);
130110cde894SPaolo Bonzini         do_vmexit(env, cs->exception_index - EXCP_VMEXIT, env->error_code);
130210cde894SPaolo Bonzini     } else {
130327103424SAndreas Färber         do_interrupt_all(cpu, cs->exception_index,
1304e694d4e2SBlue Swirl                          env->exception_is_int,
1305e694d4e2SBlue Swirl                          env->error_code,
1306e694d4e2SBlue Swirl                          env->exception_next_eip, 0);
1307e694d4e2SBlue Swirl         /* successfully delivered */
1308e694d4e2SBlue Swirl         env->old_exception = -1;
130910cde894SPaolo Bonzini     }
1310e694d4e2SBlue Swirl #endif
1311e694d4e2SBlue Swirl }
1312e694d4e2SBlue Swirl 
13132999a0b2SBlue Swirl void do_interrupt_x86_hardirq(CPUX86State *env, int intno, int is_hw)
1314e694d4e2SBlue Swirl {
13156aa9e42fSRichard Henderson     do_interrupt_all(env_archcpu(env), intno, 0, 0, 0, is_hw);
1316e694d4e2SBlue Swirl }
1317e694d4e2SBlue Swirl 
131842f53feaSRichard Henderson bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
131942f53feaSRichard Henderson {
132042f53feaSRichard Henderson     X86CPU *cpu = X86_CPU(cs);
132142f53feaSRichard Henderson     CPUX86State *env = &cpu->env;
132292d5f1a4SPaolo Bonzini     int intno;
132342f53feaSRichard Henderson 
132492d5f1a4SPaolo Bonzini     interrupt_request = x86_cpu_pending_interrupt(cs, interrupt_request);
132592d5f1a4SPaolo Bonzini     if (!interrupt_request) {
132692d5f1a4SPaolo Bonzini         return false;
132792d5f1a4SPaolo Bonzini     }
132892d5f1a4SPaolo Bonzini 
132992d5f1a4SPaolo Bonzini     /* Don't process multiple interrupt requests in a single call.
133092d5f1a4SPaolo Bonzini      * This is required to make icount-driven execution deterministic.
133192d5f1a4SPaolo Bonzini      */
133292d5f1a4SPaolo Bonzini     switch (interrupt_request) {
133342f53feaSRichard Henderson #if !defined(CONFIG_USER_ONLY)
133492d5f1a4SPaolo Bonzini     case CPU_INTERRUPT_POLL:
133542f53feaSRichard Henderson         cs->interrupt_request &= ~CPU_INTERRUPT_POLL;
133642f53feaSRichard Henderson         apic_poll_irq(cpu->apic_state);
133792d5f1a4SPaolo Bonzini         break;
133842f53feaSRichard Henderson #endif
133992d5f1a4SPaolo Bonzini     case CPU_INTERRUPT_SIPI:
134042f53feaSRichard Henderson         do_cpu_sipi(cpu);
134192d5f1a4SPaolo Bonzini         break;
134292d5f1a4SPaolo Bonzini     case CPU_INTERRUPT_SMI:
134365c9d60aSPaolo Bonzini         cpu_svm_check_intercept_param(env, SVM_EXIT_SMI, 0, 0);
134442f53feaSRichard Henderson         cs->interrupt_request &= ~CPU_INTERRUPT_SMI;
134542f53feaSRichard Henderson         do_smm_enter(cpu);
134692d5f1a4SPaolo Bonzini         break;
134792d5f1a4SPaolo Bonzini     case CPU_INTERRUPT_NMI:
134802f7fd25SJan Kiszka         cpu_svm_check_intercept_param(env, SVM_EXIT_NMI, 0, 0);
134942f53feaSRichard Henderson         cs->interrupt_request &= ~CPU_INTERRUPT_NMI;
135042f53feaSRichard Henderson         env->hflags2 |= HF2_NMI_MASK;
135142f53feaSRichard Henderson         do_interrupt_x86_hardirq(env, EXCP02_NMI, 1);
135292d5f1a4SPaolo Bonzini         break;
135392d5f1a4SPaolo Bonzini     case CPU_INTERRUPT_MCE:
135442f53feaSRichard Henderson         cs->interrupt_request &= ~CPU_INTERRUPT_MCE;
135542f53feaSRichard Henderson         do_interrupt_x86_hardirq(env, EXCP12_MCHK, 0);
135692d5f1a4SPaolo Bonzini         break;
135792d5f1a4SPaolo Bonzini     case CPU_INTERRUPT_HARD:
135865c9d60aSPaolo Bonzini         cpu_svm_check_intercept_param(env, SVM_EXIT_INTR, 0, 0);
135942f53feaSRichard Henderson         cs->interrupt_request &= ~(CPU_INTERRUPT_HARD |
136042f53feaSRichard Henderson                                    CPU_INTERRUPT_VIRQ);
136142f53feaSRichard Henderson         intno = cpu_get_pic_interrupt(env);
136242f53feaSRichard Henderson         qemu_log_mask(CPU_LOG_TB_IN_ASM,
136342f53feaSRichard Henderson                       "Servicing hardware INT=0x%02x\n", intno);
136442f53feaSRichard Henderson         do_interrupt_x86_hardirq(env, intno, 1);
136592d5f1a4SPaolo Bonzini         break;
136642f53feaSRichard Henderson #if !defined(CONFIG_USER_ONLY)
136792d5f1a4SPaolo Bonzini     case CPU_INTERRUPT_VIRQ:
136842f53feaSRichard Henderson         /* FIXME: this should respect TPR */
136965c9d60aSPaolo Bonzini         cpu_svm_check_intercept_param(env, SVM_EXIT_VINTR, 0, 0);
1370b216aa6cSPaolo Bonzini         intno = x86_ldl_phys(cs, env->vm_vmcb
137142f53feaSRichard Henderson                              + offsetof(struct vmcb, control.int_vector));
137242f53feaSRichard Henderson         qemu_log_mask(CPU_LOG_TB_IN_ASM,
137342f53feaSRichard Henderson                       "Servicing virtual hardware INT=0x%02x\n", intno);
137442f53feaSRichard Henderson         do_interrupt_x86_hardirq(env, intno, 1);
137542f53feaSRichard Henderson         cs->interrupt_request &= ~CPU_INTERRUPT_VIRQ;
137692d5f1a4SPaolo Bonzini         break;
137742f53feaSRichard Henderson #endif
137842f53feaSRichard Henderson     }
137942f53feaSRichard Henderson 
138092d5f1a4SPaolo Bonzini     /* Ensure that no TB jump will be modified as the program flow was changed.  */
138192d5f1a4SPaolo Bonzini     return true;
138242f53feaSRichard Henderson }
138342f53feaSRichard Henderson 
13842999a0b2SBlue Swirl void helper_lldt(CPUX86State *env, int selector)
1385eaa728eeSbellard {
1386eaa728eeSbellard     SegmentCache *dt;
1387eaa728eeSbellard     uint32_t e1, e2;
1388eaa728eeSbellard     int index, entry_limit;
1389eaa728eeSbellard     target_ulong ptr;
1390eaa728eeSbellard 
1391eaa728eeSbellard     selector &= 0xffff;
1392eaa728eeSbellard     if ((selector & 0xfffc) == 0) {
1393eaa728eeSbellard         /* XXX: NULL selector case: invalid LDT */
1394eaa728eeSbellard         env->ldt.base = 0;
1395eaa728eeSbellard         env->ldt.limit = 0;
1396eaa728eeSbellard     } else {
139720054ef0SBlue Swirl         if (selector & 0x4) {
1398100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
139920054ef0SBlue Swirl         }
1400eaa728eeSbellard         dt = &env->gdt;
1401eaa728eeSbellard         index = selector & ~7;
1402eaa728eeSbellard #ifdef TARGET_X86_64
140320054ef0SBlue Swirl         if (env->hflags & HF_LMA_MASK) {
1404eaa728eeSbellard             entry_limit = 15;
140520054ef0SBlue Swirl         } else
1406eaa728eeSbellard #endif
140720054ef0SBlue Swirl         {
1408eaa728eeSbellard             entry_limit = 7;
140920054ef0SBlue Swirl         }
141020054ef0SBlue Swirl         if ((index + entry_limit) > dt->limit) {
1411100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
141220054ef0SBlue Swirl         }
1413eaa728eeSbellard         ptr = dt->base + index;
1414100ec099SPavel Dovgalyuk         e1 = cpu_ldl_kernel_ra(env, ptr, GETPC());
1415100ec099SPavel Dovgalyuk         e2 = cpu_ldl_kernel_ra(env, ptr + 4, GETPC());
141620054ef0SBlue Swirl         if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2) {
1417100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
141820054ef0SBlue Swirl         }
141920054ef0SBlue Swirl         if (!(e2 & DESC_P_MASK)) {
1420100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0B_NOSEG, selector & 0xfffc, GETPC());
142120054ef0SBlue Swirl         }
1422eaa728eeSbellard #ifdef TARGET_X86_64
1423eaa728eeSbellard         if (env->hflags & HF_LMA_MASK) {
1424eaa728eeSbellard             uint32_t e3;
142520054ef0SBlue Swirl 
1426100ec099SPavel Dovgalyuk             e3 = cpu_ldl_kernel_ra(env, ptr + 8, GETPC());
1427eaa728eeSbellard             load_seg_cache_raw_dt(&env->ldt, e1, e2);
1428eaa728eeSbellard             env->ldt.base |= (target_ulong)e3 << 32;
1429eaa728eeSbellard         } else
1430eaa728eeSbellard #endif
1431eaa728eeSbellard         {
1432eaa728eeSbellard             load_seg_cache_raw_dt(&env->ldt, e1, e2);
1433eaa728eeSbellard         }
1434eaa728eeSbellard     }
1435eaa728eeSbellard     env->ldt.selector = selector;
1436eaa728eeSbellard }
1437eaa728eeSbellard 
14382999a0b2SBlue Swirl void helper_ltr(CPUX86State *env, int selector)
1439eaa728eeSbellard {
1440eaa728eeSbellard     SegmentCache *dt;
1441eaa728eeSbellard     uint32_t e1, e2;
1442eaa728eeSbellard     int index, type, entry_limit;
1443eaa728eeSbellard     target_ulong ptr;
1444eaa728eeSbellard 
1445eaa728eeSbellard     selector &= 0xffff;
1446eaa728eeSbellard     if ((selector & 0xfffc) == 0) {
1447eaa728eeSbellard         /* NULL selector case: invalid TR */
1448eaa728eeSbellard         env->tr.base = 0;
1449eaa728eeSbellard         env->tr.limit = 0;
1450eaa728eeSbellard         env->tr.flags = 0;
1451eaa728eeSbellard     } else {
145220054ef0SBlue Swirl         if (selector & 0x4) {
1453100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
145420054ef0SBlue Swirl         }
1455eaa728eeSbellard         dt = &env->gdt;
1456eaa728eeSbellard         index = selector & ~7;
1457eaa728eeSbellard #ifdef TARGET_X86_64
145820054ef0SBlue Swirl         if (env->hflags & HF_LMA_MASK) {
1459eaa728eeSbellard             entry_limit = 15;
146020054ef0SBlue Swirl         } else
1461eaa728eeSbellard #endif
146220054ef0SBlue Swirl         {
1463eaa728eeSbellard             entry_limit = 7;
146420054ef0SBlue Swirl         }
146520054ef0SBlue Swirl         if ((index + entry_limit) > dt->limit) {
1466100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
146720054ef0SBlue Swirl         }
1468eaa728eeSbellard         ptr = dt->base + index;
1469100ec099SPavel Dovgalyuk         e1 = cpu_ldl_kernel_ra(env, ptr, GETPC());
1470100ec099SPavel Dovgalyuk         e2 = cpu_ldl_kernel_ra(env, ptr + 4, GETPC());
1471eaa728eeSbellard         type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
1472eaa728eeSbellard         if ((e2 & DESC_S_MASK) ||
147320054ef0SBlue Swirl             (type != 1 && type != 9)) {
1474100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
147520054ef0SBlue Swirl         }
147620054ef0SBlue Swirl         if (!(e2 & DESC_P_MASK)) {
1477100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0B_NOSEG, selector & 0xfffc, GETPC());
147820054ef0SBlue Swirl         }
1479eaa728eeSbellard #ifdef TARGET_X86_64
1480eaa728eeSbellard         if (env->hflags & HF_LMA_MASK) {
1481eaa728eeSbellard             uint32_t e3, e4;
148220054ef0SBlue Swirl 
1483100ec099SPavel Dovgalyuk             e3 = cpu_ldl_kernel_ra(env, ptr + 8, GETPC());
1484100ec099SPavel Dovgalyuk             e4 = cpu_ldl_kernel_ra(env, ptr + 12, GETPC());
148520054ef0SBlue Swirl             if ((e4 >> DESC_TYPE_SHIFT) & 0xf) {
1486100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
148720054ef0SBlue Swirl             }
1488eaa728eeSbellard             load_seg_cache_raw_dt(&env->tr, e1, e2);
1489eaa728eeSbellard             env->tr.base |= (target_ulong)e3 << 32;
1490eaa728eeSbellard         } else
1491eaa728eeSbellard #endif
1492eaa728eeSbellard         {
1493eaa728eeSbellard             load_seg_cache_raw_dt(&env->tr, e1, e2);
1494eaa728eeSbellard         }
1495eaa728eeSbellard         e2 |= DESC_TSS_BUSY_MASK;
1496100ec099SPavel Dovgalyuk         cpu_stl_kernel_ra(env, ptr + 4, e2, GETPC());
1497eaa728eeSbellard     }
1498eaa728eeSbellard     env->tr.selector = selector;
1499eaa728eeSbellard }
1500eaa728eeSbellard 
1501eaa728eeSbellard /* only works if protected mode and not VM86. seg_reg must be != R_CS */
15022999a0b2SBlue Swirl void helper_load_seg(CPUX86State *env, int seg_reg, int selector)
1503eaa728eeSbellard {
1504eaa728eeSbellard     uint32_t e1, e2;
1505eaa728eeSbellard     int cpl, dpl, rpl;
1506eaa728eeSbellard     SegmentCache *dt;
1507eaa728eeSbellard     int index;
1508eaa728eeSbellard     target_ulong ptr;
1509eaa728eeSbellard 
1510eaa728eeSbellard     selector &= 0xffff;
1511eaa728eeSbellard     cpl = env->hflags & HF_CPL_MASK;
1512eaa728eeSbellard     if ((selector & 0xfffc) == 0) {
1513eaa728eeSbellard         /* null selector case */
1514eaa728eeSbellard         if (seg_reg == R_SS
1515eaa728eeSbellard #ifdef TARGET_X86_64
1516eaa728eeSbellard             && (!(env->hflags & HF_CS64_MASK) || cpl == 3)
1517eaa728eeSbellard #endif
151820054ef0SBlue Swirl             ) {
1519100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC());
152020054ef0SBlue Swirl         }
1521eaa728eeSbellard         cpu_x86_load_seg_cache(env, seg_reg, selector, 0, 0, 0);
1522eaa728eeSbellard     } else {
1523eaa728eeSbellard 
152420054ef0SBlue Swirl         if (selector & 0x4) {
1525eaa728eeSbellard             dt = &env->ldt;
152620054ef0SBlue Swirl         } else {
1527eaa728eeSbellard             dt = &env->gdt;
152820054ef0SBlue Swirl         }
1529eaa728eeSbellard         index = selector & ~7;
153020054ef0SBlue Swirl         if ((index + 7) > dt->limit) {
1531100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
153220054ef0SBlue Swirl         }
1533eaa728eeSbellard         ptr = dt->base + index;
1534100ec099SPavel Dovgalyuk         e1 = cpu_ldl_kernel_ra(env, ptr, GETPC());
1535100ec099SPavel Dovgalyuk         e2 = cpu_ldl_kernel_ra(env, ptr + 4, GETPC());
1536eaa728eeSbellard 
153720054ef0SBlue Swirl         if (!(e2 & DESC_S_MASK)) {
1538100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
153920054ef0SBlue Swirl         }
1540eaa728eeSbellard         rpl = selector & 3;
1541eaa728eeSbellard         dpl = (e2 >> DESC_DPL_SHIFT) & 3;
1542eaa728eeSbellard         if (seg_reg == R_SS) {
1543eaa728eeSbellard             /* must be writable segment */
154420054ef0SBlue Swirl             if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK)) {
1545100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
154620054ef0SBlue Swirl             }
154720054ef0SBlue Swirl             if (rpl != cpl || dpl != cpl) {
1548100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
154920054ef0SBlue Swirl             }
1550eaa728eeSbellard         } else {
1551eaa728eeSbellard             /* must be readable segment */
155220054ef0SBlue Swirl             if ((e2 & (DESC_CS_MASK | DESC_R_MASK)) == DESC_CS_MASK) {
1553100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
155420054ef0SBlue Swirl             }
1555eaa728eeSbellard 
1556eaa728eeSbellard             if (!(e2 & DESC_CS_MASK) || !(e2 & DESC_C_MASK)) {
1557eaa728eeSbellard                 /* if not conforming code, test rights */
155820054ef0SBlue Swirl                 if (dpl < cpl || dpl < rpl) {
1559100ec099SPavel Dovgalyuk                     raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
1560eaa728eeSbellard                 }
1561eaa728eeSbellard             }
156220054ef0SBlue Swirl         }
1563eaa728eeSbellard 
1564eaa728eeSbellard         if (!(e2 & DESC_P_MASK)) {
156520054ef0SBlue Swirl             if (seg_reg == R_SS) {
1566100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0C_STACK, selector & 0xfffc, GETPC());
156720054ef0SBlue Swirl             } else {
1568100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0B_NOSEG, selector & 0xfffc, GETPC());
1569eaa728eeSbellard             }
157020054ef0SBlue Swirl         }
1571eaa728eeSbellard 
1572eaa728eeSbellard         /* set the access bit if not already set */
1573eaa728eeSbellard         if (!(e2 & DESC_A_MASK)) {
1574eaa728eeSbellard             e2 |= DESC_A_MASK;
1575100ec099SPavel Dovgalyuk             cpu_stl_kernel_ra(env, ptr + 4, e2, GETPC());
1576eaa728eeSbellard         }
1577eaa728eeSbellard 
1578eaa728eeSbellard         cpu_x86_load_seg_cache(env, seg_reg, selector,
1579eaa728eeSbellard                        get_seg_base(e1, e2),
1580eaa728eeSbellard                        get_seg_limit(e1, e2),
1581eaa728eeSbellard                        e2);
1582eaa728eeSbellard #if 0
158393fcfe39Saliguori         qemu_log("load_seg: sel=0x%04x base=0x%08lx limit=0x%08lx flags=%08x\n",
1584eaa728eeSbellard                 selector, (unsigned long)sc->base, sc->limit, sc->flags);
1585eaa728eeSbellard #endif
1586eaa728eeSbellard     }
1587eaa728eeSbellard }
1588eaa728eeSbellard 
1589eaa728eeSbellard /* protected mode jump */
15902999a0b2SBlue Swirl void helper_ljmp_protected(CPUX86State *env, int new_cs, target_ulong new_eip,
1591100ec099SPavel Dovgalyuk                            target_ulong next_eip)
1592eaa728eeSbellard {
1593eaa728eeSbellard     int gate_cs, type;
1594eaa728eeSbellard     uint32_t e1, e2, cpl, dpl, rpl, limit;
1595eaa728eeSbellard 
159620054ef0SBlue Swirl     if ((new_cs & 0xfffc) == 0) {
1597100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC());
159820054ef0SBlue Swirl     }
1599100ec099SPavel Dovgalyuk     if (load_segment_ra(env, &e1, &e2, new_cs, GETPC()) != 0) {
1600100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
160120054ef0SBlue Swirl     }
1602eaa728eeSbellard     cpl = env->hflags & HF_CPL_MASK;
1603eaa728eeSbellard     if (e2 & DESC_S_MASK) {
160420054ef0SBlue Swirl         if (!(e2 & DESC_CS_MASK)) {
1605100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
160620054ef0SBlue Swirl         }
1607eaa728eeSbellard         dpl = (e2 >> DESC_DPL_SHIFT) & 3;
1608eaa728eeSbellard         if (e2 & DESC_C_MASK) {
1609eaa728eeSbellard             /* conforming code segment */
161020054ef0SBlue Swirl             if (dpl > cpl) {
1611100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
161220054ef0SBlue Swirl             }
1613eaa728eeSbellard         } else {
1614eaa728eeSbellard             /* non conforming code segment */
1615eaa728eeSbellard             rpl = new_cs & 3;
161620054ef0SBlue Swirl             if (rpl > cpl) {
1617100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
1618eaa728eeSbellard             }
161920054ef0SBlue Swirl             if (dpl != cpl) {
1620100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
162120054ef0SBlue Swirl             }
162220054ef0SBlue Swirl         }
162320054ef0SBlue Swirl         if (!(e2 & DESC_P_MASK)) {
1624100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, GETPC());
162520054ef0SBlue Swirl         }
1626eaa728eeSbellard         limit = get_seg_limit(e1, e2);
1627eaa728eeSbellard         if (new_eip > limit &&
1628db7196dbSAndrew Oates             (!(env->hflags & HF_LMA_MASK) || !(e2 & DESC_L_MASK))) {
1629db7196dbSAndrew Oates             raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC());
163020054ef0SBlue Swirl         }
1631eaa728eeSbellard         cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
1632eaa728eeSbellard                        get_seg_base(e1, e2), limit, e2);
1633a78d0eabSliguang         env->eip = new_eip;
1634eaa728eeSbellard     } else {
1635eaa728eeSbellard         /* jump to call or task gate */
1636eaa728eeSbellard         dpl = (e2 >> DESC_DPL_SHIFT) & 3;
1637eaa728eeSbellard         rpl = new_cs & 3;
1638eaa728eeSbellard         cpl = env->hflags & HF_CPL_MASK;
1639eaa728eeSbellard         type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
16400aca0605SAndrew Oates 
16410aca0605SAndrew Oates #ifdef TARGET_X86_64
16420aca0605SAndrew Oates         if (env->efer & MSR_EFER_LMA) {
16430aca0605SAndrew Oates             if (type != 12) {
16440aca0605SAndrew Oates                 raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
16450aca0605SAndrew Oates             }
16460aca0605SAndrew Oates         }
16470aca0605SAndrew Oates #endif
1648eaa728eeSbellard         switch (type) {
1649eaa728eeSbellard         case 1: /* 286 TSS */
1650eaa728eeSbellard         case 9: /* 386 TSS */
1651eaa728eeSbellard         case 5: /* task gate */
165220054ef0SBlue Swirl             if (dpl < cpl || dpl < rpl) {
1653100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
165420054ef0SBlue Swirl             }
1655100ec099SPavel Dovgalyuk             switch_tss_ra(env, new_cs, e1, e2, SWITCH_TSS_JMP, next_eip, GETPC());
1656eaa728eeSbellard             break;
1657eaa728eeSbellard         case 4: /* 286 call gate */
1658eaa728eeSbellard         case 12: /* 386 call gate */
165920054ef0SBlue Swirl             if ((dpl < cpl) || (dpl < rpl)) {
1660100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
166120054ef0SBlue Swirl             }
166220054ef0SBlue Swirl             if (!(e2 & DESC_P_MASK)) {
1663100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, GETPC());
166420054ef0SBlue Swirl             }
1665eaa728eeSbellard             gate_cs = e1 >> 16;
1666eaa728eeSbellard             new_eip = (e1 & 0xffff);
166720054ef0SBlue Swirl             if (type == 12) {
1668eaa728eeSbellard                 new_eip |= (e2 & 0xffff0000);
166920054ef0SBlue Swirl             }
16700aca0605SAndrew Oates 
16710aca0605SAndrew Oates #ifdef TARGET_X86_64
16720aca0605SAndrew Oates             if (env->efer & MSR_EFER_LMA) {
16730aca0605SAndrew Oates                 /* load the upper 8 bytes of the 64-bit call gate */
16740aca0605SAndrew Oates                 if (load_segment_ra(env, &e1, &e2, new_cs + 8, GETPC())) {
16750aca0605SAndrew Oates                     raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc,
16760aca0605SAndrew Oates                                            GETPC());
16770aca0605SAndrew Oates                 }
16780aca0605SAndrew Oates                 type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
16790aca0605SAndrew Oates                 if (type != 0) {
16800aca0605SAndrew Oates                     raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc,
16810aca0605SAndrew Oates                                            GETPC());
16820aca0605SAndrew Oates                 }
16830aca0605SAndrew Oates                 new_eip |= ((target_ulong)e1) << 32;
16840aca0605SAndrew Oates             }
16850aca0605SAndrew Oates #endif
16860aca0605SAndrew Oates 
1687100ec099SPavel Dovgalyuk             if (load_segment_ra(env, &e1, &e2, gate_cs, GETPC()) != 0) {
1688100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC());
168920054ef0SBlue Swirl             }
1690eaa728eeSbellard             dpl = (e2 >> DESC_DPL_SHIFT) & 3;
1691eaa728eeSbellard             /* must be code segment */
1692eaa728eeSbellard             if (((e2 & (DESC_S_MASK | DESC_CS_MASK)) !=
169320054ef0SBlue Swirl                  (DESC_S_MASK | DESC_CS_MASK))) {
1694100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC());
169520054ef0SBlue Swirl             }
1696eaa728eeSbellard             if (((e2 & DESC_C_MASK) && (dpl > cpl)) ||
169720054ef0SBlue Swirl                 (!(e2 & DESC_C_MASK) && (dpl != cpl))) {
1698100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC());
169920054ef0SBlue Swirl             }
17000aca0605SAndrew Oates #ifdef TARGET_X86_64
17010aca0605SAndrew Oates             if (env->efer & MSR_EFER_LMA) {
17020aca0605SAndrew Oates                 if (!(e2 & DESC_L_MASK)) {
17030aca0605SAndrew Oates                     raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC());
17040aca0605SAndrew Oates                 }
17050aca0605SAndrew Oates                 if (e2 & DESC_B_MASK) {
17060aca0605SAndrew Oates                     raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC());
17070aca0605SAndrew Oates                 }
17080aca0605SAndrew Oates             }
17090aca0605SAndrew Oates #endif
171020054ef0SBlue Swirl             if (!(e2 & DESC_P_MASK)) {
1711100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC());
171220054ef0SBlue Swirl             }
1713eaa728eeSbellard             limit = get_seg_limit(e1, e2);
17140aca0605SAndrew Oates             if (new_eip > limit &&
17150aca0605SAndrew Oates                 (!(env->hflags & HF_LMA_MASK) || !(e2 & DESC_L_MASK))) {
1716100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC());
171720054ef0SBlue Swirl             }
1718eaa728eeSbellard             cpu_x86_load_seg_cache(env, R_CS, (gate_cs & 0xfffc) | cpl,
1719eaa728eeSbellard                                    get_seg_base(e1, e2), limit, e2);
1720a78d0eabSliguang             env->eip = new_eip;
1721eaa728eeSbellard             break;
1722eaa728eeSbellard         default:
1723100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
1724eaa728eeSbellard             break;
1725eaa728eeSbellard         }
1726eaa728eeSbellard     }
1727eaa728eeSbellard }
1728eaa728eeSbellard 
1729eaa728eeSbellard /* real mode call */
17302999a0b2SBlue Swirl void helper_lcall_real(CPUX86State *env, int new_cs, target_ulong new_eip1,
1731eaa728eeSbellard                        int shift, int next_eip)
1732eaa728eeSbellard {
1733eaa728eeSbellard     int new_eip;
1734eaa728eeSbellard     uint32_t esp, esp_mask;
1735eaa728eeSbellard     target_ulong ssp;
1736eaa728eeSbellard 
1737eaa728eeSbellard     new_eip = new_eip1;
173808b3ded6Sliguang     esp = env->regs[R_ESP];
1739eaa728eeSbellard     esp_mask = get_sp_mask(env->segs[R_SS].flags);
1740eaa728eeSbellard     ssp = env->segs[R_SS].base;
1741eaa728eeSbellard     if (shift) {
1742100ec099SPavel Dovgalyuk         PUSHL_RA(ssp, esp, esp_mask, env->segs[R_CS].selector, GETPC());
1743100ec099SPavel Dovgalyuk         PUSHL_RA(ssp, esp, esp_mask, next_eip, GETPC());
1744eaa728eeSbellard     } else {
1745100ec099SPavel Dovgalyuk         PUSHW_RA(ssp, esp, esp_mask, env->segs[R_CS].selector, GETPC());
1746100ec099SPavel Dovgalyuk         PUSHW_RA(ssp, esp, esp_mask, next_eip, GETPC());
1747eaa728eeSbellard     }
1748eaa728eeSbellard 
1749eaa728eeSbellard     SET_ESP(esp, esp_mask);
1750eaa728eeSbellard     env->eip = new_eip;
1751eaa728eeSbellard     env->segs[R_CS].selector = new_cs;
1752eaa728eeSbellard     env->segs[R_CS].base = (new_cs << 4);
1753eaa728eeSbellard }
1754eaa728eeSbellard 
1755eaa728eeSbellard /* protected mode call */
17562999a0b2SBlue Swirl void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip,
1757100ec099SPavel Dovgalyuk                             int shift, target_ulong next_eip)
1758eaa728eeSbellard {
1759eaa728eeSbellard     int new_stack, i;
17600aca0605SAndrew Oates     uint32_t e1, e2, cpl, dpl, rpl, selector, param_count;
17610aca0605SAndrew Oates     uint32_t ss = 0, ss_e1 = 0, ss_e2 = 0, type, ss_dpl, sp_mask;
1762eaa728eeSbellard     uint32_t val, limit, old_sp_mask;
17630aca0605SAndrew Oates     target_ulong ssp, old_ssp, offset, sp;
1764eaa728eeSbellard 
17650aca0605SAndrew Oates     LOG_PCALL("lcall %04x:" TARGET_FMT_lx " s=%d\n", new_cs, new_eip, shift);
17666aa9e42fSRichard Henderson     LOG_PCALL_STATE(env_cpu(env));
176720054ef0SBlue Swirl     if ((new_cs & 0xfffc) == 0) {
1768100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC());
176920054ef0SBlue Swirl     }
1770100ec099SPavel Dovgalyuk     if (load_segment_ra(env, &e1, &e2, new_cs, GETPC()) != 0) {
1771100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
177220054ef0SBlue Swirl     }
1773eaa728eeSbellard     cpl = env->hflags & HF_CPL_MASK;
1774d12d51d5Saliguori     LOG_PCALL("desc=%08x:%08x\n", e1, e2);
1775eaa728eeSbellard     if (e2 & DESC_S_MASK) {
177620054ef0SBlue Swirl         if (!(e2 & DESC_CS_MASK)) {
1777100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
177820054ef0SBlue Swirl         }
1779eaa728eeSbellard         dpl = (e2 >> DESC_DPL_SHIFT) & 3;
1780eaa728eeSbellard         if (e2 & DESC_C_MASK) {
1781eaa728eeSbellard             /* conforming code segment */
178220054ef0SBlue Swirl             if (dpl > cpl) {
1783100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
178420054ef0SBlue Swirl             }
1785eaa728eeSbellard         } else {
1786eaa728eeSbellard             /* non conforming code segment */
1787eaa728eeSbellard             rpl = new_cs & 3;
178820054ef0SBlue Swirl             if (rpl > cpl) {
1789100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
1790eaa728eeSbellard             }
179120054ef0SBlue Swirl             if (dpl != cpl) {
1792100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
179320054ef0SBlue Swirl             }
179420054ef0SBlue Swirl         }
179520054ef0SBlue Swirl         if (!(e2 & DESC_P_MASK)) {
1796100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, GETPC());
179720054ef0SBlue Swirl         }
1798eaa728eeSbellard 
1799eaa728eeSbellard #ifdef TARGET_X86_64
1800eaa728eeSbellard         /* XXX: check 16/32 bit cases in long mode */
1801eaa728eeSbellard         if (shift == 2) {
1802eaa728eeSbellard             target_ulong rsp;
180320054ef0SBlue Swirl 
1804eaa728eeSbellard             /* 64 bit case */
180508b3ded6Sliguang             rsp = env->regs[R_ESP];
1806100ec099SPavel Dovgalyuk             PUSHQ_RA(rsp, env->segs[R_CS].selector, GETPC());
1807100ec099SPavel Dovgalyuk             PUSHQ_RA(rsp, next_eip, GETPC());
1808eaa728eeSbellard             /* from this point, not restartable */
180908b3ded6Sliguang             env->regs[R_ESP] = rsp;
1810eaa728eeSbellard             cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
1811eaa728eeSbellard                                    get_seg_base(e1, e2),
1812eaa728eeSbellard                                    get_seg_limit(e1, e2), e2);
1813a78d0eabSliguang             env->eip = new_eip;
1814eaa728eeSbellard         } else
1815eaa728eeSbellard #endif
1816eaa728eeSbellard         {
181708b3ded6Sliguang             sp = env->regs[R_ESP];
1818eaa728eeSbellard             sp_mask = get_sp_mask(env->segs[R_SS].flags);
1819eaa728eeSbellard             ssp = env->segs[R_SS].base;
1820eaa728eeSbellard             if (shift) {
1821100ec099SPavel Dovgalyuk                 PUSHL_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC());
1822100ec099SPavel Dovgalyuk                 PUSHL_RA(ssp, sp, sp_mask, next_eip, GETPC());
1823eaa728eeSbellard             } else {
1824100ec099SPavel Dovgalyuk                 PUSHW_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC());
1825100ec099SPavel Dovgalyuk                 PUSHW_RA(ssp, sp, sp_mask, next_eip, GETPC());
1826eaa728eeSbellard             }
1827eaa728eeSbellard 
1828eaa728eeSbellard             limit = get_seg_limit(e1, e2);
182920054ef0SBlue Swirl             if (new_eip > limit) {
1830100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
183120054ef0SBlue Swirl             }
1832eaa728eeSbellard             /* from this point, not restartable */
1833eaa728eeSbellard             SET_ESP(sp, sp_mask);
1834eaa728eeSbellard             cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
1835eaa728eeSbellard                                    get_seg_base(e1, e2), limit, e2);
1836a78d0eabSliguang             env->eip = new_eip;
1837eaa728eeSbellard         }
1838eaa728eeSbellard     } else {
1839eaa728eeSbellard         /* check gate type */
1840eaa728eeSbellard         type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
1841eaa728eeSbellard         dpl = (e2 >> DESC_DPL_SHIFT) & 3;
1842eaa728eeSbellard         rpl = new_cs & 3;
18430aca0605SAndrew Oates 
18440aca0605SAndrew Oates #ifdef TARGET_X86_64
18450aca0605SAndrew Oates         if (env->efer & MSR_EFER_LMA) {
18460aca0605SAndrew Oates             if (type != 12) {
18470aca0605SAndrew Oates                 raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
18480aca0605SAndrew Oates             }
18490aca0605SAndrew Oates         }
18500aca0605SAndrew Oates #endif
18510aca0605SAndrew Oates 
1852eaa728eeSbellard         switch (type) {
1853eaa728eeSbellard         case 1: /* available 286 TSS */
1854eaa728eeSbellard         case 9: /* available 386 TSS */
1855eaa728eeSbellard         case 5: /* task gate */
185620054ef0SBlue Swirl             if (dpl < cpl || dpl < rpl) {
1857100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
185820054ef0SBlue Swirl             }
1859100ec099SPavel Dovgalyuk             switch_tss_ra(env, new_cs, e1, e2, SWITCH_TSS_CALL, next_eip, GETPC());
1860eaa728eeSbellard             return;
1861eaa728eeSbellard         case 4: /* 286 call gate */
1862eaa728eeSbellard         case 12: /* 386 call gate */
1863eaa728eeSbellard             break;
1864eaa728eeSbellard         default:
1865100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
1866eaa728eeSbellard             break;
1867eaa728eeSbellard         }
1868eaa728eeSbellard         shift = type >> 3;
1869eaa728eeSbellard 
187020054ef0SBlue Swirl         if (dpl < cpl || dpl < rpl) {
1871100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
187220054ef0SBlue Swirl         }
1873eaa728eeSbellard         /* check valid bit */
187420054ef0SBlue Swirl         if (!(e2 & DESC_P_MASK)) {
1875100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0B_NOSEG,  new_cs & 0xfffc, GETPC());
187620054ef0SBlue Swirl         }
1877eaa728eeSbellard         selector = e1 >> 16;
1878eaa728eeSbellard         param_count = e2 & 0x1f;
18790aca0605SAndrew Oates         offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff);
18800aca0605SAndrew Oates #ifdef TARGET_X86_64
18810aca0605SAndrew Oates         if (env->efer & MSR_EFER_LMA) {
18820aca0605SAndrew Oates             /* load the upper 8 bytes of the 64-bit call gate */
18830aca0605SAndrew Oates             if (load_segment_ra(env, &e1, &e2, new_cs + 8, GETPC())) {
18840aca0605SAndrew Oates                 raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc,
18850aca0605SAndrew Oates                                        GETPC());
18860aca0605SAndrew Oates             }
18870aca0605SAndrew Oates             type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
18880aca0605SAndrew Oates             if (type != 0) {
18890aca0605SAndrew Oates                 raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc,
18900aca0605SAndrew Oates                                        GETPC());
18910aca0605SAndrew Oates             }
18920aca0605SAndrew Oates             offset |= ((target_ulong)e1) << 32;
18930aca0605SAndrew Oates         }
18940aca0605SAndrew Oates #endif
189520054ef0SBlue Swirl         if ((selector & 0xfffc) == 0) {
1896100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC());
189720054ef0SBlue Swirl         }
1898eaa728eeSbellard 
1899100ec099SPavel Dovgalyuk         if (load_segment_ra(env, &e1, &e2, selector, GETPC()) != 0) {
1900100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
190120054ef0SBlue Swirl         }
190220054ef0SBlue Swirl         if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK))) {
1903100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
190420054ef0SBlue Swirl         }
1905eaa728eeSbellard         dpl = (e2 >> DESC_DPL_SHIFT) & 3;
190620054ef0SBlue Swirl         if (dpl > cpl) {
1907100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
190820054ef0SBlue Swirl         }
19090aca0605SAndrew Oates #ifdef TARGET_X86_64
19100aca0605SAndrew Oates         if (env->efer & MSR_EFER_LMA) {
19110aca0605SAndrew Oates             if (!(e2 & DESC_L_MASK)) {
19120aca0605SAndrew Oates                 raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
19130aca0605SAndrew Oates             }
19140aca0605SAndrew Oates             if (e2 & DESC_B_MASK) {
19150aca0605SAndrew Oates                 raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
19160aca0605SAndrew Oates             }
19170aca0605SAndrew Oates             shift++;
19180aca0605SAndrew Oates         }
19190aca0605SAndrew Oates #endif
192020054ef0SBlue Swirl         if (!(e2 & DESC_P_MASK)) {
1921100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0B_NOSEG, selector & 0xfffc, GETPC());
192220054ef0SBlue Swirl         }
1923eaa728eeSbellard 
1924eaa728eeSbellard         if (!(e2 & DESC_C_MASK) && dpl < cpl) {
1925eaa728eeSbellard             /* to inner privilege */
19260aca0605SAndrew Oates #ifdef TARGET_X86_64
19270aca0605SAndrew Oates             if (shift == 2) {
19280aca0605SAndrew Oates                 sp = get_rsp_from_tss(env, dpl);
19290aca0605SAndrew Oates                 ss = dpl;  /* SS = NULL selector with RPL = new CPL */
19300aca0605SAndrew Oates                 new_stack = 1;
19310aca0605SAndrew Oates                 sp_mask = 0;
19320aca0605SAndrew Oates                 ssp = 0;  /* SS base is always zero in IA-32e mode */
19330aca0605SAndrew Oates                 LOG_PCALL("new ss:rsp=%04x:%016llx env->regs[R_ESP]="
19340aca0605SAndrew Oates                           TARGET_FMT_lx "\n", ss, sp, env->regs[R_ESP]);
19350aca0605SAndrew Oates             } else
19360aca0605SAndrew Oates #endif
19370aca0605SAndrew Oates             {
19380aca0605SAndrew Oates                 uint32_t sp32;
19390aca0605SAndrew Oates                 get_ss_esp_from_tss(env, &ss, &sp32, dpl, GETPC());
194090a2541bSliguang                 LOG_PCALL("new ss:esp=%04x:%08x param_count=%d env->regs[R_ESP]="
19410aca0605SAndrew Oates                           TARGET_FMT_lx "\n", ss, sp32, param_count,
194290a2541bSliguang                           env->regs[R_ESP]);
19430aca0605SAndrew Oates                 sp = sp32;
194420054ef0SBlue Swirl                 if ((ss & 0xfffc) == 0) {
1945100ec099SPavel Dovgalyuk                     raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC());
194620054ef0SBlue Swirl                 }
194720054ef0SBlue Swirl                 if ((ss & 3) != dpl) {
1948100ec099SPavel Dovgalyuk                     raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC());
194920054ef0SBlue Swirl                 }
1950100ec099SPavel Dovgalyuk                 if (load_segment_ra(env, &ss_e1, &ss_e2, ss, GETPC()) != 0) {
1951100ec099SPavel Dovgalyuk                     raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC());
195220054ef0SBlue Swirl                 }
1953eaa728eeSbellard                 ss_dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3;
195420054ef0SBlue Swirl                 if (ss_dpl != dpl) {
1955100ec099SPavel Dovgalyuk                     raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC());
195620054ef0SBlue Swirl                 }
1957eaa728eeSbellard                 if (!(ss_e2 & DESC_S_MASK) ||
1958eaa728eeSbellard                     (ss_e2 & DESC_CS_MASK) ||
195920054ef0SBlue Swirl                     !(ss_e2 & DESC_W_MASK)) {
1960100ec099SPavel Dovgalyuk                     raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC());
196120054ef0SBlue Swirl                 }
196220054ef0SBlue Swirl                 if (!(ss_e2 & DESC_P_MASK)) {
1963100ec099SPavel Dovgalyuk                     raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC());
196420054ef0SBlue Swirl                 }
1965eaa728eeSbellard 
19660aca0605SAndrew Oates                 sp_mask = get_sp_mask(ss_e2);
19670aca0605SAndrew Oates                 ssp = get_seg_base(ss_e1, ss_e2);
19680aca0605SAndrew Oates             }
19690aca0605SAndrew Oates 
197020054ef0SBlue Swirl             /* push_size = ((param_count * 2) + 8) << shift; */
1971eaa728eeSbellard 
1972eaa728eeSbellard             old_sp_mask = get_sp_mask(env->segs[R_SS].flags);
1973eaa728eeSbellard             old_ssp = env->segs[R_SS].base;
19740aca0605SAndrew Oates #ifdef TARGET_X86_64
19750aca0605SAndrew Oates             if (shift == 2) {
19760aca0605SAndrew Oates                 /* XXX: verify if new stack address is canonical */
19770aca0605SAndrew Oates                 PUSHQ_RA(sp, env->segs[R_SS].selector, GETPC());
19780aca0605SAndrew Oates                 PUSHQ_RA(sp, env->regs[R_ESP], GETPC());
19790aca0605SAndrew Oates                 /* parameters aren't supported for 64-bit call gates */
19800aca0605SAndrew Oates             } else
19810aca0605SAndrew Oates #endif
19820aca0605SAndrew Oates             if (shift == 1) {
1983100ec099SPavel Dovgalyuk                 PUSHL_RA(ssp, sp, sp_mask, env->segs[R_SS].selector, GETPC());
1984100ec099SPavel Dovgalyuk                 PUSHL_RA(ssp, sp, sp_mask, env->regs[R_ESP], GETPC());
1985eaa728eeSbellard                 for (i = param_count - 1; i >= 0; i--) {
1986100ec099SPavel Dovgalyuk                     val = cpu_ldl_kernel_ra(env, old_ssp +
198790a2541bSliguang                                             ((env->regs[R_ESP] + i * 4) &
1988100ec099SPavel Dovgalyuk                                              old_sp_mask), GETPC());
1989100ec099SPavel Dovgalyuk                     PUSHL_RA(ssp, sp, sp_mask, val, GETPC());
1990eaa728eeSbellard                 }
1991eaa728eeSbellard             } else {
1992100ec099SPavel Dovgalyuk                 PUSHW_RA(ssp, sp, sp_mask, env->segs[R_SS].selector, GETPC());
1993100ec099SPavel Dovgalyuk                 PUSHW_RA(ssp, sp, sp_mask, env->regs[R_ESP], GETPC());
1994eaa728eeSbellard                 for (i = param_count - 1; i >= 0; i--) {
1995100ec099SPavel Dovgalyuk                     val = cpu_lduw_kernel_ra(env, old_ssp +
199690a2541bSliguang                                              ((env->regs[R_ESP] + i * 2) &
1997100ec099SPavel Dovgalyuk                                               old_sp_mask), GETPC());
1998100ec099SPavel Dovgalyuk                     PUSHW_RA(ssp, sp, sp_mask, val, GETPC());
1999eaa728eeSbellard                 }
2000eaa728eeSbellard             }
2001eaa728eeSbellard             new_stack = 1;
2002eaa728eeSbellard         } else {
2003eaa728eeSbellard             /* to same privilege */
200408b3ded6Sliguang             sp = env->regs[R_ESP];
2005eaa728eeSbellard             sp_mask = get_sp_mask(env->segs[R_SS].flags);
2006eaa728eeSbellard             ssp = env->segs[R_SS].base;
200720054ef0SBlue Swirl             /* push_size = (4 << shift); */
2008eaa728eeSbellard             new_stack = 0;
2009eaa728eeSbellard         }
2010eaa728eeSbellard 
20110aca0605SAndrew Oates #ifdef TARGET_X86_64
20120aca0605SAndrew Oates         if (shift == 2) {
20130aca0605SAndrew Oates             PUSHQ_RA(sp, env->segs[R_CS].selector, GETPC());
20140aca0605SAndrew Oates             PUSHQ_RA(sp, next_eip, GETPC());
20150aca0605SAndrew Oates         } else
20160aca0605SAndrew Oates #endif
20170aca0605SAndrew Oates         if (shift == 1) {
2018100ec099SPavel Dovgalyuk             PUSHL_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC());
2019100ec099SPavel Dovgalyuk             PUSHL_RA(ssp, sp, sp_mask, next_eip, GETPC());
2020eaa728eeSbellard         } else {
2021100ec099SPavel Dovgalyuk             PUSHW_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC());
2022100ec099SPavel Dovgalyuk             PUSHW_RA(ssp, sp, sp_mask, next_eip, GETPC());
2023eaa728eeSbellard         }
2024eaa728eeSbellard 
2025eaa728eeSbellard         /* from this point, not restartable */
2026eaa728eeSbellard 
2027eaa728eeSbellard         if (new_stack) {
20280aca0605SAndrew Oates #ifdef TARGET_X86_64
20290aca0605SAndrew Oates             if (shift == 2) {
20300aca0605SAndrew Oates                 cpu_x86_load_seg_cache(env, R_SS, ss, 0, 0, 0);
20310aca0605SAndrew Oates             } else
20320aca0605SAndrew Oates #endif
20330aca0605SAndrew Oates             {
2034eaa728eeSbellard                 ss = (ss & ~3) | dpl;
2035eaa728eeSbellard                 cpu_x86_load_seg_cache(env, R_SS, ss,
2036eaa728eeSbellard                                        ssp,
2037eaa728eeSbellard                                        get_seg_limit(ss_e1, ss_e2),
2038eaa728eeSbellard                                        ss_e2);
2039eaa728eeSbellard             }
20400aca0605SAndrew Oates         }
2041eaa728eeSbellard 
2042eaa728eeSbellard         selector = (selector & ~3) | dpl;
2043eaa728eeSbellard         cpu_x86_load_seg_cache(env, R_CS, selector,
2044eaa728eeSbellard                        get_seg_base(e1, e2),
2045eaa728eeSbellard                        get_seg_limit(e1, e2),
2046eaa728eeSbellard                        e2);
2047eaa728eeSbellard         SET_ESP(sp, sp_mask);
2048a78d0eabSliguang         env->eip = offset;
2049eaa728eeSbellard     }
2050eaa728eeSbellard }
2051eaa728eeSbellard 
2052eaa728eeSbellard /* real and vm86 mode iret */
20532999a0b2SBlue Swirl void helper_iret_real(CPUX86State *env, int shift)
2054eaa728eeSbellard {
2055eaa728eeSbellard     uint32_t sp, new_cs, new_eip, new_eflags, sp_mask;
2056eaa728eeSbellard     target_ulong ssp;
2057eaa728eeSbellard     int eflags_mask;
2058eaa728eeSbellard 
2059eaa728eeSbellard     sp_mask = 0xffff; /* XXXX: use SS segment size? */
206008b3ded6Sliguang     sp = env->regs[R_ESP];
2061eaa728eeSbellard     ssp = env->segs[R_SS].base;
2062eaa728eeSbellard     if (shift == 1) {
2063eaa728eeSbellard         /* 32 bits */
2064100ec099SPavel Dovgalyuk         POPL_RA(ssp, sp, sp_mask, new_eip, GETPC());
2065100ec099SPavel Dovgalyuk         POPL_RA(ssp, sp, sp_mask, new_cs, GETPC());
2066eaa728eeSbellard         new_cs &= 0xffff;
2067100ec099SPavel Dovgalyuk         POPL_RA(ssp, sp, sp_mask, new_eflags, GETPC());
2068eaa728eeSbellard     } else {
2069eaa728eeSbellard         /* 16 bits */
2070100ec099SPavel Dovgalyuk         POPW_RA(ssp, sp, sp_mask, new_eip, GETPC());
2071100ec099SPavel Dovgalyuk         POPW_RA(ssp, sp, sp_mask, new_cs, GETPC());
2072100ec099SPavel Dovgalyuk         POPW_RA(ssp, sp, sp_mask, new_eflags, GETPC());
2073eaa728eeSbellard     }
207408b3ded6Sliguang     env->regs[R_ESP] = (env->regs[R_ESP] & ~sp_mask) | (sp & sp_mask);
2075bdadc0b5Smalc     env->segs[R_CS].selector = new_cs;
2076bdadc0b5Smalc     env->segs[R_CS].base = (new_cs << 4);
2077eaa728eeSbellard     env->eip = new_eip;
207820054ef0SBlue Swirl     if (env->eflags & VM_MASK) {
207920054ef0SBlue Swirl         eflags_mask = TF_MASK | AC_MASK | ID_MASK | IF_MASK | RF_MASK |
208020054ef0SBlue Swirl             NT_MASK;
208120054ef0SBlue Swirl     } else {
208220054ef0SBlue Swirl         eflags_mask = TF_MASK | AC_MASK | ID_MASK | IF_MASK | IOPL_MASK |
208320054ef0SBlue Swirl             RF_MASK | NT_MASK;
208420054ef0SBlue Swirl     }
208520054ef0SBlue Swirl     if (shift == 0) {
2086eaa728eeSbellard         eflags_mask &= 0xffff;
208720054ef0SBlue Swirl     }
2088997ff0d9SBlue Swirl     cpu_load_eflags(env, new_eflags, eflags_mask);
2089db620f46Sbellard     env->hflags2 &= ~HF2_NMI_MASK;
2090eaa728eeSbellard }
2091eaa728eeSbellard 
20922999a0b2SBlue Swirl static inline void validate_seg(CPUX86State *env, int seg_reg, int cpl)
2093eaa728eeSbellard {
2094eaa728eeSbellard     int dpl;
2095eaa728eeSbellard     uint32_t e2;
2096eaa728eeSbellard 
2097eaa728eeSbellard     /* XXX: on x86_64, we do not want to nullify FS and GS because
2098eaa728eeSbellard        they may still contain a valid base. I would be interested to
2099eaa728eeSbellard        know how a real x86_64 CPU behaves */
2100eaa728eeSbellard     if ((seg_reg == R_FS || seg_reg == R_GS) &&
210120054ef0SBlue Swirl         (env->segs[seg_reg].selector & 0xfffc) == 0) {
2102eaa728eeSbellard         return;
210320054ef0SBlue Swirl     }
2104eaa728eeSbellard 
2105eaa728eeSbellard     e2 = env->segs[seg_reg].flags;
2106eaa728eeSbellard     dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2107eaa728eeSbellard     if (!(e2 & DESC_CS_MASK) || !(e2 & DESC_C_MASK)) {
2108eaa728eeSbellard         /* data or non conforming code segment */
2109eaa728eeSbellard         if (dpl < cpl) {
2110eaa728eeSbellard             cpu_x86_load_seg_cache(env, seg_reg, 0, 0, 0, 0);
2111eaa728eeSbellard         }
2112eaa728eeSbellard     }
2113eaa728eeSbellard }
2114eaa728eeSbellard 
2115eaa728eeSbellard /* protected mode iret */
21162999a0b2SBlue Swirl static inline void helper_ret_protected(CPUX86State *env, int shift,
2117100ec099SPavel Dovgalyuk                                         int is_iret, int addend,
2118100ec099SPavel Dovgalyuk                                         uintptr_t retaddr)
2119eaa728eeSbellard {
2120eaa728eeSbellard     uint32_t new_cs, new_eflags, new_ss;
2121eaa728eeSbellard     uint32_t new_es, new_ds, new_fs, new_gs;
2122eaa728eeSbellard     uint32_t e1, e2, ss_e1, ss_e2;
2123eaa728eeSbellard     int cpl, dpl, rpl, eflags_mask, iopl;
2124eaa728eeSbellard     target_ulong ssp, sp, new_eip, new_esp, sp_mask;
2125eaa728eeSbellard 
2126eaa728eeSbellard #ifdef TARGET_X86_64
212720054ef0SBlue Swirl     if (shift == 2) {
2128eaa728eeSbellard         sp_mask = -1;
212920054ef0SBlue Swirl     } else
2130eaa728eeSbellard #endif
213120054ef0SBlue Swirl     {
2132eaa728eeSbellard         sp_mask = get_sp_mask(env->segs[R_SS].flags);
213320054ef0SBlue Swirl     }
213408b3ded6Sliguang     sp = env->regs[R_ESP];
2135eaa728eeSbellard     ssp = env->segs[R_SS].base;
2136eaa728eeSbellard     new_eflags = 0; /* avoid warning */
2137eaa728eeSbellard #ifdef TARGET_X86_64
2138eaa728eeSbellard     if (shift == 2) {
2139100ec099SPavel Dovgalyuk         POPQ_RA(sp, new_eip, retaddr);
2140100ec099SPavel Dovgalyuk         POPQ_RA(sp, new_cs, retaddr);
2141eaa728eeSbellard         new_cs &= 0xffff;
2142eaa728eeSbellard         if (is_iret) {
2143100ec099SPavel Dovgalyuk             POPQ_RA(sp, new_eflags, retaddr);
2144eaa728eeSbellard         }
2145eaa728eeSbellard     } else
2146eaa728eeSbellard #endif
214720054ef0SBlue Swirl     {
2148eaa728eeSbellard         if (shift == 1) {
2149eaa728eeSbellard             /* 32 bits */
2150100ec099SPavel Dovgalyuk             POPL_RA(ssp, sp, sp_mask, new_eip, retaddr);
2151100ec099SPavel Dovgalyuk             POPL_RA(ssp, sp, sp_mask, new_cs, retaddr);
2152eaa728eeSbellard             new_cs &= 0xffff;
2153eaa728eeSbellard             if (is_iret) {
2154100ec099SPavel Dovgalyuk                 POPL_RA(ssp, sp, sp_mask, new_eflags, retaddr);
215520054ef0SBlue Swirl                 if (new_eflags & VM_MASK) {
2156eaa728eeSbellard                     goto return_to_vm86;
2157eaa728eeSbellard                 }
215820054ef0SBlue Swirl             }
2159eaa728eeSbellard         } else {
2160eaa728eeSbellard             /* 16 bits */
2161100ec099SPavel Dovgalyuk             POPW_RA(ssp, sp, sp_mask, new_eip, retaddr);
2162100ec099SPavel Dovgalyuk             POPW_RA(ssp, sp, sp_mask, new_cs, retaddr);
216320054ef0SBlue Swirl             if (is_iret) {
2164100ec099SPavel Dovgalyuk                 POPW_RA(ssp, sp, sp_mask, new_eflags, retaddr);
2165eaa728eeSbellard             }
216620054ef0SBlue Swirl         }
216720054ef0SBlue Swirl     }
2168d12d51d5Saliguori     LOG_PCALL("lret new %04x:" TARGET_FMT_lx " s=%d addend=0x%x\n",
2169eaa728eeSbellard               new_cs, new_eip, shift, addend);
21706aa9e42fSRichard Henderson     LOG_PCALL_STATE(env_cpu(env));
217120054ef0SBlue Swirl     if ((new_cs & 0xfffc) == 0) {
2172100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr);
2173eaa728eeSbellard     }
2174100ec099SPavel Dovgalyuk     if (load_segment_ra(env, &e1, &e2, new_cs, retaddr) != 0) {
2175100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr);
217620054ef0SBlue Swirl     }
217720054ef0SBlue Swirl     if (!(e2 & DESC_S_MASK) ||
217820054ef0SBlue Swirl         !(e2 & DESC_CS_MASK)) {
2179100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr);
218020054ef0SBlue Swirl     }
218120054ef0SBlue Swirl     cpl = env->hflags & HF_CPL_MASK;
218220054ef0SBlue Swirl     rpl = new_cs & 3;
218320054ef0SBlue Swirl     if (rpl < cpl) {
2184100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr);
218520054ef0SBlue Swirl     }
218620054ef0SBlue Swirl     dpl = (e2 >> DESC_DPL_SHIFT) & 3;
218720054ef0SBlue Swirl     if (e2 & DESC_C_MASK) {
218820054ef0SBlue Swirl         if (dpl > rpl) {
2189100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr);
219020054ef0SBlue Swirl         }
219120054ef0SBlue Swirl     } else {
219220054ef0SBlue Swirl         if (dpl != rpl) {
2193100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr);
219420054ef0SBlue Swirl         }
219520054ef0SBlue Swirl     }
219620054ef0SBlue Swirl     if (!(e2 & DESC_P_MASK)) {
2197100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, retaddr);
219820054ef0SBlue Swirl     }
2199eaa728eeSbellard 
2200eaa728eeSbellard     sp += addend;
2201eaa728eeSbellard     if (rpl == cpl && (!(env->hflags & HF_CS64_MASK) ||
2202eaa728eeSbellard                        ((env->hflags & HF_CS64_MASK) && !is_iret))) {
22031235fc06Sths         /* return to same privilege level */
2204eaa728eeSbellard         cpu_x86_load_seg_cache(env, R_CS, new_cs,
2205eaa728eeSbellard                        get_seg_base(e1, e2),
2206eaa728eeSbellard                        get_seg_limit(e1, e2),
2207eaa728eeSbellard                        e2);
2208eaa728eeSbellard     } else {
2209eaa728eeSbellard         /* return to different privilege level */
2210eaa728eeSbellard #ifdef TARGET_X86_64
2211eaa728eeSbellard         if (shift == 2) {
2212100ec099SPavel Dovgalyuk             POPQ_RA(sp, new_esp, retaddr);
2213100ec099SPavel Dovgalyuk             POPQ_RA(sp, new_ss, retaddr);
2214eaa728eeSbellard             new_ss &= 0xffff;
2215eaa728eeSbellard         } else
2216eaa728eeSbellard #endif
221720054ef0SBlue Swirl         {
2218eaa728eeSbellard             if (shift == 1) {
2219eaa728eeSbellard                 /* 32 bits */
2220100ec099SPavel Dovgalyuk                 POPL_RA(ssp, sp, sp_mask, new_esp, retaddr);
2221100ec099SPavel Dovgalyuk                 POPL_RA(ssp, sp, sp_mask, new_ss, retaddr);
2222eaa728eeSbellard                 new_ss &= 0xffff;
2223eaa728eeSbellard             } else {
2224eaa728eeSbellard                 /* 16 bits */
2225100ec099SPavel Dovgalyuk                 POPW_RA(ssp, sp, sp_mask, new_esp, retaddr);
2226100ec099SPavel Dovgalyuk                 POPW_RA(ssp, sp, sp_mask, new_ss, retaddr);
2227eaa728eeSbellard             }
222820054ef0SBlue Swirl         }
2229d12d51d5Saliguori         LOG_PCALL("new ss:esp=%04x:" TARGET_FMT_lx "\n",
2230eaa728eeSbellard                   new_ss, new_esp);
2231eaa728eeSbellard         if ((new_ss & 0xfffc) == 0) {
2232eaa728eeSbellard #ifdef TARGET_X86_64
2233eaa728eeSbellard             /* NULL ss is allowed in long mode if cpl != 3 */
2234eaa728eeSbellard             /* XXX: test CS64? */
2235eaa728eeSbellard             if ((env->hflags & HF_LMA_MASK) && rpl != 3) {
2236eaa728eeSbellard                 cpu_x86_load_seg_cache(env, R_SS, new_ss,
2237eaa728eeSbellard                                        0, 0xffffffff,
2238eaa728eeSbellard                                        DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2239eaa728eeSbellard                                        DESC_S_MASK | (rpl << DESC_DPL_SHIFT) |
2240eaa728eeSbellard                                        DESC_W_MASK | DESC_A_MASK);
2241eaa728eeSbellard                 ss_e2 = DESC_B_MASK; /* XXX: should not be needed? */
2242eaa728eeSbellard             } else
2243eaa728eeSbellard #endif
2244eaa728eeSbellard             {
2245100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, 0, retaddr);
2246eaa728eeSbellard             }
2247eaa728eeSbellard         } else {
224820054ef0SBlue Swirl             if ((new_ss & 3) != rpl) {
2249100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, new_ss & 0xfffc, retaddr);
225020054ef0SBlue Swirl             }
2251100ec099SPavel Dovgalyuk             if (load_segment_ra(env, &ss_e1, &ss_e2, new_ss, retaddr) != 0) {
2252100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, new_ss & 0xfffc, retaddr);
225320054ef0SBlue Swirl             }
2254eaa728eeSbellard             if (!(ss_e2 & DESC_S_MASK) ||
2255eaa728eeSbellard                 (ss_e2 & DESC_CS_MASK) ||
225620054ef0SBlue Swirl                 !(ss_e2 & DESC_W_MASK)) {
2257100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, new_ss & 0xfffc, retaddr);
225820054ef0SBlue Swirl             }
2259eaa728eeSbellard             dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3;
226020054ef0SBlue Swirl             if (dpl != rpl) {
2261100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, new_ss & 0xfffc, retaddr);
226220054ef0SBlue Swirl             }
226320054ef0SBlue Swirl             if (!(ss_e2 & DESC_P_MASK)) {
2264100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0B_NOSEG, new_ss & 0xfffc, retaddr);
226520054ef0SBlue Swirl             }
2266eaa728eeSbellard             cpu_x86_load_seg_cache(env, R_SS, new_ss,
2267eaa728eeSbellard                                    get_seg_base(ss_e1, ss_e2),
2268eaa728eeSbellard                                    get_seg_limit(ss_e1, ss_e2),
2269eaa728eeSbellard                                    ss_e2);
2270eaa728eeSbellard         }
2271eaa728eeSbellard 
2272eaa728eeSbellard         cpu_x86_load_seg_cache(env, R_CS, new_cs,
2273eaa728eeSbellard                        get_seg_base(e1, e2),
2274eaa728eeSbellard                        get_seg_limit(e1, e2),
2275eaa728eeSbellard                        e2);
2276eaa728eeSbellard         sp = new_esp;
2277eaa728eeSbellard #ifdef TARGET_X86_64
227820054ef0SBlue Swirl         if (env->hflags & HF_CS64_MASK) {
2279eaa728eeSbellard             sp_mask = -1;
228020054ef0SBlue Swirl         } else
2281eaa728eeSbellard #endif
228220054ef0SBlue Swirl         {
2283eaa728eeSbellard             sp_mask = get_sp_mask(ss_e2);
228420054ef0SBlue Swirl         }
2285eaa728eeSbellard 
2286eaa728eeSbellard         /* validate data segments */
22872999a0b2SBlue Swirl         validate_seg(env, R_ES, rpl);
22882999a0b2SBlue Swirl         validate_seg(env, R_DS, rpl);
22892999a0b2SBlue Swirl         validate_seg(env, R_FS, rpl);
22902999a0b2SBlue Swirl         validate_seg(env, R_GS, rpl);
2291eaa728eeSbellard 
2292eaa728eeSbellard         sp += addend;
2293eaa728eeSbellard     }
2294eaa728eeSbellard     SET_ESP(sp, sp_mask);
2295eaa728eeSbellard     env->eip = new_eip;
2296eaa728eeSbellard     if (is_iret) {
2297eaa728eeSbellard         /* NOTE: 'cpl' is the _old_ CPL */
2298eaa728eeSbellard         eflags_mask = TF_MASK | AC_MASK | ID_MASK | RF_MASK | NT_MASK;
229920054ef0SBlue Swirl         if (cpl == 0) {
2300eaa728eeSbellard             eflags_mask |= IOPL_MASK;
230120054ef0SBlue Swirl         }
2302eaa728eeSbellard         iopl = (env->eflags >> IOPL_SHIFT) & 3;
230320054ef0SBlue Swirl         if (cpl <= iopl) {
2304eaa728eeSbellard             eflags_mask |= IF_MASK;
230520054ef0SBlue Swirl         }
230620054ef0SBlue Swirl         if (shift == 0) {
2307eaa728eeSbellard             eflags_mask &= 0xffff;
230820054ef0SBlue Swirl         }
2309997ff0d9SBlue Swirl         cpu_load_eflags(env, new_eflags, eflags_mask);
2310eaa728eeSbellard     }
2311eaa728eeSbellard     return;
2312eaa728eeSbellard 
2313eaa728eeSbellard  return_to_vm86:
2314100ec099SPavel Dovgalyuk     POPL_RA(ssp, sp, sp_mask, new_esp, retaddr);
2315100ec099SPavel Dovgalyuk     POPL_RA(ssp, sp, sp_mask, new_ss, retaddr);
2316100ec099SPavel Dovgalyuk     POPL_RA(ssp, sp, sp_mask, new_es, retaddr);
2317100ec099SPavel Dovgalyuk     POPL_RA(ssp, sp, sp_mask, new_ds, retaddr);
2318100ec099SPavel Dovgalyuk     POPL_RA(ssp, sp, sp_mask, new_fs, retaddr);
2319100ec099SPavel Dovgalyuk     POPL_RA(ssp, sp, sp_mask, new_gs, retaddr);
2320eaa728eeSbellard 
2321eaa728eeSbellard     /* modify processor state */
2322997ff0d9SBlue Swirl     cpu_load_eflags(env, new_eflags, TF_MASK | AC_MASK | ID_MASK |
2323997ff0d9SBlue Swirl                     IF_MASK | IOPL_MASK | VM_MASK | NT_MASK | VIF_MASK |
2324997ff0d9SBlue Swirl                     VIP_MASK);
23252999a0b2SBlue Swirl     load_seg_vm(env, R_CS, new_cs & 0xffff);
23262999a0b2SBlue Swirl     load_seg_vm(env, R_SS, new_ss & 0xffff);
23272999a0b2SBlue Swirl     load_seg_vm(env, R_ES, new_es & 0xffff);
23282999a0b2SBlue Swirl     load_seg_vm(env, R_DS, new_ds & 0xffff);
23292999a0b2SBlue Swirl     load_seg_vm(env, R_FS, new_fs & 0xffff);
23302999a0b2SBlue Swirl     load_seg_vm(env, R_GS, new_gs & 0xffff);
2331eaa728eeSbellard 
2332eaa728eeSbellard     env->eip = new_eip & 0xffff;
233308b3ded6Sliguang     env->regs[R_ESP] = new_esp;
2334eaa728eeSbellard }
2335eaa728eeSbellard 
23362999a0b2SBlue Swirl void helper_iret_protected(CPUX86State *env, int shift, int next_eip)
2337eaa728eeSbellard {
2338eaa728eeSbellard     int tss_selector, type;
2339eaa728eeSbellard     uint32_t e1, e2;
2340eaa728eeSbellard 
2341eaa728eeSbellard     /* specific case for TSS */
2342eaa728eeSbellard     if (env->eflags & NT_MASK) {
2343eaa728eeSbellard #ifdef TARGET_X86_64
234420054ef0SBlue Swirl         if (env->hflags & HF_LMA_MASK) {
2345100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC());
234620054ef0SBlue Swirl         }
2347eaa728eeSbellard #endif
2348100ec099SPavel Dovgalyuk         tss_selector = cpu_lduw_kernel_ra(env, env->tr.base + 0, GETPC());
234920054ef0SBlue Swirl         if (tss_selector & 4) {
2350100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, GETPC());
235120054ef0SBlue Swirl         }
2352100ec099SPavel Dovgalyuk         if (load_segment_ra(env, &e1, &e2, tss_selector, GETPC()) != 0) {
2353100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, GETPC());
235420054ef0SBlue Swirl         }
2355eaa728eeSbellard         type = (e2 >> DESC_TYPE_SHIFT) & 0x17;
2356eaa728eeSbellard         /* NOTE: we check both segment and busy TSS */
235720054ef0SBlue Swirl         if (type != 3) {
2358100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, GETPC());
235920054ef0SBlue Swirl         }
2360100ec099SPavel Dovgalyuk         switch_tss_ra(env, tss_selector, e1, e2, SWITCH_TSS_IRET, next_eip, GETPC());
2361eaa728eeSbellard     } else {
2362100ec099SPavel Dovgalyuk         helper_ret_protected(env, shift, 1, 0, GETPC());
2363eaa728eeSbellard     }
2364db620f46Sbellard     env->hflags2 &= ~HF2_NMI_MASK;
2365eaa728eeSbellard }
2366eaa728eeSbellard 
23672999a0b2SBlue Swirl void helper_lret_protected(CPUX86State *env, int shift, int addend)
2368eaa728eeSbellard {
2369100ec099SPavel Dovgalyuk     helper_ret_protected(env, shift, 0, addend, GETPC());
2370eaa728eeSbellard }
2371eaa728eeSbellard 
23722999a0b2SBlue Swirl void helper_sysenter(CPUX86State *env)
2373eaa728eeSbellard {
2374eaa728eeSbellard     if (env->sysenter_cs == 0) {
2375100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC());
2376eaa728eeSbellard     }
2377eaa728eeSbellard     env->eflags &= ~(VM_MASK | IF_MASK | RF_MASK);
23782436b61aSbalrog 
23792436b61aSbalrog #ifdef TARGET_X86_64
23802436b61aSbalrog     if (env->hflags & HF_LMA_MASK) {
23812436b61aSbalrog         cpu_x86_load_seg_cache(env, R_CS, env->sysenter_cs & 0xfffc,
23822436b61aSbalrog                                0, 0xffffffff,
23832436b61aSbalrog                                DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
23842436b61aSbalrog                                DESC_S_MASK |
238520054ef0SBlue Swirl                                DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK |
238620054ef0SBlue Swirl                                DESC_L_MASK);
23872436b61aSbalrog     } else
23882436b61aSbalrog #endif
23892436b61aSbalrog     {
2390eaa728eeSbellard         cpu_x86_load_seg_cache(env, R_CS, env->sysenter_cs & 0xfffc,
2391eaa728eeSbellard                                0, 0xffffffff,
2392eaa728eeSbellard                                DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2393eaa728eeSbellard                                DESC_S_MASK |
2394eaa728eeSbellard                                DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
23952436b61aSbalrog     }
2396eaa728eeSbellard     cpu_x86_load_seg_cache(env, R_SS, (env->sysenter_cs + 8) & 0xfffc,
2397eaa728eeSbellard                            0, 0xffffffff,
2398eaa728eeSbellard                            DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2399eaa728eeSbellard                            DESC_S_MASK |
2400eaa728eeSbellard                            DESC_W_MASK | DESC_A_MASK);
240108b3ded6Sliguang     env->regs[R_ESP] = env->sysenter_esp;
2402a78d0eabSliguang     env->eip = env->sysenter_eip;
2403eaa728eeSbellard }
2404eaa728eeSbellard 
24052999a0b2SBlue Swirl void helper_sysexit(CPUX86State *env, int dflag)
2406eaa728eeSbellard {
2407eaa728eeSbellard     int cpl;
2408eaa728eeSbellard 
2409eaa728eeSbellard     cpl = env->hflags & HF_CPL_MASK;
2410eaa728eeSbellard     if (env->sysenter_cs == 0 || cpl != 0) {
2411100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC());
2412eaa728eeSbellard     }
24132436b61aSbalrog #ifdef TARGET_X86_64
24142436b61aSbalrog     if (dflag == 2) {
241520054ef0SBlue Swirl         cpu_x86_load_seg_cache(env, R_CS, ((env->sysenter_cs + 32) & 0xfffc) |
241620054ef0SBlue Swirl                                3, 0, 0xffffffff,
24172436b61aSbalrog                                DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
24182436b61aSbalrog                                DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
241920054ef0SBlue Swirl                                DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK |
242020054ef0SBlue Swirl                                DESC_L_MASK);
242120054ef0SBlue Swirl         cpu_x86_load_seg_cache(env, R_SS, ((env->sysenter_cs + 40) & 0xfffc) |
242220054ef0SBlue Swirl                                3, 0, 0xffffffff,
24232436b61aSbalrog                                DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
24242436b61aSbalrog                                DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
24252436b61aSbalrog                                DESC_W_MASK | DESC_A_MASK);
24262436b61aSbalrog     } else
24272436b61aSbalrog #endif
24282436b61aSbalrog     {
242920054ef0SBlue Swirl         cpu_x86_load_seg_cache(env, R_CS, ((env->sysenter_cs + 16) & 0xfffc) |
243020054ef0SBlue Swirl                                3, 0, 0xffffffff,
2431eaa728eeSbellard                                DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2432eaa728eeSbellard                                DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
2433eaa728eeSbellard                                DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
243420054ef0SBlue Swirl         cpu_x86_load_seg_cache(env, R_SS, ((env->sysenter_cs + 24) & 0xfffc) |
243520054ef0SBlue Swirl                                3, 0, 0xffffffff,
2436eaa728eeSbellard                                DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2437eaa728eeSbellard                                DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
2438eaa728eeSbellard                                DESC_W_MASK | DESC_A_MASK);
24392436b61aSbalrog     }
244008b3ded6Sliguang     env->regs[R_ESP] = env->regs[R_ECX];
2441a78d0eabSliguang     env->eip = env->regs[R_EDX];
2442eaa728eeSbellard }
2443eaa728eeSbellard 
24442999a0b2SBlue Swirl target_ulong helper_lsl(CPUX86State *env, target_ulong selector1)
2445eaa728eeSbellard {
2446eaa728eeSbellard     unsigned int limit;
2447eaa728eeSbellard     uint32_t e1, e2, eflags, selector;
2448eaa728eeSbellard     int rpl, dpl, cpl, type;
2449eaa728eeSbellard 
2450eaa728eeSbellard     selector = selector1 & 0xffff;
2451f0967a1aSBlue Swirl     eflags = cpu_cc_compute_all(env, CC_OP);
245220054ef0SBlue Swirl     if ((selector & 0xfffc) == 0) {
2453dc1ded53Saliguori         goto fail;
245420054ef0SBlue Swirl     }
2455100ec099SPavel Dovgalyuk     if (load_segment_ra(env, &e1, &e2, selector, GETPC()) != 0) {
2456eaa728eeSbellard         goto fail;
245720054ef0SBlue Swirl     }
2458eaa728eeSbellard     rpl = selector & 3;
2459eaa728eeSbellard     dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2460eaa728eeSbellard     cpl = env->hflags & HF_CPL_MASK;
2461eaa728eeSbellard     if (e2 & DESC_S_MASK) {
2462eaa728eeSbellard         if ((e2 & DESC_CS_MASK) && (e2 & DESC_C_MASK)) {
2463eaa728eeSbellard             /* conforming */
2464eaa728eeSbellard         } else {
246520054ef0SBlue Swirl             if (dpl < cpl || dpl < rpl) {
2466eaa728eeSbellard                 goto fail;
2467eaa728eeSbellard             }
246820054ef0SBlue Swirl         }
2469eaa728eeSbellard     } else {
2470eaa728eeSbellard         type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
2471eaa728eeSbellard         switch (type) {
2472eaa728eeSbellard         case 1:
2473eaa728eeSbellard         case 2:
2474eaa728eeSbellard         case 3:
2475eaa728eeSbellard         case 9:
2476eaa728eeSbellard         case 11:
2477eaa728eeSbellard             break;
2478eaa728eeSbellard         default:
2479eaa728eeSbellard             goto fail;
2480eaa728eeSbellard         }
2481eaa728eeSbellard         if (dpl < cpl || dpl < rpl) {
2482eaa728eeSbellard         fail:
2483eaa728eeSbellard             CC_SRC = eflags & ~CC_Z;
2484eaa728eeSbellard             return 0;
2485eaa728eeSbellard         }
2486eaa728eeSbellard     }
2487eaa728eeSbellard     limit = get_seg_limit(e1, e2);
2488eaa728eeSbellard     CC_SRC = eflags | CC_Z;
2489eaa728eeSbellard     return limit;
2490eaa728eeSbellard }
2491eaa728eeSbellard 
24922999a0b2SBlue Swirl target_ulong helper_lar(CPUX86State *env, target_ulong selector1)
2493eaa728eeSbellard {
2494eaa728eeSbellard     uint32_t e1, e2, eflags, selector;
2495eaa728eeSbellard     int rpl, dpl, cpl, type;
2496eaa728eeSbellard 
2497eaa728eeSbellard     selector = selector1 & 0xffff;
2498f0967a1aSBlue Swirl     eflags = cpu_cc_compute_all(env, CC_OP);
249920054ef0SBlue Swirl     if ((selector & 0xfffc) == 0) {
2500eaa728eeSbellard         goto fail;
250120054ef0SBlue Swirl     }
2502100ec099SPavel Dovgalyuk     if (load_segment_ra(env, &e1, &e2, selector, GETPC()) != 0) {
2503eaa728eeSbellard         goto fail;
250420054ef0SBlue Swirl     }
2505eaa728eeSbellard     rpl = selector & 3;
2506eaa728eeSbellard     dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2507eaa728eeSbellard     cpl = env->hflags & HF_CPL_MASK;
2508eaa728eeSbellard     if (e2 & DESC_S_MASK) {
2509eaa728eeSbellard         if ((e2 & DESC_CS_MASK) && (e2 & DESC_C_MASK)) {
2510eaa728eeSbellard             /* conforming */
2511eaa728eeSbellard         } else {
251220054ef0SBlue Swirl             if (dpl < cpl || dpl < rpl) {
2513eaa728eeSbellard                 goto fail;
2514eaa728eeSbellard             }
251520054ef0SBlue Swirl         }
2516eaa728eeSbellard     } else {
2517eaa728eeSbellard         type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
2518eaa728eeSbellard         switch (type) {
2519eaa728eeSbellard         case 1:
2520eaa728eeSbellard         case 2:
2521eaa728eeSbellard         case 3:
2522eaa728eeSbellard         case 4:
2523eaa728eeSbellard         case 5:
2524eaa728eeSbellard         case 9:
2525eaa728eeSbellard         case 11:
2526eaa728eeSbellard         case 12:
2527eaa728eeSbellard             break;
2528eaa728eeSbellard         default:
2529eaa728eeSbellard             goto fail;
2530eaa728eeSbellard         }
2531eaa728eeSbellard         if (dpl < cpl || dpl < rpl) {
2532eaa728eeSbellard         fail:
2533eaa728eeSbellard             CC_SRC = eflags & ~CC_Z;
2534eaa728eeSbellard             return 0;
2535eaa728eeSbellard         }
2536eaa728eeSbellard     }
2537eaa728eeSbellard     CC_SRC = eflags | CC_Z;
2538eaa728eeSbellard     return e2 & 0x00f0ff00;
2539eaa728eeSbellard }
2540eaa728eeSbellard 
25412999a0b2SBlue Swirl void helper_verr(CPUX86State *env, target_ulong selector1)
2542eaa728eeSbellard {
2543eaa728eeSbellard     uint32_t e1, e2, eflags, selector;
2544eaa728eeSbellard     int rpl, dpl, cpl;
2545eaa728eeSbellard 
2546eaa728eeSbellard     selector = selector1 & 0xffff;
2547f0967a1aSBlue Swirl     eflags = cpu_cc_compute_all(env, CC_OP);
254820054ef0SBlue Swirl     if ((selector & 0xfffc) == 0) {
2549eaa728eeSbellard         goto fail;
255020054ef0SBlue Swirl     }
2551100ec099SPavel Dovgalyuk     if (load_segment_ra(env, &e1, &e2, selector, GETPC()) != 0) {
2552eaa728eeSbellard         goto fail;
255320054ef0SBlue Swirl     }
255420054ef0SBlue Swirl     if (!(e2 & DESC_S_MASK)) {
2555eaa728eeSbellard         goto fail;
255620054ef0SBlue Swirl     }
2557eaa728eeSbellard     rpl = selector & 3;
2558eaa728eeSbellard     dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2559eaa728eeSbellard     cpl = env->hflags & HF_CPL_MASK;
2560eaa728eeSbellard     if (e2 & DESC_CS_MASK) {
256120054ef0SBlue Swirl         if (!(e2 & DESC_R_MASK)) {
2562eaa728eeSbellard             goto fail;
256320054ef0SBlue Swirl         }
2564eaa728eeSbellard         if (!(e2 & DESC_C_MASK)) {
256520054ef0SBlue Swirl             if (dpl < cpl || dpl < rpl) {
2566eaa728eeSbellard                 goto fail;
2567eaa728eeSbellard             }
256820054ef0SBlue Swirl         }
2569eaa728eeSbellard     } else {
2570eaa728eeSbellard         if (dpl < cpl || dpl < rpl) {
2571eaa728eeSbellard         fail:
2572eaa728eeSbellard             CC_SRC = eflags & ~CC_Z;
2573eaa728eeSbellard             return;
2574eaa728eeSbellard         }
2575eaa728eeSbellard     }
2576eaa728eeSbellard     CC_SRC = eflags | CC_Z;
2577eaa728eeSbellard }
2578eaa728eeSbellard 
25792999a0b2SBlue Swirl void helper_verw(CPUX86State *env, target_ulong selector1)
2580eaa728eeSbellard {
2581eaa728eeSbellard     uint32_t e1, e2, eflags, selector;
2582eaa728eeSbellard     int rpl, dpl, cpl;
2583eaa728eeSbellard 
2584eaa728eeSbellard     selector = selector1 & 0xffff;
2585f0967a1aSBlue Swirl     eflags = cpu_cc_compute_all(env, CC_OP);
258620054ef0SBlue Swirl     if ((selector & 0xfffc) == 0) {
2587eaa728eeSbellard         goto fail;
258820054ef0SBlue Swirl     }
2589100ec099SPavel Dovgalyuk     if (load_segment_ra(env, &e1, &e2, selector, GETPC()) != 0) {
2590eaa728eeSbellard         goto fail;
259120054ef0SBlue Swirl     }
259220054ef0SBlue Swirl     if (!(e2 & DESC_S_MASK)) {
2593eaa728eeSbellard         goto fail;
259420054ef0SBlue Swirl     }
2595eaa728eeSbellard     rpl = selector & 3;
2596eaa728eeSbellard     dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2597eaa728eeSbellard     cpl = env->hflags & HF_CPL_MASK;
2598eaa728eeSbellard     if (e2 & DESC_CS_MASK) {
2599eaa728eeSbellard         goto fail;
2600eaa728eeSbellard     } else {
260120054ef0SBlue Swirl         if (dpl < cpl || dpl < rpl) {
2602eaa728eeSbellard             goto fail;
260320054ef0SBlue Swirl         }
2604eaa728eeSbellard         if (!(e2 & DESC_W_MASK)) {
2605eaa728eeSbellard         fail:
2606eaa728eeSbellard             CC_SRC = eflags & ~CC_Z;
2607eaa728eeSbellard             return;
2608eaa728eeSbellard         }
2609eaa728eeSbellard     }
2610eaa728eeSbellard     CC_SRC = eflags | CC_Z;
2611eaa728eeSbellard }
2612eaa728eeSbellard 
26133e457172SBlue Swirl #if defined(CONFIG_USER_ONLY)
26142999a0b2SBlue Swirl void cpu_x86_load_seg(CPUX86State *env, int seg_reg, int selector)
26153e457172SBlue Swirl {
26163e457172SBlue Swirl     if (!(env->cr[0] & CR0_PE_MASK) || (env->eflags & VM_MASK)) {
2617b98dbc90SPaolo Bonzini         int dpl = (env->eflags & VM_MASK) ? 3 : 0;
26183e457172SBlue Swirl         selector &= 0xffff;
26193e457172SBlue Swirl         cpu_x86_load_seg_cache(env, seg_reg, selector,
2620b98dbc90SPaolo Bonzini                                (selector << 4), 0xffff,
2621b98dbc90SPaolo Bonzini                                DESC_P_MASK | DESC_S_MASK | DESC_W_MASK |
2622b98dbc90SPaolo Bonzini                                DESC_A_MASK | (dpl << DESC_DPL_SHIFT));
26233e457172SBlue Swirl     } else {
26242999a0b2SBlue Swirl         helper_load_seg(env, seg_reg, selector);
26253e457172SBlue Swirl     }
26263e457172SBlue Swirl }
26273e457172SBlue Swirl #endif
262881cf8d8aSPaolo Bonzini 
262981cf8d8aSPaolo Bonzini /* check if Port I/O is allowed in TSS */
2630100ec099SPavel Dovgalyuk static inline void check_io(CPUX86State *env, int addr, int size,
2631100ec099SPavel Dovgalyuk                             uintptr_t retaddr)
263281cf8d8aSPaolo Bonzini {
263381cf8d8aSPaolo Bonzini     int io_offset, val, mask;
263481cf8d8aSPaolo Bonzini 
263581cf8d8aSPaolo Bonzini     /* TSS must be a valid 32 bit one */
263681cf8d8aSPaolo Bonzini     if (!(env->tr.flags & DESC_P_MASK) ||
263781cf8d8aSPaolo Bonzini         ((env->tr.flags >> DESC_TYPE_SHIFT) & 0xf) != 9 ||
263881cf8d8aSPaolo Bonzini         env->tr.limit < 103) {
263981cf8d8aSPaolo Bonzini         goto fail;
264081cf8d8aSPaolo Bonzini     }
2641100ec099SPavel Dovgalyuk     io_offset = cpu_lduw_kernel_ra(env, env->tr.base + 0x66, retaddr);
264281cf8d8aSPaolo Bonzini     io_offset += (addr >> 3);
264381cf8d8aSPaolo Bonzini     /* Note: the check needs two bytes */
264481cf8d8aSPaolo Bonzini     if ((io_offset + 1) > env->tr.limit) {
264581cf8d8aSPaolo Bonzini         goto fail;
264681cf8d8aSPaolo Bonzini     }
2647100ec099SPavel Dovgalyuk     val = cpu_lduw_kernel_ra(env, env->tr.base + io_offset, retaddr);
264881cf8d8aSPaolo Bonzini     val >>= (addr & 7);
264981cf8d8aSPaolo Bonzini     mask = (1 << size) - 1;
265081cf8d8aSPaolo Bonzini     /* all bits must be zero to allow the I/O */
265181cf8d8aSPaolo Bonzini     if ((val & mask) != 0) {
265281cf8d8aSPaolo Bonzini     fail:
2653100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0D_GPF, 0, retaddr);
265481cf8d8aSPaolo Bonzini     }
265581cf8d8aSPaolo Bonzini }
265681cf8d8aSPaolo Bonzini 
265781cf8d8aSPaolo Bonzini void helper_check_iob(CPUX86State *env, uint32_t t0)
265881cf8d8aSPaolo Bonzini {
2659100ec099SPavel Dovgalyuk     check_io(env, t0, 1, GETPC());
266081cf8d8aSPaolo Bonzini }
266181cf8d8aSPaolo Bonzini 
266281cf8d8aSPaolo Bonzini void helper_check_iow(CPUX86State *env, uint32_t t0)
266381cf8d8aSPaolo Bonzini {
2664100ec099SPavel Dovgalyuk     check_io(env, t0, 2, GETPC());
266581cf8d8aSPaolo Bonzini }
266681cf8d8aSPaolo Bonzini 
266781cf8d8aSPaolo Bonzini void helper_check_iol(CPUX86State *env, uint32_t t0)
266881cf8d8aSPaolo Bonzini {
2669100ec099SPavel Dovgalyuk     check_io(env, t0, 4, GETPC());
267081cf8d8aSPaolo Bonzini }
2671