xref: /qemu/target/i386/tcg/seg_helper.c (revision ed69e8314d403d1bfa8c0210f850ffe69bb89dbe)
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
10d9ff33adSChetan Pant  * version 2.1 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"
28*ed69e831SClaudio Fontana #include "helper-tcg.h"
293e457172SBlue Swirl 
30eaa728eeSbellard //#define DEBUG_PCALL
31eaa728eeSbellard 
32d12d51d5Saliguori #ifdef DEBUG_PCALL
3393fcfe39Saliguori # define LOG_PCALL(...) qemu_log_mask(CPU_LOG_PCALL, ## __VA_ARGS__)
348995b7a0SAndreas Färber # define LOG_PCALL_STATE(cpu)                                  \
358995b7a0SAndreas Färber     log_cpu_state_mask(CPU_LOG_PCALL, (cpu), CPU_DUMP_CCOP)
36d12d51d5Saliguori #else
37d12d51d5Saliguori # define LOG_PCALL(...) do { } while (0)
388995b7a0SAndreas Färber # define LOG_PCALL_STATE(cpu) do { } while (0)
39d12d51d5Saliguori #endif
40d12d51d5Saliguori 
4121ffbdc9SRichard Henderson /*
4221ffbdc9SRichard Henderson  * TODO: Convert callers to compute cpu_mmu_index_kernel once
4321ffbdc9SRichard Henderson  * and use *_mmuidx_ra directly.
4421ffbdc9SRichard Henderson  */
4521ffbdc9SRichard Henderson #define cpu_ldub_kernel_ra(e, p, r) \
4621ffbdc9SRichard Henderson     cpu_ldub_mmuidx_ra(e, p, cpu_mmu_index_kernel(e), r)
4721ffbdc9SRichard Henderson #define cpu_lduw_kernel_ra(e, p, r) \
4821ffbdc9SRichard Henderson     cpu_lduw_mmuidx_ra(e, p, cpu_mmu_index_kernel(e), r)
4921ffbdc9SRichard Henderson #define cpu_ldl_kernel_ra(e, p, r) \
5021ffbdc9SRichard Henderson     cpu_ldl_mmuidx_ra(e, p, cpu_mmu_index_kernel(e), r)
5121ffbdc9SRichard Henderson #define cpu_ldq_kernel_ra(e, p, r) \
5221ffbdc9SRichard Henderson     cpu_ldq_mmuidx_ra(e, p, cpu_mmu_index_kernel(e), r)
539220fe54SPeter Maydell 
5421ffbdc9SRichard Henderson #define cpu_stb_kernel_ra(e, p, v, r) \
5521ffbdc9SRichard Henderson     cpu_stb_mmuidx_ra(e, p, v, cpu_mmu_index_kernel(e), r)
5621ffbdc9SRichard Henderson #define cpu_stw_kernel_ra(e, p, v, r) \
5721ffbdc9SRichard Henderson     cpu_stw_mmuidx_ra(e, p, v, cpu_mmu_index_kernel(e), r)
5821ffbdc9SRichard Henderson #define cpu_stl_kernel_ra(e, p, v, r) \
5921ffbdc9SRichard Henderson     cpu_stl_mmuidx_ra(e, p, v, cpu_mmu_index_kernel(e), r)
6021ffbdc9SRichard Henderson #define cpu_stq_kernel_ra(e, p, v, r) \
6121ffbdc9SRichard Henderson     cpu_stq_mmuidx_ra(e, p, v, cpu_mmu_index_kernel(e), r)
629220fe54SPeter Maydell 
6321ffbdc9SRichard Henderson #define cpu_ldub_kernel(e, p)    cpu_ldub_kernel_ra(e, p, 0)
6421ffbdc9SRichard Henderson #define cpu_lduw_kernel(e, p)    cpu_lduw_kernel_ra(e, p, 0)
6521ffbdc9SRichard Henderson #define cpu_ldl_kernel(e, p)     cpu_ldl_kernel_ra(e, p, 0)
6621ffbdc9SRichard Henderson #define cpu_ldq_kernel(e, p)     cpu_ldq_kernel_ra(e, p, 0)
679220fe54SPeter Maydell 
6821ffbdc9SRichard Henderson #define cpu_stb_kernel(e, p, v)  cpu_stb_kernel_ra(e, p, v, 0)
6921ffbdc9SRichard Henderson #define cpu_stw_kernel(e, p, v)  cpu_stw_kernel_ra(e, p, v, 0)
7021ffbdc9SRichard Henderson #define cpu_stl_kernel(e, p, v)  cpu_stl_kernel_ra(e, p, v, 0)
7121ffbdc9SRichard Henderson #define cpu_stq_kernel(e, p, v)  cpu_stq_kernel_ra(e, p, v, 0)
728a201bd4SPaolo Bonzini 
73eaa728eeSbellard /* return non zero if error */
74100ec099SPavel Dovgalyuk static inline int load_segment_ra(CPUX86State *env, uint32_t *e1_ptr,
75100ec099SPavel Dovgalyuk                                uint32_t *e2_ptr, int selector,
76100ec099SPavel Dovgalyuk                                uintptr_t retaddr)
77eaa728eeSbellard {
78eaa728eeSbellard     SegmentCache *dt;
79eaa728eeSbellard     int index;
80eaa728eeSbellard     target_ulong ptr;
81eaa728eeSbellard 
8220054ef0SBlue Swirl     if (selector & 0x4) {
83eaa728eeSbellard         dt = &env->ldt;
8420054ef0SBlue Swirl     } else {
85eaa728eeSbellard         dt = &env->gdt;
8620054ef0SBlue Swirl     }
87eaa728eeSbellard     index = selector & ~7;
8820054ef0SBlue Swirl     if ((index + 7) > dt->limit) {
89eaa728eeSbellard         return -1;
9020054ef0SBlue Swirl     }
91eaa728eeSbellard     ptr = dt->base + index;
92100ec099SPavel Dovgalyuk     *e1_ptr = cpu_ldl_kernel_ra(env, ptr, retaddr);
93100ec099SPavel Dovgalyuk     *e2_ptr = cpu_ldl_kernel_ra(env, ptr + 4, retaddr);
94eaa728eeSbellard     return 0;
95eaa728eeSbellard }
96eaa728eeSbellard 
97100ec099SPavel Dovgalyuk static inline int load_segment(CPUX86State *env, uint32_t *e1_ptr,
98100ec099SPavel Dovgalyuk                                uint32_t *e2_ptr, int selector)
99100ec099SPavel Dovgalyuk {
100100ec099SPavel Dovgalyuk     return load_segment_ra(env, e1_ptr, e2_ptr, selector, 0);
101100ec099SPavel Dovgalyuk }
102100ec099SPavel Dovgalyuk 
103eaa728eeSbellard static inline unsigned int get_seg_limit(uint32_t e1, uint32_t e2)
104eaa728eeSbellard {
105eaa728eeSbellard     unsigned int limit;
10620054ef0SBlue Swirl 
107eaa728eeSbellard     limit = (e1 & 0xffff) | (e2 & 0x000f0000);
10820054ef0SBlue Swirl     if (e2 & DESC_G_MASK) {
109eaa728eeSbellard         limit = (limit << 12) | 0xfff;
11020054ef0SBlue Swirl     }
111eaa728eeSbellard     return limit;
112eaa728eeSbellard }
113eaa728eeSbellard 
114eaa728eeSbellard static inline uint32_t get_seg_base(uint32_t e1, uint32_t e2)
115eaa728eeSbellard {
11620054ef0SBlue Swirl     return (e1 >> 16) | ((e2 & 0xff) << 16) | (e2 & 0xff000000);
117eaa728eeSbellard }
118eaa728eeSbellard 
11920054ef0SBlue Swirl static inline void load_seg_cache_raw_dt(SegmentCache *sc, uint32_t e1,
12020054ef0SBlue Swirl                                          uint32_t e2)
121eaa728eeSbellard {
122eaa728eeSbellard     sc->base = get_seg_base(e1, e2);
123eaa728eeSbellard     sc->limit = get_seg_limit(e1, e2);
124eaa728eeSbellard     sc->flags = e2;
125eaa728eeSbellard }
126eaa728eeSbellard 
127eaa728eeSbellard /* init the segment cache in vm86 mode. */
1282999a0b2SBlue Swirl static inline void load_seg_vm(CPUX86State *env, int seg, int selector)
129eaa728eeSbellard {
130eaa728eeSbellard     selector &= 0xffff;
131b98dbc90SPaolo Bonzini 
132b98dbc90SPaolo Bonzini     cpu_x86_load_seg_cache(env, seg, selector, (selector << 4), 0xffff,
133b98dbc90SPaolo Bonzini                            DESC_P_MASK | DESC_S_MASK | DESC_W_MASK |
134b98dbc90SPaolo Bonzini                            DESC_A_MASK | (3 << DESC_DPL_SHIFT));
135eaa728eeSbellard }
136eaa728eeSbellard 
1372999a0b2SBlue Swirl static inline void get_ss_esp_from_tss(CPUX86State *env, uint32_t *ss_ptr,
138100ec099SPavel Dovgalyuk                                        uint32_t *esp_ptr, int dpl,
139100ec099SPavel Dovgalyuk                                        uintptr_t retaddr)
140eaa728eeSbellard {
1416aa9e42fSRichard Henderson     X86CPU *cpu = env_archcpu(env);
142eaa728eeSbellard     int type, index, shift;
143eaa728eeSbellard 
144eaa728eeSbellard #if 0
145eaa728eeSbellard     {
146eaa728eeSbellard         int i;
147eaa728eeSbellard         printf("TR: base=%p limit=%x\n", env->tr.base, env->tr.limit);
148eaa728eeSbellard         for (i = 0; i < env->tr.limit; i++) {
149eaa728eeSbellard             printf("%02x ", env->tr.base[i]);
15020054ef0SBlue Swirl             if ((i & 7) == 7) {
15120054ef0SBlue Swirl                 printf("\n");
15220054ef0SBlue Swirl             }
153eaa728eeSbellard         }
154eaa728eeSbellard         printf("\n");
155eaa728eeSbellard     }
156eaa728eeSbellard #endif
157eaa728eeSbellard 
15820054ef0SBlue Swirl     if (!(env->tr.flags & DESC_P_MASK)) {
159a47dddd7SAndreas Färber         cpu_abort(CPU(cpu), "invalid tss");
16020054ef0SBlue Swirl     }
161eaa728eeSbellard     type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf;
16220054ef0SBlue Swirl     if ((type & 7) != 1) {
163a47dddd7SAndreas Färber         cpu_abort(CPU(cpu), "invalid tss type");
16420054ef0SBlue Swirl     }
165eaa728eeSbellard     shift = type >> 3;
166eaa728eeSbellard     index = (dpl * 4 + 2) << shift;
16720054ef0SBlue Swirl     if (index + (4 << shift) - 1 > env->tr.limit) {
168100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0A_TSS, env->tr.selector & 0xfffc, retaddr);
16920054ef0SBlue Swirl     }
170eaa728eeSbellard     if (shift == 0) {
171100ec099SPavel Dovgalyuk         *esp_ptr = cpu_lduw_kernel_ra(env, env->tr.base + index, retaddr);
172100ec099SPavel Dovgalyuk         *ss_ptr = cpu_lduw_kernel_ra(env, env->tr.base + index + 2, retaddr);
173eaa728eeSbellard     } else {
174100ec099SPavel Dovgalyuk         *esp_ptr = cpu_ldl_kernel_ra(env, env->tr.base + index, retaddr);
175100ec099SPavel Dovgalyuk         *ss_ptr = cpu_lduw_kernel_ra(env, env->tr.base + index + 4, retaddr);
176eaa728eeSbellard     }
177eaa728eeSbellard }
178eaa728eeSbellard 
179100ec099SPavel Dovgalyuk static void tss_load_seg(CPUX86State *env, int seg_reg, int selector, int cpl,
180100ec099SPavel Dovgalyuk                          uintptr_t retaddr)
181eaa728eeSbellard {
182eaa728eeSbellard     uint32_t e1, e2;
183d3b54918SPaolo Bonzini     int rpl, dpl;
184eaa728eeSbellard 
185eaa728eeSbellard     if ((selector & 0xfffc) != 0) {
186100ec099SPavel Dovgalyuk         if (load_segment_ra(env, &e1, &e2, selector, retaddr) != 0) {
187100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr);
18820054ef0SBlue Swirl         }
18920054ef0SBlue Swirl         if (!(e2 & DESC_S_MASK)) {
190100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr);
19120054ef0SBlue Swirl         }
192eaa728eeSbellard         rpl = selector & 3;
193eaa728eeSbellard         dpl = (e2 >> DESC_DPL_SHIFT) & 3;
194eaa728eeSbellard         if (seg_reg == R_CS) {
19520054ef0SBlue Swirl             if (!(e2 & DESC_CS_MASK)) {
196100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr);
19720054ef0SBlue Swirl             }
19820054ef0SBlue Swirl             if (dpl != rpl) {
199100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr);
20020054ef0SBlue Swirl             }
201eaa728eeSbellard         } else if (seg_reg == R_SS) {
202eaa728eeSbellard             /* SS must be writable data */
20320054ef0SBlue Swirl             if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK)) {
204100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr);
20520054ef0SBlue Swirl             }
20620054ef0SBlue Swirl             if (dpl != cpl || dpl != rpl) {
207100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr);
20820054ef0SBlue Swirl             }
209eaa728eeSbellard         } else {
210eaa728eeSbellard             /* not readable code */
21120054ef0SBlue Swirl             if ((e2 & DESC_CS_MASK) && !(e2 & DESC_R_MASK)) {
212100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr);
21320054ef0SBlue Swirl             }
214eaa728eeSbellard             /* if data or non conforming code, checks the rights */
215eaa728eeSbellard             if (((e2 >> DESC_TYPE_SHIFT) & 0xf) < 12) {
21620054ef0SBlue Swirl                 if (dpl < cpl || dpl < rpl) {
217100ec099SPavel Dovgalyuk                     raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr);
218eaa728eeSbellard                 }
219eaa728eeSbellard             }
22020054ef0SBlue Swirl         }
22120054ef0SBlue Swirl         if (!(e2 & DESC_P_MASK)) {
222100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0B_NOSEG, selector & 0xfffc, retaddr);
22320054ef0SBlue Swirl         }
224eaa728eeSbellard         cpu_x86_load_seg_cache(env, seg_reg, selector,
225eaa728eeSbellard                                get_seg_base(e1, e2),
226eaa728eeSbellard                                get_seg_limit(e1, e2),
227eaa728eeSbellard                                e2);
228eaa728eeSbellard     } else {
22920054ef0SBlue Swirl         if (seg_reg == R_SS || seg_reg == R_CS) {
230100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr);
231eaa728eeSbellard         }
232eaa728eeSbellard     }
23320054ef0SBlue Swirl }
234eaa728eeSbellard 
235eaa728eeSbellard #define SWITCH_TSS_JMP  0
236eaa728eeSbellard #define SWITCH_TSS_IRET 1
237eaa728eeSbellard #define SWITCH_TSS_CALL 2
238eaa728eeSbellard 
239eaa728eeSbellard /* XXX: restore CPU state in registers (PowerPC case) */
240100ec099SPavel Dovgalyuk static void switch_tss_ra(CPUX86State *env, int tss_selector,
241eaa728eeSbellard                           uint32_t e1, uint32_t e2, int source,
242100ec099SPavel Dovgalyuk                           uint32_t next_eip, uintptr_t retaddr)
243eaa728eeSbellard {
244eaa728eeSbellard     int tss_limit, tss_limit_max, type, old_tss_limit_max, old_type, v1, v2, i;
245eaa728eeSbellard     target_ulong tss_base;
246eaa728eeSbellard     uint32_t new_regs[8], new_segs[6];
247eaa728eeSbellard     uint32_t new_eflags, new_eip, new_cr3, new_ldt, new_trap;
248eaa728eeSbellard     uint32_t old_eflags, eflags_mask;
249eaa728eeSbellard     SegmentCache *dt;
250eaa728eeSbellard     int index;
251eaa728eeSbellard     target_ulong ptr;
252eaa728eeSbellard 
253eaa728eeSbellard     type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
25420054ef0SBlue Swirl     LOG_PCALL("switch_tss: sel=0x%04x type=%d src=%d\n", tss_selector, type,
25520054ef0SBlue Swirl               source);
256eaa728eeSbellard 
257eaa728eeSbellard     /* if task gate, we read the TSS segment and we load it */
258eaa728eeSbellard     if (type == 5) {
25920054ef0SBlue Swirl         if (!(e2 & DESC_P_MASK)) {
260100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0B_NOSEG, tss_selector & 0xfffc, retaddr);
26120054ef0SBlue Swirl         }
262eaa728eeSbellard         tss_selector = e1 >> 16;
26320054ef0SBlue Swirl         if (tss_selector & 4) {
264100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, retaddr);
26520054ef0SBlue Swirl         }
266100ec099SPavel Dovgalyuk         if (load_segment_ra(env, &e1, &e2, tss_selector, retaddr) != 0) {
267100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, tss_selector & 0xfffc, retaddr);
268eaa728eeSbellard         }
26920054ef0SBlue Swirl         if (e2 & DESC_S_MASK) {
270100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, tss_selector & 0xfffc, retaddr);
27120054ef0SBlue Swirl         }
27220054ef0SBlue Swirl         type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
27320054ef0SBlue Swirl         if ((type & 7) != 1) {
274100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, tss_selector & 0xfffc, retaddr);
27520054ef0SBlue Swirl         }
27620054ef0SBlue Swirl     }
277eaa728eeSbellard 
27820054ef0SBlue Swirl     if (!(e2 & DESC_P_MASK)) {
279100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0B_NOSEG, tss_selector & 0xfffc, retaddr);
28020054ef0SBlue Swirl     }
281eaa728eeSbellard 
28220054ef0SBlue Swirl     if (type & 8) {
283eaa728eeSbellard         tss_limit_max = 103;
28420054ef0SBlue Swirl     } else {
285eaa728eeSbellard         tss_limit_max = 43;
28620054ef0SBlue Swirl     }
287eaa728eeSbellard     tss_limit = get_seg_limit(e1, e2);
288eaa728eeSbellard     tss_base = get_seg_base(e1, e2);
289eaa728eeSbellard     if ((tss_selector & 4) != 0 ||
29020054ef0SBlue Swirl         tss_limit < tss_limit_max) {
291100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, retaddr);
29220054ef0SBlue Swirl     }
293eaa728eeSbellard     old_type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf;
29420054ef0SBlue Swirl     if (old_type & 8) {
295eaa728eeSbellard         old_tss_limit_max = 103;
29620054ef0SBlue Swirl     } else {
297eaa728eeSbellard         old_tss_limit_max = 43;
29820054ef0SBlue Swirl     }
299eaa728eeSbellard 
300eaa728eeSbellard     /* read all the registers from the new TSS */
301eaa728eeSbellard     if (type & 8) {
302eaa728eeSbellard         /* 32 bit */
303100ec099SPavel Dovgalyuk         new_cr3 = cpu_ldl_kernel_ra(env, tss_base + 0x1c, retaddr);
304100ec099SPavel Dovgalyuk         new_eip = cpu_ldl_kernel_ra(env, tss_base + 0x20, retaddr);
305100ec099SPavel Dovgalyuk         new_eflags = cpu_ldl_kernel_ra(env, tss_base + 0x24, retaddr);
30620054ef0SBlue Swirl         for (i = 0; i < 8; i++) {
307100ec099SPavel Dovgalyuk             new_regs[i] = cpu_ldl_kernel_ra(env, tss_base + (0x28 + i * 4),
308100ec099SPavel Dovgalyuk                                             retaddr);
30920054ef0SBlue Swirl         }
31020054ef0SBlue Swirl         for (i = 0; i < 6; i++) {
311100ec099SPavel Dovgalyuk             new_segs[i] = cpu_lduw_kernel_ra(env, tss_base + (0x48 + i * 4),
312100ec099SPavel Dovgalyuk                                              retaddr);
31320054ef0SBlue Swirl         }
314100ec099SPavel Dovgalyuk         new_ldt = cpu_lduw_kernel_ra(env, tss_base + 0x60, retaddr);
315100ec099SPavel Dovgalyuk         new_trap = cpu_ldl_kernel_ra(env, tss_base + 0x64, retaddr);
316eaa728eeSbellard     } else {
317eaa728eeSbellard         /* 16 bit */
318eaa728eeSbellard         new_cr3 = 0;
319100ec099SPavel Dovgalyuk         new_eip = cpu_lduw_kernel_ra(env, tss_base + 0x0e, retaddr);
320100ec099SPavel Dovgalyuk         new_eflags = cpu_lduw_kernel_ra(env, tss_base + 0x10, retaddr);
32120054ef0SBlue Swirl         for (i = 0; i < 8; i++) {
322100ec099SPavel Dovgalyuk             new_regs[i] = cpu_lduw_kernel_ra(env, tss_base + (0x12 + i * 2),
323100ec099SPavel Dovgalyuk                                              retaddr) | 0xffff0000;
32420054ef0SBlue Swirl         }
32520054ef0SBlue Swirl         for (i = 0; i < 4; i++) {
326100ec099SPavel Dovgalyuk             new_segs[i] = cpu_lduw_kernel_ra(env, tss_base + (0x22 + i * 4),
327100ec099SPavel Dovgalyuk                                              retaddr);
32820054ef0SBlue Swirl         }
329100ec099SPavel Dovgalyuk         new_ldt = cpu_lduw_kernel_ra(env, tss_base + 0x2a, retaddr);
330eaa728eeSbellard         new_segs[R_FS] = 0;
331eaa728eeSbellard         new_segs[R_GS] = 0;
332eaa728eeSbellard         new_trap = 0;
333eaa728eeSbellard     }
3344581cbcdSBlue Swirl     /* XXX: avoid a compiler warning, see
3354581cbcdSBlue Swirl      http://support.amd.com/us/Processor_TechDocs/24593.pdf
3364581cbcdSBlue Swirl      chapters 12.2.5 and 13.2.4 on how to implement TSS Trap bit */
3374581cbcdSBlue Swirl     (void)new_trap;
338eaa728eeSbellard 
339eaa728eeSbellard     /* NOTE: we must avoid memory exceptions during the task switch,
340eaa728eeSbellard        so we make dummy accesses before */
341eaa728eeSbellard     /* XXX: it can still fail in some cases, so a bigger hack is
342eaa728eeSbellard        necessary to valid the TLB after having done the accesses */
343eaa728eeSbellard 
344100ec099SPavel Dovgalyuk     v1 = cpu_ldub_kernel_ra(env, env->tr.base, retaddr);
345100ec099SPavel Dovgalyuk     v2 = cpu_ldub_kernel_ra(env, env->tr.base + old_tss_limit_max, retaddr);
346100ec099SPavel Dovgalyuk     cpu_stb_kernel_ra(env, env->tr.base, v1, retaddr);
347100ec099SPavel Dovgalyuk     cpu_stb_kernel_ra(env, env->tr.base + old_tss_limit_max, v2, retaddr);
348eaa728eeSbellard 
349eaa728eeSbellard     /* clear busy bit (it is restartable) */
350eaa728eeSbellard     if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_IRET) {
351eaa728eeSbellard         target_ulong ptr;
352eaa728eeSbellard         uint32_t e2;
35320054ef0SBlue Swirl 
354eaa728eeSbellard         ptr = env->gdt.base + (env->tr.selector & ~7);
355100ec099SPavel Dovgalyuk         e2 = cpu_ldl_kernel_ra(env, ptr + 4, retaddr);
356eaa728eeSbellard         e2 &= ~DESC_TSS_BUSY_MASK;
357100ec099SPavel Dovgalyuk         cpu_stl_kernel_ra(env, ptr + 4, e2, retaddr);
358eaa728eeSbellard     }
359997ff0d9SBlue Swirl     old_eflags = cpu_compute_eflags(env);
36020054ef0SBlue Swirl     if (source == SWITCH_TSS_IRET) {
361eaa728eeSbellard         old_eflags &= ~NT_MASK;
36220054ef0SBlue Swirl     }
363eaa728eeSbellard 
364eaa728eeSbellard     /* save the current state in the old TSS */
365eaa728eeSbellard     if (type & 8) {
366eaa728eeSbellard         /* 32 bit */
367100ec099SPavel Dovgalyuk         cpu_stl_kernel_ra(env, env->tr.base + 0x20, next_eip, retaddr);
368100ec099SPavel Dovgalyuk         cpu_stl_kernel_ra(env, env->tr.base + 0x24, old_eflags, retaddr);
369100ec099SPavel Dovgalyuk         cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 0 * 4), env->regs[R_EAX], retaddr);
370100ec099SPavel Dovgalyuk         cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 1 * 4), env->regs[R_ECX], retaddr);
371100ec099SPavel Dovgalyuk         cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 2 * 4), env->regs[R_EDX], retaddr);
372100ec099SPavel Dovgalyuk         cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 3 * 4), env->regs[R_EBX], retaddr);
373100ec099SPavel Dovgalyuk         cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 4 * 4), env->regs[R_ESP], retaddr);
374100ec099SPavel Dovgalyuk         cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 5 * 4), env->regs[R_EBP], retaddr);
375100ec099SPavel Dovgalyuk         cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 6 * 4), env->regs[R_ESI], retaddr);
376100ec099SPavel Dovgalyuk         cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 7 * 4), env->regs[R_EDI], retaddr);
37720054ef0SBlue Swirl         for (i = 0; i < 6; i++) {
378100ec099SPavel Dovgalyuk             cpu_stw_kernel_ra(env, env->tr.base + (0x48 + i * 4),
379100ec099SPavel Dovgalyuk                               env->segs[i].selector, retaddr);
38020054ef0SBlue Swirl         }
381eaa728eeSbellard     } else {
382eaa728eeSbellard         /* 16 bit */
383100ec099SPavel Dovgalyuk         cpu_stw_kernel_ra(env, env->tr.base + 0x0e, next_eip, retaddr);
384100ec099SPavel Dovgalyuk         cpu_stw_kernel_ra(env, env->tr.base + 0x10, old_eflags, retaddr);
385100ec099SPavel Dovgalyuk         cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 0 * 2), env->regs[R_EAX], retaddr);
386100ec099SPavel Dovgalyuk         cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 1 * 2), env->regs[R_ECX], retaddr);
387100ec099SPavel Dovgalyuk         cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 2 * 2), env->regs[R_EDX], retaddr);
388100ec099SPavel Dovgalyuk         cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 3 * 2), env->regs[R_EBX], retaddr);
389100ec099SPavel Dovgalyuk         cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 4 * 2), env->regs[R_ESP], retaddr);
390100ec099SPavel Dovgalyuk         cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 5 * 2), env->regs[R_EBP], retaddr);
391100ec099SPavel Dovgalyuk         cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 6 * 2), env->regs[R_ESI], retaddr);
392100ec099SPavel Dovgalyuk         cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 7 * 2), env->regs[R_EDI], retaddr);
39320054ef0SBlue Swirl         for (i = 0; i < 4; i++) {
394100ec099SPavel Dovgalyuk             cpu_stw_kernel_ra(env, env->tr.base + (0x22 + i * 4),
395100ec099SPavel Dovgalyuk                               env->segs[i].selector, retaddr);
396eaa728eeSbellard         }
39720054ef0SBlue Swirl     }
398eaa728eeSbellard 
399eaa728eeSbellard     /* now if an exception occurs, it will occurs in the next task
400eaa728eeSbellard        context */
401eaa728eeSbellard 
402eaa728eeSbellard     if (source == SWITCH_TSS_CALL) {
403100ec099SPavel Dovgalyuk         cpu_stw_kernel_ra(env, tss_base, env->tr.selector, retaddr);
404eaa728eeSbellard         new_eflags |= NT_MASK;
405eaa728eeSbellard     }
406eaa728eeSbellard 
407eaa728eeSbellard     /* set busy bit */
408eaa728eeSbellard     if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_CALL) {
409eaa728eeSbellard         target_ulong ptr;
410eaa728eeSbellard         uint32_t e2;
41120054ef0SBlue Swirl 
412eaa728eeSbellard         ptr = env->gdt.base + (tss_selector & ~7);
413100ec099SPavel Dovgalyuk         e2 = cpu_ldl_kernel_ra(env, ptr + 4, retaddr);
414eaa728eeSbellard         e2 |= DESC_TSS_BUSY_MASK;
415100ec099SPavel Dovgalyuk         cpu_stl_kernel_ra(env, ptr + 4, e2, retaddr);
416eaa728eeSbellard     }
417eaa728eeSbellard 
418eaa728eeSbellard     /* set the new CPU state */
419eaa728eeSbellard     /* from this point, any exception which occurs can give problems */
420eaa728eeSbellard     env->cr[0] |= CR0_TS_MASK;
421eaa728eeSbellard     env->hflags |= HF_TS_MASK;
422eaa728eeSbellard     env->tr.selector = tss_selector;
423eaa728eeSbellard     env->tr.base = tss_base;
424eaa728eeSbellard     env->tr.limit = tss_limit;
425eaa728eeSbellard     env->tr.flags = e2 & ~DESC_TSS_BUSY_MASK;
426eaa728eeSbellard 
427eaa728eeSbellard     if ((type & 8) && (env->cr[0] & CR0_PG_MASK)) {
428eaa728eeSbellard         cpu_x86_update_cr3(env, new_cr3);
429eaa728eeSbellard     }
430eaa728eeSbellard 
431eaa728eeSbellard     /* load all registers without an exception, then reload them with
432eaa728eeSbellard        possible exception */
433eaa728eeSbellard     env->eip = new_eip;
434eaa728eeSbellard     eflags_mask = TF_MASK | AC_MASK | ID_MASK |
435eaa728eeSbellard         IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | NT_MASK;
43620054ef0SBlue Swirl     if (!(type & 8)) {
437eaa728eeSbellard         eflags_mask &= 0xffff;
43820054ef0SBlue Swirl     }
439997ff0d9SBlue Swirl     cpu_load_eflags(env, new_eflags, eflags_mask);
440eaa728eeSbellard     /* XXX: what to do in 16 bit case? */
4414b34e3adSliguang     env->regs[R_EAX] = new_regs[0];
442a4165610Sliguang     env->regs[R_ECX] = new_regs[1];
44300f5e6f2Sliguang     env->regs[R_EDX] = new_regs[2];
44470b51365Sliguang     env->regs[R_EBX] = new_regs[3];
44508b3ded6Sliguang     env->regs[R_ESP] = new_regs[4];
446c12dddd7Sliguang     env->regs[R_EBP] = new_regs[5];
44778c3c6d3Sliguang     env->regs[R_ESI] = new_regs[6];
448cf75c597Sliguang     env->regs[R_EDI] = new_regs[7];
449eaa728eeSbellard     if (new_eflags & VM_MASK) {
45020054ef0SBlue Swirl         for (i = 0; i < 6; i++) {
4512999a0b2SBlue Swirl             load_seg_vm(env, i, new_segs[i]);
45220054ef0SBlue Swirl         }
453eaa728eeSbellard     } else {
454eaa728eeSbellard         /* first just selectors as the rest may trigger exceptions */
45520054ef0SBlue Swirl         for (i = 0; i < 6; i++) {
456eaa728eeSbellard             cpu_x86_load_seg_cache(env, i, new_segs[i], 0, 0, 0);
457eaa728eeSbellard         }
45820054ef0SBlue Swirl     }
459eaa728eeSbellard 
460eaa728eeSbellard     env->ldt.selector = new_ldt & ~4;
461eaa728eeSbellard     env->ldt.base = 0;
462eaa728eeSbellard     env->ldt.limit = 0;
463eaa728eeSbellard     env->ldt.flags = 0;
464eaa728eeSbellard 
465eaa728eeSbellard     /* load the LDT */
46620054ef0SBlue Swirl     if (new_ldt & 4) {
467100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0A_TSS, new_ldt & 0xfffc, retaddr);
46820054ef0SBlue Swirl     }
469eaa728eeSbellard 
470eaa728eeSbellard     if ((new_ldt & 0xfffc) != 0) {
471eaa728eeSbellard         dt = &env->gdt;
472eaa728eeSbellard         index = new_ldt & ~7;
47320054ef0SBlue Swirl         if ((index + 7) > dt->limit) {
474100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0A_TSS, new_ldt & 0xfffc, retaddr);
47520054ef0SBlue Swirl         }
476eaa728eeSbellard         ptr = dt->base + index;
477100ec099SPavel Dovgalyuk         e1 = cpu_ldl_kernel_ra(env, ptr, retaddr);
478100ec099SPavel Dovgalyuk         e2 = cpu_ldl_kernel_ra(env, ptr + 4, retaddr);
47920054ef0SBlue Swirl         if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2) {
480100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0A_TSS, new_ldt & 0xfffc, retaddr);
48120054ef0SBlue Swirl         }
48220054ef0SBlue Swirl         if (!(e2 & DESC_P_MASK)) {
483100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0A_TSS, new_ldt & 0xfffc, retaddr);
48420054ef0SBlue Swirl         }
485eaa728eeSbellard         load_seg_cache_raw_dt(&env->ldt, e1, e2);
486eaa728eeSbellard     }
487eaa728eeSbellard 
488eaa728eeSbellard     /* load the segments */
489eaa728eeSbellard     if (!(new_eflags & VM_MASK)) {
490d3b54918SPaolo Bonzini         int cpl = new_segs[R_CS] & 3;
491100ec099SPavel Dovgalyuk         tss_load_seg(env, R_CS, new_segs[R_CS], cpl, retaddr);
492100ec099SPavel Dovgalyuk         tss_load_seg(env, R_SS, new_segs[R_SS], cpl, retaddr);
493100ec099SPavel Dovgalyuk         tss_load_seg(env, R_ES, new_segs[R_ES], cpl, retaddr);
494100ec099SPavel Dovgalyuk         tss_load_seg(env, R_DS, new_segs[R_DS], cpl, retaddr);
495100ec099SPavel Dovgalyuk         tss_load_seg(env, R_FS, new_segs[R_FS], cpl, retaddr);
496100ec099SPavel Dovgalyuk         tss_load_seg(env, R_GS, new_segs[R_GS], cpl, retaddr);
497eaa728eeSbellard     }
498eaa728eeSbellard 
499a78d0eabSliguang     /* check that env->eip is in the CS segment limits */
500eaa728eeSbellard     if (new_eip > env->segs[R_CS].limit) {
501eaa728eeSbellard         /* XXX: different exception if CALL? */
502100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0D_GPF, 0, retaddr);
503eaa728eeSbellard     }
50401df040bSaliguori 
50501df040bSaliguori #ifndef CONFIG_USER_ONLY
50601df040bSaliguori     /* reset local breakpoints */
507428065ceSliguang     if (env->dr[7] & DR7_LOCAL_BP_MASK) {
50893d00d0fSRichard Henderson         cpu_x86_update_dr7(env, env->dr[7] & ~DR7_LOCAL_BP_MASK);
50901df040bSaliguori     }
51001df040bSaliguori #endif
511eaa728eeSbellard }
512eaa728eeSbellard 
513100ec099SPavel Dovgalyuk static void switch_tss(CPUX86State *env, int tss_selector,
514100ec099SPavel Dovgalyuk                        uint32_t e1, uint32_t e2, int source,
515100ec099SPavel Dovgalyuk                         uint32_t next_eip)
516100ec099SPavel Dovgalyuk {
517100ec099SPavel Dovgalyuk     switch_tss_ra(env, tss_selector, e1, e2, source, next_eip, 0);
518100ec099SPavel Dovgalyuk }
519100ec099SPavel Dovgalyuk 
520eaa728eeSbellard static inline unsigned int get_sp_mask(unsigned int e2)
521eaa728eeSbellard {
5220aca0605SAndrew Oates #ifdef TARGET_X86_64
5230aca0605SAndrew Oates     if (e2 & DESC_L_MASK) {
5240aca0605SAndrew Oates         return 0;
5250aca0605SAndrew Oates     } else
5260aca0605SAndrew Oates #endif
52720054ef0SBlue Swirl     if (e2 & DESC_B_MASK) {
528eaa728eeSbellard         return 0xffffffff;
52920054ef0SBlue Swirl     } else {
530eaa728eeSbellard         return 0xffff;
531eaa728eeSbellard     }
53220054ef0SBlue Swirl }
533eaa728eeSbellard 
53420054ef0SBlue Swirl static int exception_has_error_code(int intno)
5352ed51f5bSaliguori {
5362ed51f5bSaliguori     switch (intno) {
5372ed51f5bSaliguori     case 8:
5382ed51f5bSaliguori     case 10:
5392ed51f5bSaliguori     case 11:
5402ed51f5bSaliguori     case 12:
5412ed51f5bSaliguori     case 13:
5422ed51f5bSaliguori     case 14:
5432ed51f5bSaliguori     case 17:
5442ed51f5bSaliguori         return 1;
5452ed51f5bSaliguori     }
5462ed51f5bSaliguori     return 0;
5472ed51f5bSaliguori }
5482ed51f5bSaliguori 
549eaa728eeSbellard #ifdef TARGET_X86_64
550eaa728eeSbellard #define SET_ESP(val, sp_mask)                                   \
551eaa728eeSbellard     do {                                                        \
55220054ef0SBlue Swirl         if ((sp_mask) == 0xffff) {                              \
55308b3ded6Sliguang             env->regs[R_ESP] = (env->regs[R_ESP] & ~0xffff) |   \
55408b3ded6Sliguang                 ((val) & 0xffff);                               \
55520054ef0SBlue Swirl         } else if ((sp_mask) == 0xffffffffLL) {                 \
55608b3ded6Sliguang             env->regs[R_ESP] = (uint32_t)(val);                 \
55720054ef0SBlue Swirl         } else {                                                \
55808b3ded6Sliguang             env->regs[R_ESP] = (val);                           \
55920054ef0SBlue Swirl         }                                                       \
560eaa728eeSbellard     } while (0)
561eaa728eeSbellard #else
56220054ef0SBlue Swirl #define SET_ESP(val, sp_mask)                                   \
56320054ef0SBlue Swirl     do {                                                        \
56408b3ded6Sliguang         env->regs[R_ESP] = (env->regs[R_ESP] & ~(sp_mask)) |    \
56508b3ded6Sliguang             ((val) & (sp_mask));                                \
56620054ef0SBlue Swirl     } while (0)
567eaa728eeSbellard #endif
568eaa728eeSbellard 
569c0a04f0eSaliguori /* in 64-bit machines, this can overflow. So this segment addition macro
570c0a04f0eSaliguori  * can be used to trim the value to 32-bit whenever needed */
571c0a04f0eSaliguori #define SEG_ADDL(ssp, sp, sp_mask) ((uint32_t)((ssp) + (sp & (sp_mask))))
572c0a04f0eSaliguori 
573eaa728eeSbellard /* XXX: add a is_user flag to have proper security support */
574100ec099SPavel Dovgalyuk #define PUSHW_RA(ssp, sp, sp_mask, val, ra)                      \
575eaa728eeSbellard     {                                                            \
576eaa728eeSbellard         sp -= 2;                                                 \
577100ec099SPavel Dovgalyuk         cpu_stw_kernel_ra(env, (ssp) + (sp & (sp_mask)), (val), ra); \
578eaa728eeSbellard     }
579eaa728eeSbellard 
580100ec099SPavel Dovgalyuk #define PUSHL_RA(ssp, sp, sp_mask, val, ra)                             \
581eaa728eeSbellard     {                                                                   \
582eaa728eeSbellard         sp -= 4;                                                        \
583100ec099SPavel Dovgalyuk         cpu_stl_kernel_ra(env, SEG_ADDL(ssp, sp, sp_mask), (uint32_t)(val), ra); \
584eaa728eeSbellard     }
585eaa728eeSbellard 
586100ec099SPavel Dovgalyuk #define POPW_RA(ssp, sp, sp_mask, val, ra)                       \
587eaa728eeSbellard     {                                                            \
588100ec099SPavel Dovgalyuk         val = cpu_lduw_kernel_ra(env, (ssp) + (sp & (sp_mask)), ra); \
589eaa728eeSbellard         sp += 2;                                                 \
590eaa728eeSbellard     }
591eaa728eeSbellard 
592100ec099SPavel Dovgalyuk #define POPL_RA(ssp, sp, sp_mask, val, ra)                              \
593eaa728eeSbellard     {                                                                   \
594100ec099SPavel Dovgalyuk         val = (uint32_t)cpu_ldl_kernel_ra(env, SEG_ADDL(ssp, sp, sp_mask), ra); \
595eaa728eeSbellard         sp += 4;                                                        \
596eaa728eeSbellard     }
597eaa728eeSbellard 
598100ec099SPavel Dovgalyuk #define PUSHW(ssp, sp, sp_mask, val) PUSHW_RA(ssp, sp, sp_mask, val, 0)
599100ec099SPavel Dovgalyuk #define PUSHL(ssp, sp, sp_mask, val) PUSHL_RA(ssp, sp, sp_mask, val, 0)
600100ec099SPavel Dovgalyuk #define POPW(ssp, sp, sp_mask, val) POPW_RA(ssp, sp, sp_mask, val, 0)
601100ec099SPavel Dovgalyuk #define POPL(ssp, sp, sp_mask, val) POPL_RA(ssp, sp, sp_mask, val, 0)
602100ec099SPavel Dovgalyuk 
603eaa728eeSbellard /* protected mode interrupt */
6042999a0b2SBlue Swirl static void do_interrupt_protected(CPUX86State *env, int intno, int is_int,
6052999a0b2SBlue Swirl                                    int error_code, unsigned int next_eip,
6062999a0b2SBlue Swirl                                    int is_hw)
607eaa728eeSbellard {
608eaa728eeSbellard     SegmentCache *dt;
609eaa728eeSbellard     target_ulong ptr, ssp;
610eaa728eeSbellard     int type, dpl, selector, ss_dpl, cpl;
611eaa728eeSbellard     int has_error_code, new_stack, shift;
6121c918ebaSblueswir1     uint32_t e1, e2, offset, ss = 0, esp, ss_e1 = 0, ss_e2 = 0;
613eaa728eeSbellard     uint32_t old_eip, sp_mask;
61487446327SKevin O'Connor     int vm86 = env->eflags & VM_MASK;
615eaa728eeSbellard 
616eaa728eeSbellard     has_error_code = 0;
61720054ef0SBlue Swirl     if (!is_int && !is_hw) {
61820054ef0SBlue Swirl         has_error_code = exception_has_error_code(intno);
61920054ef0SBlue Swirl     }
62020054ef0SBlue Swirl     if (is_int) {
621eaa728eeSbellard         old_eip = next_eip;
62220054ef0SBlue Swirl     } else {
623eaa728eeSbellard         old_eip = env->eip;
62420054ef0SBlue Swirl     }
625eaa728eeSbellard 
626eaa728eeSbellard     dt = &env->idt;
62720054ef0SBlue Swirl     if (intno * 8 + 7 > dt->limit) {
62877b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0D_GPF, intno * 8 + 2);
62920054ef0SBlue Swirl     }
630eaa728eeSbellard     ptr = dt->base + intno * 8;
631329e607dSBlue Swirl     e1 = cpu_ldl_kernel(env, ptr);
632329e607dSBlue Swirl     e2 = cpu_ldl_kernel(env, ptr + 4);
633eaa728eeSbellard     /* check gate type */
634eaa728eeSbellard     type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
635eaa728eeSbellard     switch (type) {
636eaa728eeSbellard     case 5: /* task gate */
637eaa728eeSbellard         /* must do that check here to return the correct error code */
63820054ef0SBlue Swirl         if (!(e2 & DESC_P_MASK)) {
63977b2bc2cSBlue Swirl             raise_exception_err(env, EXCP0B_NOSEG, intno * 8 + 2);
64020054ef0SBlue Swirl         }
6412999a0b2SBlue Swirl         switch_tss(env, intno * 8, e1, e2, SWITCH_TSS_CALL, old_eip);
642eaa728eeSbellard         if (has_error_code) {
643eaa728eeSbellard             int type;
644eaa728eeSbellard             uint32_t mask;
64520054ef0SBlue Swirl 
646eaa728eeSbellard             /* push the error code */
647eaa728eeSbellard             type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf;
648eaa728eeSbellard             shift = type >> 3;
64920054ef0SBlue Swirl             if (env->segs[R_SS].flags & DESC_B_MASK) {
650eaa728eeSbellard                 mask = 0xffffffff;
65120054ef0SBlue Swirl             } else {
652eaa728eeSbellard                 mask = 0xffff;
65320054ef0SBlue Swirl             }
65408b3ded6Sliguang             esp = (env->regs[R_ESP] - (2 << shift)) & mask;
655eaa728eeSbellard             ssp = env->segs[R_SS].base + esp;
65620054ef0SBlue Swirl             if (shift) {
657329e607dSBlue Swirl                 cpu_stl_kernel(env, ssp, error_code);
65820054ef0SBlue Swirl             } else {
659329e607dSBlue Swirl                 cpu_stw_kernel(env, ssp, error_code);
66020054ef0SBlue Swirl             }
661eaa728eeSbellard             SET_ESP(esp, mask);
662eaa728eeSbellard         }
663eaa728eeSbellard         return;
664eaa728eeSbellard     case 6: /* 286 interrupt gate */
665eaa728eeSbellard     case 7: /* 286 trap gate */
666eaa728eeSbellard     case 14: /* 386 interrupt gate */
667eaa728eeSbellard     case 15: /* 386 trap gate */
668eaa728eeSbellard         break;
669eaa728eeSbellard     default:
67077b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0D_GPF, intno * 8 + 2);
671eaa728eeSbellard         break;
672eaa728eeSbellard     }
673eaa728eeSbellard     dpl = (e2 >> DESC_DPL_SHIFT) & 3;
674eaa728eeSbellard     cpl = env->hflags & HF_CPL_MASK;
6751235fc06Sths     /* check privilege if software int */
67620054ef0SBlue Swirl     if (is_int && dpl < cpl) {
67777b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0D_GPF, intno * 8 + 2);
67820054ef0SBlue Swirl     }
679eaa728eeSbellard     /* check valid bit */
68020054ef0SBlue Swirl     if (!(e2 & DESC_P_MASK)) {
68177b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0B_NOSEG, intno * 8 + 2);
68220054ef0SBlue Swirl     }
683eaa728eeSbellard     selector = e1 >> 16;
684eaa728eeSbellard     offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff);
68520054ef0SBlue Swirl     if ((selector & 0xfffc) == 0) {
68677b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0D_GPF, 0);
68720054ef0SBlue Swirl     }
6882999a0b2SBlue Swirl     if (load_segment(env, &e1, &e2, selector) != 0) {
68977b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
69020054ef0SBlue Swirl     }
69120054ef0SBlue Swirl     if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK))) {
69277b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
69320054ef0SBlue Swirl     }
694eaa728eeSbellard     dpl = (e2 >> DESC_DPL_SHIFT) & 3;
69520054ef0SBlue Swirl     if (dpl > cpl) {
69677b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
69720054ef0SBlue Swirl     }
69820054ef0SBlue Swirl     if (!(e2 & DESC_P_MASK)) {
69977b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0B_NOSEG, selector & 0xfffc);
70020054ef0SBlue Swirl     }
7011110bfe6SPaolo Bonzini     if (e2 & DESC_C_MASK) {
7021110bfe6SPaolo Bonzini         dpl = cpl;
7031110bfe6SPaolo Bonzini     }
7041110bfe6SPaolo Bonzini     if (dpl < cpl) {
705eaa728eeSbellard         /* to inner privilege */
706100ec099SPavel Dovgalyuk         get_ss_esp_from_tss(env, &ss, &esp, dpl, 0);
70720054ef0SBlue Swirl         if ((ss & 0xfffc) == 0) {
70877b2bc2cSBlue Swirl             raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc);
70920054ef0SBlue Swirl         }
71020054ef0SBlue Swirl         if ((ss & 3) != dpl) {
71177b2bc2cSBlue Swirl             raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc);
71220054ef0SBlue Swirl         }
7132999a0b2SBlue Swirl         if (load_segment(env, &ss_e1, &ss_e2, ss) != 0) {
71477b2bc2cSBlue Swirl             raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc);
71520054ef0SBlue Swirl         }
716eaa728eeSbellard         ss_dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3;
71720054ef0SBlue Swirl         if (ss_dpl != dpl) {
71877b2bc2cSBlue Swirl             raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc);
71920054ef0SBlue Swirl         }
720eaa728eeSbellard         if (!(ss_e2 & DESC_S_MASK) ||
721eaa728eeSbellard             (ss_e2 & DESC_CS_MASK) ||
72220054ef0SBlue Swirl             !(ss_e2 & DESC_W_MASK)) {
72377b2bc2cSBlue Swirl             raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc);
72420054ef0SBlue Swirl         }
72520054ef0SBlue Swirl         if (!(ss_e2 & DESC_P_MASK)) {
72677b2bc2cSBlue Swirl             raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc);
72720054ef0SBlue Swirl         }
728eaa728eeSbellard         new_stack = 1;
729eaa728eeSbellard         sp_mask = get_sp_mask(ss_e2);
730eaa728eeSbellard         ssp = get_seg_base(ss_e1, ss_e2);
7311110bfe6SPaolo Bonzini     } else  {
732eaa728eeSbellard         /* to same privilege */
73387446327SKevin O'Connor         if (vm86) {
73477b2bc2cSBlue Swirl             raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
73520054ef0SBlue Swirl         }
736eaa728eeSbellard         new_stack = 0;
737eaa728eeSbellard         sp_mask = get_sp_mask(env->segs[R_SS].flags);
738eaa728eeSbellard         ssp = env->segs[R_SS].base;
73908b3ded6Sliguang         esp = env->regs[R_ESP];
740eaa728eeSbellard     }
741eaa728eeSbellard 
742eaa728eeSbellard     shift = type >> 3;
743eaa728eeSbellard 
744eaa728eeSbellard #if 0
745eaa728eeSbellard     /* XXX: check that enough room is available */
746eaa728eeSbellard     push_size = 6 + (new_stack << 2) + (has_error_code << 1);
74787446327SKevin O'Connor     if (vm86) {
748eaa728eeSbellard         push_size += 8;
74920054ef0SBlue Swirl     }
750eaa728eeSbellard     push_size <<= shift;
751eaa728eeSbellard #endif
752eaa728eeSbellard     if (shift == 1) {
753eaa728eeSbellard         if (new_stack) {
75487446327SKevin O'Connor             if (vm86) {
755eaa728eeSbellard                 PUSHL(ssp, esp, sp_mask, env->segs[R_GS].selector);
756eaa728eeSbellard                 PUSHL(ssp, esp, sp_mask, env->segs[R_FS].selector);
757eaa728eeSbellard                 PUSHL(ssp, esp, sp_mask, env->segs[R_DS].selector);
758eaa728eeSbellard                 PUSHL(ssp, esp, sp_mask, env->segs[R_ES].selector);
759eaa728eeSbellard             }
760eaa728eeSbellard             PUSHL(ssp, esp, sp_mask, env->segs[R_SS].selector);
76108b3ded6Sliguang             PUSHL(ssp, esp, sp_mask, env->regs[R_ESP]);
762eaa728eeSbellard         }
763997ff0d9SBlue Swirl         PUSHL(ssp, esp, sp_mask, cpu_compute_eflags(env));
764eaa728eeSbellard         PUSHL(ssp, esp, sp_mask, env->segs[R_CS].selector);
765eaa728eeSbellard         PUSHL(ssp, esp, sp_mask, old_eip);
766eaa728eeSbellard         if (has_error_code) {
767eaa728eeSbellard             PUSHL(ssp, esp, sp_mask, error_code);
768eaa728eeSbellard         }
769eaa728eeSbellard     } else {
770eaa728eeSbellard         if (new_stack) {
77187446327SKevin O'Connor             if (vm86) {
772eaa728eeSbellard                 PUSHW(ssp, esp, sp_mask, env->segs[R_GS].selector);
773eaa728eeSbellard                 PUSHW(ssp, esp, sp_mask, env->segs[R_FS].selector);
774eaa728eeSbellard                 PUSHW(ssp, esp, sp_mask, env->segs[R_DS].selector);
775eaa728eeSbellard                 PUSHW(ssp, esp, sp_mask, env->segs[R_ES].selector);
776eaa728eeSbellard             }
777eaa728eeSbellard             PUSHW(ssp, esp, sp_mask, env->segs[R_SS].selector);
77808b3ded6Sliguang             PUSHW(ssp, esp, sp_mask, env->regs[R_ESP]);
779eaa728eeSbellard         }
780997ff0d9SBlue Swirl         PUSHW(ssp, esp, sp_mask, cpu_compute_eflags(env));
781eaa728eeSbellard         PUSHW(ssp, esp, sp_mask, env->segs[R_CS].selector);
782eaa728eeSbellard         PUSHW(ssp, esp, sp_mask, old_eip);
783eaa728eeSbellard         if (has_error_code) {
784eaa728eeSbellard             PUSHW(ssp, esp, sp_mask, error_code);
785eaa728eeSbellard         }
786eaa728eeSbellard     }
787eaa728eeSbellard 
788fd460606SKevin O'Connor     /* interrupt gate clear IF mask */
789fd460606SKevin O'Connor     if ((type & 1) == 0) {
790fd460606SKevin O'Connor         env->eflags &= ~IF_MASK;
791fd460606SKevin O'Connor     }
792fd460606SKevin O'Connor     env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK);
793fd460606SKevin O'Connor 
794eaa728eeSbellard     if (new_stack) {
79587446327SKevin O'Connor         if (vm86) {
796eaa728eeSbellard             cpu_x86_load_seg_cache(env, R_ES, 0, 0, 0, 0);
797eaa728eeSbellard             cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0, 0);
798eaa728eeSbellard             cpu_x86_load_seg_cache(env, R_FS, 0, 0, 0, 0);
799eaa728eeSbellard             cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0, 0);
800eaa728eeSbellard         }
801eaa728eeSbellard         ss = (ss & ~3) | dpl;
802eaa728eeSbellard         cpu_x86_load_seg_cache(env, R_SS, ss,
803eaa728eeSbellard                                ssp, get_seg_limit(ss_e1, ss_e2), ss_e2);
804eaa728eeSbellard     }
805eaa728eeSbellard     SET_ESP(esp, sp_mask);
806eaa728eeSbellard 
807eaa728eeSbellard     selector = (selector & ~3) | dpl;
808eaa728eeSbellard     cpu_x86_load_seg_cache(env, R_CS, selector,
809eaa728eeSbellard                    get_seg_base(e1, e2),
810eaa728eeSbellard                    get_seg_limit(e1, e2),
811eaa728eeSbellard                    e2);
812eaa728eeSbellard     env->eip = offset;
813eaa728eeSbellard }
814eaa728eeSbellard 
815eaa728eeSbellard #ifdef TARGET_X86_64
816eaa728eeSbellard 
817100ec099SPavel Dovgalyuk #define PUSHQ_RA(sp, val, ra)                   \
818eaa728eeSbellard     {                                           \
819eaa728eeSbellard         sp -= 8;                                \
820100ec099SPavel Dovgalyuk         cpu_stq_kernel_ra(env, sp, (val), ra);  \
821eaa728eeSbellard     }
822eaa728eeSbellard 
823100ec099SPavel Dovgalyuk #define POPQ_RA(sp, val, ra)                    \
824eaa728eeSbellard     {                                           \
825100ec099SPavel Dovgalyuk         val = cpu_ldq_kernel_ra(env, sp, ra);   \
826eaa728eeSbellard         sp += 8;                                \
827eaa728eeSbellard     }
828eaa728eeSbellard 
829100ec099SPavel Dovgalyuk #define PUSHQ(sp, val) PUSHQ_RA(sp, val, 0)
830100ec099SPavel Dovgalyuk #define POPQ(sp, val) POPQ_RA(sp, val, 0)
831100ec099SPavel Dovgalyuk 
8322999a0b2SBlue Swirl static inline target_ulong get_rsp_from_tss(CPUX86State *env, int level)
833eaa728eeSbellard {
8346aa9e42fSRichard Henderson     X86CPU *cpu = env_archcpu(env);
835eaa728eeSbellard     int index;
836eaa728eeSbellard 
837eaa728eeSbellard #if 0
838eaa728eeSbellard     printf("TR: base=" TARGET_FMT_lx " limit=%x\n",
839eaa728eeSbellard            env->tr.base, env->tr.limit);
840eaa728eeSbellard #endif
841eaa728eeSbellard 
84220054ef0SBlue Swirl     if (!(env->tr.flags & DESC_P_MASK)) {
843a47dddd7SAndreas Färber         cpu_abort(CPU(cpu), "invalid tss");
84420054ef0SBlue Swirl     }
845eaa728eeSbellard     index = 8 * level + 4;
84620054ef0SBlue Swirl     if ((index + 7) > env->tr.limit) {
84777b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0A_TSS, env->tr.selector & 0xfffc);
84820054ef0SBlue Swirl     }
849329e607dSBlue Swirl     return cpu_ldq_kernel(env, env->tr.base + index);
850eaa728eeSbellard }
851eaa728eeSbellard 
852eaa728eeSbellard /* 64 bit interrupt */
8532999a0b2SBlue Swirl static void do_interrupt64(CPUX86State *env, int intno, int is_int,
8542999a0b2SBlue Swirl                            int error_code, target_ulong next_eip, int is_hw)
855eaa728eeSbellard {
856eaa728eeSbellard     SegmentCache *dt;
857eaa728eeSbellard     target_ulong ptr;
858eaa728eeSbellard     int type, dpl, selector, cpl, ist;
859eaa728eeSbellard     int has_error_code, new_stack;
860eaa728eeSbellard     uint32_t e1, e2, e3, ss;
861eaa728eeSbellard     target_ulong old_eip, esp, offset;
862eaa728eeSbellard 
863eaa728eeSbellard     has_error_code = 0;
86420054ef0SBlue Swirl     if (!is_int && !is_hw) {
86520054ef0SBlue Swirl         has_error_code = exception_has_error_code(intno);
86620054ef0SBlue Swirl     }
86720054ef0SBlue Swirl     if (is_int) {
868eaa728eeSbellard         old_eip = next_eip;
86920054ef0SBlue Swirl     } else {
870eaa728eeSbellard         old_eip = env->eip;
87120054ef0SBlue Swirl     }
872eaa728eeSbellard 
873eaa728eeSbellard     dt = &env->idt;
87420054ef0SBlue Swirl     if (intno * 16 + 15 > dt->limit) {
87577b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0D_GPF, intno * 16 + 2);
87620054ef0SBlue Swirl     }
877eaa728eeSbellard     ptr = dt->base + intno * 16;
878329e607dSBlue Swirl     e1 = cpu_ldl_kernel(env, ptr);
879329e607dSBlue Swirl     e2 = cpu_ldl_kernel(env, ptr + 4);
880329e607dSBlue Swirl     e3 = cpu_ldl_kernel(env, ptr + 8);
881eaa728eeSbellard     /* check gate type */
882eaa728eeSbellard     type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
883eaa728eeSbellard     switch (type) {
884eaa728eeSbellard     case 14: /* 386 interrupt gate */
885eaa728eeSbellard     case 15: /* 386 trap gate */
886eaa728eeSbellard         break;
887eaa728eeSbellard     default:
88877b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0D_GPF, intno * 16 + 2);
889eaa728eeSbellard         break;
890eaa728eeSbellard     }
891eaa728eeSbellard     dpl = (e2 >> DESC_DPL_SHIFT) & 3;
892eaa728eeSbellard     cpl = env->hflags & HF_CPL_MASK;
8931235fc06Sths     /* check privilege if software int */
89420054ef0SBlue Swirl     if (is_int && dpl < cpl) {
89577b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0D_GPF, intno * 16 + 2);
89620054ef0SBlue Swirl     }
897eaa728eeSbellard     /* check valid bit */
89820054ef0SBlue Swirl     if (!(e2 & DESC_P_MASK)) {
89977b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0B_NOSEG, intno * 16 + 2);
90020054ef0SBlue Swirl     }
901eaa728eeSbellard     selector = e1 >> 16;
902eaa728eeSbellard     offset = ((target_ulong)e3 << 32) | (e2 & 0xffff0000) | (e1 & 0x0000ffff);
903eaa728eeSbellard     ist = e2 & 7;
90420054ef0SBlue Swirl     if ((selector & 0xfffc) == 0) {
90577b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0D_GPF, 0);
90620054ef0SBlue Swirl     }
907eaa728eeSbellard 
9082999a0b2SBlue Swirl     if (load_segment(env, &e1, &e2, selector) != 0) {
90977b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
91020054ef0SBlue Swirl     }
91120054ef0SBlue Swirl     if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK))) {
91277b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
91320054ef0SBlue Swirl     }
914eaa728eeSbellard     dpl = (e2 >> DESC_DPL_SHIFT) & 3;
91520054ef0SBlue Swirl     if (dpl > cpl) {
91677b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
91720054ef0SBlue Swirl     }
91820054ef0SBlue Swirl     if (!(e2 & DESC_P_MASK)) {
91977b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0B_NOSEG, selector & 0xfffc);
92020054ef0SBlue Swirl     }
92120054ef0SBlue Swirl     if (!(e2 & DESC_L_MASK) || (e2 & DESC_B_MASK)) {
92277b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
92320054ef0SBlue Swirl     }
9241110bfe6SPaolo Bonzini     if (e2 & DESC_C_MASK) {
9251110bfe6SPaolo Bonzini         dpl = cpl;
9261110bfe6SPaolo Bonzini     }
9271110bfe6SPaolo Bonzini     if (dpl < cpl || ist != 0) {
928eaa728eeSbellard         /* to inner privilege */
929eaa728eeSbellard         new_stack = 1;
930ae67dc72SPaolo Bonzini         esp = get_rsp_from_tss(env, ist != 0 ? ist + 3 : dpl);
931ae67dc72SPaolo Bonzini         ss = 0;
9321110bfe6SPaolo Bonzini     } else {
933eaa728eeSbellard         /* to same privilege */
93420054ef0SBlue Swirl         if (env->eflags & VM_MASK) {
93577b2bc2cSBlue Swirl             raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
93620054ef0SBlue Swirl         }
937eaa728eeSbellard         new_stack = 0;
93808b3ded6Sliguang         esp = env->regs[R_ESP];
939e95e9b88SWu Xiang     }
940ae67dc72SPaolo Bonzini     esp &= ~0xfLL; /* align stack */
941eaa728eeSbellard 
942eaa728eeSbellard     PUSHQ(esp, env->segs[R_SS].selector);
94308b3ded6Sliguang     PUSHQ(esp, env->regs[R_ESP]);
944997ff0d9SBlue Swirl     PUSHQ(esp, cpu_compute_eflags(env));
945eaa728eeSbellard     PUSHQ(esp, env->segs[R_CS].selector);
946eaa728eeSbellard     PUSHQ(esp, old_eip);
947eaa728eeSbellard     if (has_error_code) {
948eaa728eeSbellard         PUSHQ(esp, error_code);
949eaa728eeSbellard     }
950eaa728eeSbellard 
951fd460606SKevin O'Connor     /* interrupt gate clear IF mask */
952fd460606SKevin O'Connor     if ((type & 1) == 0) {
953fd460606SKevin O'Connor         env->eflags &= ~IF_MASK;
954fd460606SKevin O'Connor     }
955fd460606SKevin O'Connor     env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK);
956fd460606SKevin O'Connor 
957eaa728eeSbellard     if (new_stack) {
958eaa728eeSbellard         ss = 0 | dpl;
959e95e9b88SWu Xiang         cpu_x86_load_seg_cache(env, R_SS, ss, 0, 0, dpl << DESC_DPL_SHIFT);
960eaa728eeSbellard     }
96108b3ded6Sliguang     env->regs[R_ESP] = esp;
962eaa728eeSbellard 
963eaa728eeSbellard     selector = (selector & ~3) | dpl;
964eaa728eeSbellard     cpu_x86_load_seg_cache(env, R_CS, selector,
965eaa728eeSbellard                    get_seg_base(e1, e2),
966eaa728eeSbellard                    get_seg_limit(e1, e2),
967eaa728eeSbellard                    e2);
968eaa728eeSbellard     env->eip = offset;
969eaa728eeSbellard }
970eaa728eeSbellard #endif
971eaa728eeSbellard 
972d9957a8bSblueswir1 #ifdef TARGET_X86_64
973eaa728eeSbellard #if defined(CONFIG_USER_ONLY)
9742999a0b2SBlue Swirl void helper_syscall(CPUX86State *env, int next_eip_addend)
975eaa728eeSbellard {
9766aa9e42fSRichard Henderson     CPUState *cs = env_cpu(env);
97727103424SAndreas Färber 
97827103424SAndreas Färber     cs->exception_index = EXCP_SYSCALL;
97956bf1c49SDouglas Crosher     env->exception_is_int = 0;
980eaa728eeSbellard     env->exception_next_eip = env->eip + next_eip_addend;
9815638d180SAndreas Färber     cpu_loop_exit(cs);
982eaa728eeSbellard }
983eaa728eeSbellard #else
9842999a0b2SBlue Swirl void helper_syscall(CPUX86State *env, int next_eip_addend)
985eaa728eeSbellard {
986eaa728eeSbellard     int selector;
987eaa728eeSbellard 
988eaa728eeSbellard     if (!(env->efer & MSR_EFER_SCE)) {
989100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC());
990eaa728eeSbellard     }
991eaa728eeSbellard     selector = (env->star >> 32) & 0xffff;
992eaa728eeSbellard     if (env->hflags & HF_LMA_MASK) {
993eaa728eeSbellard         int code64;
994eaa728eeSbellard 
995a4165610Sliguang         env->regs[R_ECX] = env->eip + next_eip_addend;
9961a1435ddSRudolf Marek         env->regs[11] = cpu_compute_eflags(env) & ~RF_MASK;
997eaa728eeSbellard 
998eaa728eeSbellard         code64 = env->hflags & HF_CS64_MASK;
999eaa728eeSbellard 
10001a1435ddSRudolf Marek         env->eflags &= ~(env->fmask | RF_MASK);
1001fd460606SKevin O'Connor         cpu_load_eflags(env, env->eflags, 0);
1002eaa728eeSbellard         cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc,
1003eaa728eeSbellard                            0, 0xffffffff,
1004eaa728eeSbellard                                DESC_G_MASK | DESC_P_MASK |
1005eaa728eeSbellard                                DESC_S_MASK |
100620054ef0SBlue Swirl                                DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK |
100720054ef0SBlue Swirl                                DESC_L_MASK);
1008eaa728eeSbellard         cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc,
1009eaa728eeSbellard                                0, 0xffffffff,
1010eaa728eeSbellard                                DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1011eaa728eeSbellard                                DESC_S_MASK |
1012eaa728eeSbellard                                DESC_W_MASK | DESC_A_MASK);
101320054ef0SBlue Swirl         if (code64) {
1014eaa728eeSbellard             env->eip = env->lstar;
101520054ef0SBlue Swirl         } else {
1016eaa728eeSbellard             env->eip = env->cstar;
101720054ef0SBlue Swirl         }
1018d9957a8bSblueswir1     } else {
1019a4165610Sliguang         env->regs[R_ECX] = (uint32_t)(env->eip + next_eip_addend);
1020eaa728eeSbellard 
1021fd460606SKevin O'Connor         env->eflags &= ~(IF_MASK | RF_MASK | VM_MASK);
1022eaa728eeSbellard         cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc,
1023eaa728eeSbellard                            0, 0xffffffff,
1024eaa728eeSbellard                                DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1025eaa728eeSbellard                                DESC_S_MASK |
1026eaa728eeSbellard                                DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
1027eaa728eeSbellard         cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc,
1028eaa728eeSbellard                                0, 0xffffffff,
1029eaa728eeSbellard                                DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1030eaa728eeSbellard                                DESC_S_MASK |
1031eaa728eeSbellard                                DESC_W_MASK | DESC_A_MASK);
1032eaa728eeSbellard         env->eip = (uint32_t)env->star;
1033eaa728eeSbellard     }
1034eaa728eeSbellard }
1035eaa728eeSbellard #endif
1036d9957a8bSblueswir1 #endif
1037eaa728eeSbellard 
1038d9957a8bSblueswir1 #ifdef TARGET_X86_64
10392999a0b2SBlue Swirl void helper_sysret(CPUX86State *env, int dflag)
1040eaa728eeSbellard {
1041eaa728eeSbellard     int cpl, selector;
1042eaa728eeSbellard 
1043eaa728eeSbellard     if (!(env->efer & MSR_EFER_SCE)) {
1044100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC());
1045eaa728eeSbellard     }
1046eaa728eeSbellard     cpl = env->hflags & HF_CPL_MASK;
1047eaa728eeSbellard     if (!(env->cr[0] & CR0_PE_MASK) || cpl != 0) {
1048100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC());
1049eaa728eeSbellard     }
1050eaa728eeSbellard     selector = (env->star >> 48) & 0xffff;
1051eaa728eeSbellard     if (env->hflags & HF_LMA_MASK) {
1052fd460606SKevin O'Connor         cpu_load_eflags(env, (uint32_t)(env->regs[11]), TF_MASK | AC_MASK
1053fd460606SKevin O'Connor                         | ID_MASK | IF_MASK | IOPL_MASK | VM_MASK | RF_MASK |
1054fd460606SKevin O'Connor                         NT_MASK);
1055eaa728eeSbellard         if (dflag == 2) {
1056eaa728eeSbellard             cpu_x86_load_seg_cache(env, R_CS, (selector + 16) | 3,
1057eaa728eeSbellard                                    0, 0xffffffff,
1058eaa728eeSbellard                                    DESC_G_MASK | DESC_P_MASK |
1059eaa728eeSbellard                                    DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
1060eaa728eeSbellard                                    DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK |
1061eaa728eeSbellard                                    DESC_L_MASK);
1062a4165610Sliguang             env->eip = env->regs[R_ECX];
1063eaa728eeSbellard         } else {
1064eaa728eeSbellard             cpu_x86_load_seg_cache(env, R_CS, selector | 3,
1065eaa728eeSbellard                                    0, 0xffffffff,
1066eaa728eeSbellard                                    DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1067eaa728eeSbellard                                    DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
1068eaa728eeSbellard                                    DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
1069a4165610Sliguang             env->eip = (uint32_t)env->regs[R_ECX];
1070eaa728eeSbellard         }
1071ac576229SBill Paul         cpu_x86_load_seg_cache(env, R_SS, (selector + 8) | 3,
1072eaa728eeSbellard                                0, 0xffffffff,
1073eaa728eeSbellard                                DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1074eaa728eeSbellard                                DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
1075eaa728eeSbellard                                DESC_W_MASK | DESC_A_MASK);
1076d9957a8bSblueswir1     } else {
1077fd460606SKevin O'Connor         env->eflags |= IF_MASK;
1078eaa728eeSbellard         cpu_x86_load_seg_cache(env, R_CS, selector | 3,
1079eaa728eeSbellard                                0, 0xffffffff,
1080eaa728eeSbellard                                DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1081eaa728eeSbellard                                DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
1082eaa728eeSbellard                                DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
1083a4165610Sliguang         env->eip = (uint32_t)env->regs[R_ECX];
1084ac576229SBill Paul         cpu_x86_load_seg_cache(env, R_SS, (selector + 8) | 3,
1085eaa728eeSbellard                                0, 0xffffffff,
1086eaa728eeSbellard                                DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
1087eaa728eeSbellard                                DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
1088eaa728eeSbellard                                DESC_W_MASK | DESC_A_MASK);
1089eaa728eeSbellard     }
1090eaa728eeSbellard }
1091d9957a8bSblueswir1 #endif
1092eaa728eeSbellard 
1093eaa728eeSbellard /* real mode interrupt */
10942999a0b2SBlue Swirl static void do_interrupt_real(CPUX86State *env, int intno, int is_int,
10952999a0b2SBlue Swirl                               int error_code, unsigned int next_eip)
1096eaa728eeSbellard {
1097eaa728eeSbellard     SegmentCache *dt;
1098eaa728eeSbellard     target_ulong ptr, ssp;
1099eaa728eeSbellard     int selector;
1100eaa728eeSbellard     uint32_t offset, esp;
1101eaa728eeSbellard     uint32_t old_cs, old_eip;
1102eaa728eeSbellard 
1103eaa728eeSbellard     /* real mode (simpler!) */
1104eaa728eeSbellard     dt = &env->idt;
110520054ef0SBlue Swirl     if (intno * 4 + 3 > dt->limit) {
110677b2bc2cSBlue Swirl         raise_exception_err(env, EXCP0D_GPF, intno * 8 + 2);
110720054ef0SBlue Swirl     }
1108eaa728eeSbellard     ptr = dt->base + intno * 4;
1109329e607dSBlue Swirl     offset = cpu_lduw_kernel(env, ptr);
1110329e607dSBlue Swirl     selector = cpu_lduw_kernel(env, ptr + 2);
111108b3ded6Sliguang     esp = env->regs[R_ESP];
1112eaa728eeSbellard     ssp = env->segs[R_SS].base;
111320054ef0SBlue Swirl     if (is_int) {
1114eaa728eeSbellard         old_eip = next_eip;
111520054ef0SBlue Swirl     } else {
1116eaa728eeSbellard         old_eip = env->eip;
111720054ef0SBlue Swirl     }
1118eaa728eeSbellard     old_cs = env->segs[R_CS].selector;
1119eaa728eeSbellard     /* XXX: use SS segment size? */
1120997ff0d9SBlue Swirl     PUSHW(ssp, esp, 0xffff, cpu_compute_eflags(env));
1121eaa728eeSbellard     PUSHW(ssp, esp, 0xffff, old_cs);
1122eaa728eeSbellard     PUSHW(ssp, esp, 0xffff, old_eip);
1123eaa728eeSbellard 
1124eaa728eeSbellard     /* update processor state */
112508b3ded6Sliguang     env->regs[R_ESP] = (env->regs[R_ESP] & ~0xffff) | (esp & 0xffff);
1126eaa728eeSbellard     env->eip = offset;
1127eaa728eeSbellard     env->segs[R_CS].selector = selector;
1128eaa728eeSbellard     env->segs[R_CS].base = (selector << 4);
1129eaa728eeSbellard     env->eflags &= ~(IF_MASK | TF_MASK | AC_MASK | RF_MASK);
1130eaa728eeSbellard }
1131eaa728eeSbellard 
1132e694d4e2SBlue Swirl #if defined(CONFIG_USER_ONLY)
113333271823SPeter Maydell /* fake user mode interrupt. is_int is TRUE if coming from the int
113433271823SPeter Maydell  * instruction. next_eip is the env->eip value AFTER the interrupt
113533271823SPeter Maydell  * instruction. It is only relevant if is_int is TRUE or if intno
113633271823SPeter Maydell  * is EXCP_SYSCALL.
113733271823SPeter Maydell  */
11382999a0b2SBlue Swirl static void do_interrupt_user(CPUX86State *env, int intno, int is_int,
11392999a0b2SBlue Swirl                               int error_code, target_ulong next_eip)
1140eaa728eeSbellard {
1141885b7c44SStanislav Shmarov     if (is_int) {
1142eaa728eeSbellard         SegmentCache *dt;
1143eaa728eeSbellard         target_ulong ptr;
1144eaa728eeSbellard         int dpl, cpl, shift;
1145eaa728eeSbellard         uint32_t e2;
1146eaa728eeSbellard 
1147eaa728eeSbellard         dt = &env->idt;
1148eaa728eeSbellard         if (env->hflags & HF_LMA_MASK) {
1149eaa728eeSbellard             shift = 4;
1150eaa728eeSbellard         } else {
1151eaa728eeSbellard             shift = 3;
1152eaa728eeSbellard         }
1153eaa728eeSbellard         ptr = dt->base + (intno << shift);
1154329e607dSBlue Swirl         e2 = cpu_ldl_kernel(env, ptr + 4);
1155eaa728eeSbellard 
1156eaa728eeSbellard         dpl = (e2 >> DESC_DPL_SHIFT) & 3;
1157eaa728eeSbellard         cpl = env->hflags & HF_CPL_MASK;
11581235fc06Sths         /* check privilege if software int */
1159885b7c44SStanislav Shmarov         if (dpl < cpl) {
116077b2bc2cSBlue Swirl             raise_exception_err(env, EXCP0D_GPF, (intno << shift) + 2);
116120054ef0SBlue Swirl         }
1162885b7c44SStanislav Shmarov     }
1163eaa728eeSbellard 
1164eaa728eeSbellard     /* Since we emulate only user space, we cannot do more than
1165eaa728eeSbellard        exiting the emulation with the suitable exception and error
116647575997SJincheng Miao        code. So update EIP for INT 0x80 and EXCP_SYSCALL. */
116747575997SJincheng Miao     if (is_int || intno == EXCP_SYSCALL) {
1168a78d0eabSliguang         env->eip = next_eip;
1169eaa728eeSbellard     }
117020054ef0SBlue Swirl }
1171eaa728eeSbellard 
1172e694d4e2SBlue Swirl #else
1173e694d4e2SBlue Swirl 
11742999a0b2SBlue Swirl static void handle_even_inj(CPUX86State *env, int intno, int is_int,
11752999a0b2SBlue Swirl                             int error_code, int is_hw, int rm)
11762ed51f5bSaliguori {
11776aa9e42fSRichard Henderson     CPUState *cs = env_cpu(env);
1178b216aa6cSPaolo Bonzini     uint32_t event_inj = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb,
117920054ef0SBlue Swirl                                                           control.event_inj));
118020054ef0SBlue Swirl 
11812ed51f5bSaliguori     if (!(event_inj & SVM_EVTINJ_VALID)) {
11822ed51f5bSaliguori         int type;
118320054ef0SBlue Swirl 
118420054ef0SBlue Swirl         if (is_int) {
11852ed51f5bSaliguori             type = SVM_EVTINJ_TYPE_SOFT;
118620054ef0SBlue Swirl         } else {
11872ed51f5bSaliguori             type = SVM_EVTINJ_TYPE_EXEPT;
11882ed51f5bSaliguori         }
118920054ef0SBlue Swirl         event_inj = intno | type | SVM_EVTINJ_VALID;
119020054ef0SBlue Swirl         if (!rm && exception_has_error_code(intno)) {
119120054ef0SBlue Swirl             event_inj |= SVM_EVTINJ_VALID_ERR;
1192b216aa6cSPaolo Bonzini             x86_stl_phys(cs, env->vm_vmcb + offsetof(struct vmcb,
119320054ef0SBlue Swirl                                              control.event_inj_err),
119420054ef0SBlue Swirl                      error_code);
119520054ef0SBlue Swirl         }
1196b216aa6cSPaolo Bonzini         x86_stl_phys(cs,
1197ab1da857SEdgar E. Iglesias                  env->vm_vmcb + offsetof(struct vmcb, control.event_inj),
119820054ef0SBlue Swirl                  event_inj);
11992ed51f5bSaliguori     }
12002ed51f5bSaliguori }
120100ea18d1Saliguori #endif
12022ed51f5bSaliguori 
1203eaa728eeSbellard /*
1204eaa728eeSbellard  * Begin execution of an interruption. is_int is TRUE if coming from
1205a78d0eabSliguang  * the int instruction. next_eip is the env->eip value AFTER the interrupt
1206eaa728eeSbellard  * instruction. It is only relevant if is_int is TRUE.
1207eaa728eeSbellard  */
1208ca4c810aSAndreas Färber static void do_interrupt_all(X86CPU *cpu, int intno, int is_int,
12092999a0b2SBlue Swirl                              int error_code, target_ulong next_eip, int is_hw)
1210eaa728eeSbellard {
1211ca4c810aSAndreas Färber     CPUX86State *env = &cpu->env;
1212ca4c810aSAndreas Färber 
12138fec2b8cSaliguori     if (qemu_loglevel_mask(CPU_LOG_INT)) {
1214eaa728eeSbellard         if ((env->cr[0] & CR0_PE_MASK)) {
1215eaa728eeSbellard             static int count;
121620054ef0SBlue Swirl 
121720054ef0SBlue Swirl             qemu_log("%6d: v=%02x e=%04x i=%d cpl=%d IP=%04x:" TARGET_FMT_lx
121820054ef0SBlue Swirl                      " pc=" TARGET_FMT_lx " SP=%04x:" TARGET_FMT_lx,
1219eaa728eeSbellard                      count, intno, error_code, is_int,
1220eaa728eeSbellard                      env->hflags & HF_CPL_MASK,
1221a78d0eabSliguang                      env->segs[R_CS].selector, env->eip,
1222a78d0eabSliguang                      (int)env->segs[R_CS].base + env->eip,
122308b3ded6Sliguang                      env->segs[R_SS].selector, env->regs[R_ESP]);
1224eaa728eeSbellard             if (intno == 0x0e) {
122593fcfe39Saliguori                 qemu_log(" CR2=" TARGET_FMT_lx, env->cr[2]);
1226eaa728eeSbellard             } else {
12274b34e3adSliguang                 qemu_log(" env->regs[R_EAX]=" TARGET_FMT_lx, env->regs[R_EAX]);
1228eaa728eeSbellard             }
122993fcfe39Saliguori             qemu_log("\n");
1230a0762859SAndreas Färber             log_cpu_state(CPU(cpu), CPU_DUMP_CCOP);
1231eaa728eeSbellard #if 0
1232eaa728eeSbellard             {
1233eaa728eeSbellard                 int i;
12349bd5494eSAdam Lackorzynski                 target_ulong ptr;
123520054ef0SBlue Swirl 
123693fcfe39Saliguori                 qemu_log("       code=");
1237eaa728eeSbellard                 ptr = env->segs[R_CS].base + env->eip;
1238eaa728eeSbellard                 for (i = 0; i < 16; i++) {
123993fcfe39Saliguori                     qemu_log(" %02x", ldub(ptr + i));
1240eaa728eeSbellard                 }
124193fcfe39Saliguori                 qemu_log("\n");
1242eaa728eeSbellard             }
1243eaa728eeSbellard #endif
1244eaa728eeSbellard             count++;
1245eaa728eeSbellard         }
1246eaa728eeSbellard     }
1247eaa728eeSbellard     if (env->cr[0] & CR0_PE_MASK) {
124800ea18d1Saliguori #if !defined(CONFIG_USER_ONLY)
1249f8dc4c64SPaolo Bonzini         if (env->hflags & HF_GUEST_MASK) {
12502999a0b2SBlue Swirl             handle_even_inj(env, intno, is_int, error_code, is_hw, 0);
125120054ef0SBlue Swirl         }
125200ea18d1Saliguori #endif
1253eb38c52cSblueswir1 #ifdef TARGET_X86_64
1254eaa728eeSbellard         if (env->hflags & HF_LMA_MASK) {
12552999a0b2SBlue Swirl             do_interrupt64(env, intno, is_int, error_code, next_eip, is_hw);
1256eaa728eeSbellard         } else
1257eaa728eeSbellard #endif
1258eaa728eeSbellard         {
12592999a0b2SBlue Swirl             do_interrupt_protected(env, intno, is_int, error_code, next_eip,
12602999a0b2SBlue Swirl                                    is_hw);
1261eaa728eeSbellard         }
1262eaa728eeSbellard     } else {
126300ea18d1Saliguori #if !defined(CONFIG_USER_ONLY)
1264f8dc4c64SPaolo Bonzini         if (env->hflags & HF_GUEST_MASK) {
12652999a0b2SBlue Swirl             handle_even_inj(env, intno, is_int, error_code, is_hw, 1);
126620054ef0SBlue Swirl         }
126700ea18d1Saliguori #endif
12682999a0b2SBlue Swirl         do_interrupt_real(env, intno, is_int, error_code, next_eip);
1269eaa728eeSbellard     }
12702ed51f5bSaliguori 
127100ea18d1Saliguori #if !defined(CONFIG_USER_ONLY)
1272f8dc4c64SPaolo Bonzini     if (env->hflags & HF_GUEST_MASK) {
1273fdfba1a2SEdgar E. Iglesias         CPUState *cs = CPU(cpu);
1274b216aa6cSPaolo Bonzini         uint32_t event_inj = x86_ldl_phys(cs, env->vm_vmcb +
127520054ef0SBlue Swirl                                       offsetof(struct vmcb,
127620054ef0SBlue Swirl                                                control.event_inj));
127720054ef0SBlue Swirl 
1278b216aa6cSPaolo Bonzini         x86_stl_phys(cs,
1279ab1da857SEdgar E. Iglesias                  env->vm_vmcb + offsetof(struct vmcb, control.event_inj),
128020054ef0SBlue Swirl                  event_inj & ~SVM_EVTINJ_VALID);
12812ed51f5bSaliguori     }
128200ea18d1Saliguori #endif
1283eaa728eeSbellard }
1284eaa728eeSbellard 
128597a8ea5aSAndreas Färber void x86_cpu_do_interrupt(CPUState *cs)
1286e694d4e2SBlue Swirl {
128797a8ea5aSAndreas Färber     X86CPU *cpu = X86_CPU(cs);
128897a8ea5aSAndreas Färber     CPUX86State *env = &cpu->env;
128997a8ea5aSAndreas Färber 
1290e694d4e2SBlue Swirl #if defined(CONFIG_USER_ONLY)
1291e694d4e2SBlue Swirl     /* if user mode only, we simulate a fake exception
1292e694d4e2SBlue Swirl        which will be handled outside the cpu execution
1293e694d4e2SBlue Swirl        loop */
129427103424SAndreas Färber     do_interrupt_user(env, cs->exception_index,
1295e694d4e2SBlue Swirl                       env->exception_is_int,
1296e694d4e2SBlue Swirl                       env->error_code,
1297e694d4e2SBlue Swirl                       env->exception_next_eip);
1298e694d4e2SBlue Swirl     /* successfully delivered */
1299e694d4e2SBlue Swirl     env->old_exception = -1;
1300e694d4e2SBlue Swirl #else
130110cde894SPaolo Bonzini     if (cs->exception_index >= EXCP_VMEXIT) {
130210cde894SPaolo Bonzini         assert(env->old_exception == -1);
130310cde894SPaolo Bonzini         do_vmexit(env, cs->exception_index - EXCP_VMEXIT, env->error_code);
130410cde894SPaolo Bonzini     } else {
130527103424SAndreas Färber         do_interrupt_all(cpu, cs->exception_index,
1306e694d4e2SBlue Swirl                          env->exception_is_int,
1307e694d4e2SBlue Swirl                          env->error_code,
1308e694d4e2SBlue Swirl                          env->exception_next_eip, 0);
1309e694d4e2SBlue Swirl         /* successfully delivered */
1310e694d4e2SBlue Swirl         env->old_exception = -1;
131110cde894SPaolo Bonzini     }
1312e694d4e2SBlue Swirl #endif
1313e694d4e2SBlue Swirl }
1314e694d4e2SBlue Swirl 
13152999a0b2SBlue Swirl void do_interrupt_x86_hardirq(CPUX86State *env, int intno, int is_hw)
1316e694d4e2SBlue Swirl {
13176aa9e42fSRichard Henderson     do_interrupt_all(env_archcpu(env), intno, 0, 0, 0, is_hw);
1318e694d4e2SBlue Swirl }
1319e694d4e2SBlue Swirl 
132042f53feaSRichard Henderson bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
132142f53feaSRichard Henderson {
132242f53feaSRichard Henderson     X86CPU *cpu = X86_CPU(cs);
132342f53feaSRichard Henderson     CPUX86State *env = &cpu->env;
132492d5f1a4SPaolo Bonzini     int intno;
132542f53feaSRichard Henderson 
132692d5f1a4SPaolo Bonzini     interrupt_request = x86_cpu_pending_interrupt(cs, interrupt_request);
132792d5f1a4SPaolo Bonzini     if (!interrupt_request) {
132892d5f1a4SPaolo Bonzini         return false;
132992d5f1a4SPaolo Bonzini     }
133092d5f1a4SPaolo Bonzini 
133192d5f1a4SPaolo Bonzini     /* Don't process multiple interrupt requests in a single call.
133292d5f1a4SPaolo Bonzini      * This is required to make icount-driven execution deterministic.
133392d5f1a4SPaolo Bonzini      */
133492d5f1a4SPaolo Bonzini     switch (interrupt_request) {
133542f53feaSRichard Henderson #if !defined(CONFIG_USER_ONLY)
133692d5f1a4SPaolo Bonzini     case CPU_INTERRUPT_POLL:
133742f53feaSRichard Henderson         cs->interrupt_request &= ~CPU_INTERRUPT_POLL;
133842f53feaSRichard Henderson         apic_poll_irq(cpu->apic_state);
133992d5f1a4SPaolo Bonzini         break;
134042f53feaSRichard Henderson #endif
134192d5f1a4SPaolo Bonzini     case CPU_INTERRUPT_SIPI:
134242f53feaSRichard Henderson         do_cpu_sipi(cpu);
134392d5f1a4SPaolo Bonzini         break;
134492d5f1a4SPaolo Bonzini     case CPU_INTERRUPT_SMI:
134565c9d60aSPaolo Bonzini         cpu_svm_check_intercept_param(env, SVM_EXIT_SMI, 0, 0);
134642f53feaSRichard Henderson         cs->interrupt_request &= ~CPU_INTERRUPT_SMI;
134742f53feaSRichard Henderson         do_smm_enter(cpu);
134892d5f1a4SPaolo Bonzini         break;
134992d5f1a4SPaolo Bonzini     case CPU_INTERRUPT_NMI:
135002f7fd25SJan Kiszka         cpu_svm_check_intercept_param(env, SVM_EXIT_NMI, 0, 0);
135142f53feaSRichard Henderson         cs->interrupt_request &= ~CPU_INTERRUPT_NMI;
135242f53feaSRichard Henderson         env->hflags2 |= HF2_NMI_MASK;
135342f53feaSRichard Henderson         do_interrupt_x86_hardirq(env, EXCP02_NMI, 1);
135492d5f1a4SPaolo Bonzini         break;
135592d5f1a4SPaolo Bonzini     case CPU_INTERRUPT_MCE:
135642f53feaSRichard Henderson         cs->interrupt_request &= ~CPU_INTERRUPT_MCE;
135742f53feaSRichard Henderson         do_interrupt_x86_hardirq(env, EXCP12_MCHK, 0);
135892d5f1a4SPaolo Bonzini         break;
135992d5f1a4SPaolo Bonzini     case CPU_INTERRUPT_HARD:
136065c9d60aSPaolo Bonzini         cpu_svm_check_intercept_param(env, SVM_EXIT_INTR, 0, 0);
136142f53feaSRichard Henderson         cs->interrupt_request &= ~(CPU_INTERRUPT_HARD |
136242f53feaSRichard Henderson                                    CPU_INTERRUPT_VIRQ);
136342f53feaSRichard Henderson         intno = cpu_get_pic_interrupt(env);
136442f53feaSRichard Henderson         qemu_log_mask(CPU_LOG_TB_IN_ASM,
136542f53feaSRichard Henderson                       "Servicing hardware INT=0x%02x\n", intno);
136642f53feaSRichard Henderson         do_interrupt_x86_hardirq(env, intno, 1);
136792d5f1a4SPaolo Bonzini         break;
136842f53feaSRichard Henderson #if !defined(CONFIG_USER_ONLY)
136992d5f1a4SPaolo Bonzini     case CPU_INTERRUPT_VIRQ:
137042f53feaSRichard Henderson         /* FIXME: this should respect TPR */
137165c9d60aSPaolo Bonzini         cpu_svm_check_intercept_param(env, SVM_EXIT_VINTR, 0, 0);
1372b216aa6cSPaolo Bonzini         intno = x86_ldl_phys(cs, env->vm_vmcb
137342f53feaSRichard Henderson                              + offsetof(struct vmcb, control.int_vector));
137442f53feaSRichard Henderson         qemu_log_mask(CPU_LOG_TB_IN_ASM,
137542f53feaSRichard Henderson                       "Servicing virtual hardware INT=0x%02x\n", intno);
137642f53feaSRichard Henderson         do_interrupt_x86_hardirq(env, intno, 1);
137742f53feaSRichard Henderson         cs->interrupt_request &= ~CPU_INTERRUPT_VIRQ;
137892d5f1a4SPaolo Bonzini         break;
137942f53feaSRichard Henderson #endif
138042f53feaSRichard Henderson     }
138142f53feaSRichard Henderson 
138292d5f1a4SPaolo Bonzini     /* Ensure that no TB jump will be modified as the program flow was changed.  */
138392d5f1a4SPaolo Bonzini     return true;
138442f53feaSRichard Henderson }
138542f53feaSRichard Henderson 
13862999a0b2SBlue Swirl void helper_lldt(CPUX86State *env, int selector)
1387eaa728eeSbellard {
1388eaa728eeSbellard     SegmentCache *dt;
1389eaa728eeSbellard     uint32_t e1, e2;
1390eaa728eeSbellard     int index, entry_limit;
1391eaa728eeSbellard     target_ulong ptr;
1392eaa728eeSbellard 
1393eaa728eeSbellard     selector &= 0xffff;
1394eaa728eeSbellard     if ((selector & 0xfffc) == 0) {
1395eaa728eeSbellard         /* XXX: NULL selector case: invalid LDT */
1396eaa728eeSbellard         env->ldt.base = 0;
1397eaa728eeSbellard         env->ldt.limit = 0;
1398eaa728eeSbellard     } else {
139920054ef0SBlue Swirl         if (selector & 0x4) {
1400100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
140120054ef0SBlue Swirl         }
1402eaa728eeSbellard         dt = &env->gdt;
1403eaa728eeSbellard         index = selector & ~7;
1404eaa728eeSbellard #ifdef TARGET_X86_64
140520054ef0SBlue Swirl         if (env->hflags & HF_LMA_MASK) {
1406eaa728eeSbellard             entry_limit = 15;
140720054ef0SBlue Swirl         } else
1408eaa728eeSbellard #endif
140920054ef0SBlue Swirl         {
1410eaa728eeSbellard             entry_limit = 7;
141120054ef0SBlue Swirl         }
141220054ef0SBlue Swirl         if ((index + entry_limit) > dt->limit) {
1413100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
141420054ef0SBlue Swirl         }
1415eaa728eeSbellard         ptr = dt->base + index;
1416100ec099SPavel Dovgalyuk         e1 = cpu_ldl_kernel_ra(env, ptr, GETPC());
1417100ec099SPavel Dovgalyuk         e2 = cpu_ldl_kernel_ra(env, ptr + 4, GETPC());
141820054ef0SBlue Swirl         if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2) {
1419100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
142020054ef0SBlue Swirl         }
142120054ef0SBlue Swirl         if (!(e2 & DESC_P_MASK)) {
1422100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0B_NOSEG, selector & 0xfffc, GETPC());
142320054ef0SBlue Swirl         }
1424eaa728eeSbellard #ifdef TARGET_X86_64
1425eaa728eeSbellard         if (env->hflags & HF_LMA_MASK) {
1426eaa728eeSbellard             uint32_t e3;
142720054ef0SBlue Swirl 
1428100ec099SPavel Dovgalyuk             e3 = cpu_ldl_kernel_ra(env, ptr + 8, GETPC());
1429eaa728eeSbellard             load_seg_cache_raw_dt(&env->ldt, e1, e2);
1430eaa728eeSbellard             env->ldt.base |= (target_ulong)e3 << 32;
1431eaa728eeSbellard         } else
1432eaa728eeSbellard #endif
1433eaa728eeSbellard         {
1434eaa728eeSbellard             load_seg_cache_raw_dt(&env->ldt, e1, e2);
1435eaa728eeSbellard         }
1436eaa728eeSbellard     }
1437eaa728eeSbellard     env->ldt.selector = selector;
1438eaa728eeSbellard }
1439eaa728eeSbellard 
14402999a0b2SBlue Swirl void helper_ltr(CPUX86State *env, int selector)
1441eaa728eeSbellard {
1442eaa728eeSbellard     SegmentCache *dt;
1443eaa728eeSbellard     uint32_t e1, e2;
1444eaa728eeSbellard     int index, type, entry_limit;
1445eaa728eeSbellard     target_ulong ptr;
1446eaa728eeSbellard 
1447eaa728eeSbellard     selector &= 0xffff;
1448eaa728eeSbellard     if ((selector & 0xfffc) == 0) {
1449eaa728eeSbellard         /* NULL selector case: invalid TR */
1450eaa728eeSbellard         env->tr.base = 0;
1451eaa728eeSbellard         env->tr.limit = 0;
1452eaa728eeSbellard         env->tr.flags = 0;
1453eaa728eeSbellard     } else {
145420054ef0SBlue Swirl         if (selector & 0x4) {
1455100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
145620054ef0SBlue Swirl         }
1457eaa728eeSbellard         dt = &env->gdt;
1458eaa728eeSbellard         index = selector & ~7;
1459eaa728eeSbellard #ifdef TARGET_X86_64
146020054ef0SBlue Swirl         if (env->hflags & HF_LMA_MASK) {
1461eaa728eeSbellard             entry_limit = 15;
146220054ef0SBlue Swirl         } else
1463eaa728eeSbellard #endif
146420054ef0SBlue Swirl         {
1465eaa728eeSbellard             entry_limit = 7;
146620054ef0SBlue Swirl         }
146720054ef0SBlue Swirl         if ((index + entry_limit) > dt->limit) {
1468100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
146920054ef0SBlue Swirl         }
1470eaa728eeSbellard         ptr = dt->base + index;
1471100ec099SPavel Dovgalyuk         e1 = cpu_ldl_kernel_ra(env, ptr, GETPC());
1472100ec099SPavel Dovgalyuk         e2 = cpu_ldl_kernel_ra(env, ptr + 4, GETPC());
1473eaa728eeSbellard         type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
1474eaa728eeSbellard         if ((e2 & DESC_S_MASK) ||
147520054ef0SBlue Swirl             (type != 1 && type != 9)) {
1476100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
147720054ef0SBlue Swirl         }
147820054ef0SBlue Swirl         if (!(e2 & DESC_P_MASK)) {
1479100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0B_NOSEG, selector & 0xfffc, GETPC());
148020054ef0SBlue Swirl         }
1481eaa728eeSbellard #ifdef TARGET_X86_64
1482eaa728eeSbellard         if (env->hflags & HF_LMA_MASK) {
1483eaa728eeSbellard             uint32_t e3, e4;
148420054ef0SBlue Swirl 
1485100ec099SPavel Dovgalyuk             e3 = cpu_ldl_kernel_ra(env, ptr + 8, GETPC());
1486100ec099SPavel Dovgalyuk             e4 = cpu_ldl_kernel_ra(env, ptr + 12, GETPC());
148720054ef0SBlue Swirl             if ((e4 >> DESC_TYPE_SHIFT) & 0xf) {
1488100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
148920054ef0SBlue Swirl             }
1490eaa728eeSbellard             load_seg_cache_raw_dt(&env->tr, e1, e2);
1491eaa728eeSbellard             env->tr.base |= (target_ulong)e3 << 32;
1492eaa728eeSbellard         } else
1493eaa728eeSbellard #endif
1494eaa728eeSbellard         {
1495eaa728eeSbellard             load_seg_cache_raw_dt(&env->tr, e1, e2);
1496eaa728eeSbellard         }
1497eaa728eeSbellard         e2 |= DESC_TSS_BUSY_MASK;
1498100ec099SPavel Dovgalyuk         cpu_stl_kernel_ra(env, ptr + 4, e2, GETPC());
1499eaa728eeSbellard     }
1500eaa728eeSbellard     env->tr.selector = selector;
1501eaa728eeSbellard }
1502eaa728eeSbellard 
1503eaa728eeSbellard /* only works if protected mode and not VM86. seg_reg must be != R_CS */
15042999a0b2SBlue Swirl void helper_load_seg(CPUX86State *env, int seg_reg, int selector)
1505eaa728eeSbellard {
1506eaa728eeSbellard     uint32_t e1, e2;
1507eaa728eeSbellard     int cpl, dpl, rpl;
1508eaa728eeSbellard     SegmentCache *dt;
1509eaa728eeSbellard     int index;
1510eaa728eeSbellard     target_ulong ptr;
1511eaa728eeSbellard 
1512eaa728eeSbellard     selector &= 0xffff;
1513eaa728eeSbellard     cpl = env->hflags & HF_CPL_MASK;
1514eaa728eeSbellard     if ((selector & 0xfffc) == 0) {
1515eaa728eeSbellard         /* null selector case */
1516eaa728eeSbellard         if (seg_reg == R_SS
1517eaa728eeSbellard #ifdef TARGET_X86_64
1518eaa728eeSbellard             && (!(env->hflags & HF_CS64_MASK) || cpl == 3)
1519eaa728eeSbellard #endif
152020054ef0SBlue Swirl             ) {
1521100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC());
152220054ef0SBlue Swirl         }
1523eaa728eeSbellard         cpu_x86_load_seg_cache(env, seg_reg, selector, 0, 0, 0);
1524eaa728eeSbellard     } else {
1525eaa728eeSbellard 
152620054ef0SBlue Swirl         if (selector & 0x4) {
1527eaa728eeSbellard             dt = &env->ldt;
152820054ef0SBlue Swirl         } else {
1529eaa728eeSbellard             dt = &env->gdt;
153020054ef0SBlue Swirl         }
1531eaa728eeSbellard         index = selector & ~7;
153220054ef0SBlue Swirl         if ((index + 7) > dt->limit) {
1533100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
153420054ef0SBlue Swirl         }
1535eaa728eeSbellard         ptr = dt->base + index;
1536100ec099SPavel Dovgalyuk         e1 = cpu_ldl_kernel_ra(env, ptr, GETPC());
1537100ec099SPavel Dovgalyuk         e2 = cpu_ldl_kernel_ra(env, ptr + 4, GETPC());
1538eaa728eeSbellard 
153920054ef0SBlue Swirl         if (!(e2 & DESC_S_MASK)) {
1540100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
154120054ef0SBlue Swirl         }
1542eaa728eeSbellard         rpl = selector & 3;
1543eaa728eeSbellard         dpl = (e2 >> DESC_DPL_SHIFT) & 3;
1544eaa728eeSbellard         if (seg_reg == R_SS) {
1545eaa728eeSbellard             /* must be writable segment */
154620054ef0SBlue Swirl             if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK)) {
1547100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
154820054ef0SBlue Swirl             }
154920054ef0SBlue Swirl             if (rpl != cpl || dpl != cpl) {
1550100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
155120054ef0SBlue Swirl             }
1552eaa728eeSbellard         } else {
1553eaa728eeSbellard             /* must be readable segment */
155420054ef0SBlue Swirl             if ((e2 & (DESC_CS_MASK | DESC_R_MASK)) == DESC_CS_MASK) {
1555100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
155620054ef0SBlue Swirl             }
1557eaa728eeSbellard 
1558eaa728eeSbellard             if (!(e2 & DESC_CS_MASK) || !(e2 & DESC_C_MASK)) {
1559eaa728eeSbellard                 /* if not conforming code, test rights */
156020054ef0SBlue Swirl                 if (dpl < cpl || dpl < rpl) {
1561100ec099SPavel Dovgalyuk                     raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
1562eaa728eeSbellard                 }
1563eaa728eeSbellard             }
156420054ef0SBlue Swirl         }
1565eaa728eeSbellard 
1566eaa728eeSbellard         if (!(e2 & DESC_P_MASK)) {
156720054ef0SBlue Swirl             if (seg_reg == R_SS) {
1568100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0C_STACK, selector & 0xfffc, GETPC());
156920054ef0SBlue Swirl             } else {
1570100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0B_NOSEG, selector & 0xfffc, GETPC());
1571eaa728eeSbellard             }
157220054ef0SBlue Swirl         }
1573eaa728eeSbellard 
1574eaa728eeSbellard         /* set the access bit if not already set */
1575eaa728eeSbellard         if (!(e2 & DESC_A_MASK)) {
1576eaa728eeSbellard             e2 |= DESC_A_MASK;
1577100ec099SPavel Dovgalyuk             cpu_stl_kernel_ra(env, ptr + 4, e2, GETPC());
1578eaa728eeSbellard         }
1579eaa728eeSbellard 
1580eaa728eeSbellard         cpu_x86_load_seg_cache(env, seg_reg, selector,
1581eaa728eeSbellard                        get_seg_base(e1, e2),
1582eaa728eeSbellard                        get_seg_limit(e1, e2),
1583eaa728eeSbellard                        e2);
1584eaa728eeSbellard #if 0
158593fcfe39Saliguori         qemu_log("load_seg: sel=0x%04x base=0x%08lx limit=0x%08lx flags=%08x\n",
1586eaa728eeSbellard                 selector, (unsigned long)sc->base, sc->limit, sc->flags);
1587eaa728eeSbellard #endif
1588eaa728eeSbellard     }
1589eaa728eeSbellard }
1590eaa728eeSbellard 
1591eaa728eeSbellard /* protected mode jump */
15922999a0b2SBlue Swirl void helper_ljmp_protected(CPUX86State *env, int new_cs, target_ulong new_eip,
1593100ec099SPavel Dovgalyuk                            target_ulong next_eip)
1594eaa728eeSbellard {
1595eaa728eeSbellard     int gate_cs, type;
1596eaa728eeSbellard     uint32_t e1, e2, cpl, dpl, rpl, limit;
1597eaa728eeSbellard 
159820054ef0SBlue Swirl     if ((new_cs & 0xfffc) == 0) {
1599100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC());
160020054ef0SBlue Swirl     }
1601100ec099SPavel Dovgalyuk     if (load_segment_ra(env, &e1, &e2, new_cs, GETPC()) != 0) {
1602100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
160320054ef0SBlue Swirl     }
1604eaa728eeSbellard     cpl = env->hflags & HF_CPL_MASK;
1605eaa728eeSbellard     if (e2 & DESC_S_MASK) {
160620054ef0SBlue Swirl         if (!(e2 & DESC_CS_MASK)) {
1607100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
160820054ef0SBlue Swirl         }
1609eaa728eeSbellard         dpl = (e2 >> DESC_DPL_SHIFT) & 3;
1610eaa728eeSbellard         if (e2 & DESC_C_MASK) {
1611eaa728eeSbellard             /* conforming code segment */
161220054ef0SBlue Swirl             if (dpl > cpl) {
1613100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
161420054ef0SBlue Swirl             }
1615eaa728eeSbellard         } else {
1616eaa728eeSbellard             /* non conforming code segment */
1617eaa728eeSbellard             rpl = new_cs & 3;
161820054ef0SBlue Swirl             if (rpl > cpl) {
1619100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
1620eaa728eeSbellard             }
162120054ef0SBlue Swirl             if (dpl != cpl) {
1622100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
162320054ef0SBlue Swirl             }
162420054ef0SBlue Swirl         }
162520054ef0SBlue Swirl         if (!(e2 & DESC_P_MASK)) {
1626100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, GETPC());
162720054ef0SBlue Swirl         }
1628eaa728eeSbellard         limit = get_seg_limit(e1, e2);
1629eaa728eeSbellard         if (new_eip > limit &&
1630db7196dbSAndrew Oates             (!(env->hflags & HF_LMA_MASK) || !(e2 & DESC_L_MASK))) {
1631db7196dbSAndrew Oates             raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC());
163220054ef0SBlue Swirl         }
1633eaa728eeSbellard         cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
1634eaa728eeSbellard                        get_seg_base(e1, e2), limit, e2);
1635a78d0eabSliguang         env->eip = new_eip;
1636eaa728eeSbellard     } else {
1637eaa728eeSbellard         /* jump to call or task gate */
1638eaa728eeSbellard         dpl = (e2 >> DESC_DPL_SHIFT) & 3;
1639eaa728eeSbellard         rpl = new_cs & 3;
1640eaa728eeSbellard         cpl = env->hflags & HF_CPL_MASK;
1641eaa728eeSbellard         type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
16420aca0605SAndrew Oates 
16430aca0605SAndrew Oates #ifdef TARGET_X86_64
16440aca0605SAndrew Oates         if (env->efer & MSR_EFER_LMA) {
16450aca0605SAndrew Oates             if (type != 12) {
16460aca0605SAndrew Oates                 raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
16470aca0605SAndrew Oates             }
16480aca0605SAndrew Oates         }
16490aca0605SAndrew Oates #endif
1650eaa728eeSbellard         switch (type) {
1651eaa728eeSbellard         case 1: /* 286 TSS */
1652eaa728eeSbellard         case 9: /* 386 TSS */
1653eaa728eeSbellard         case 5: /* task gate */
165420054ef0SBlue Swirl             if (dpl < cpl || dpl < rpl) {
1655100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
165620054ef0SBlue Swirl             }
1657100ec099SPavel Dovgalyuk             switch_tss_ra(env, new_cs, e1, e2, SWITCH_TSS_JMP, next_eip, GETPC());
1658eaa728eeSbellard             break;
1659eaa728eeSbellard         case 4: /* 286 call gate */
1660eaa728eeSbellard         case 12: /* 386 call gate */
166120054ef0SBlue Swirl             if ((dpl < cpl) || (dpl < rpl)) {
1662100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
166320054ef0SBlue Swirl             }
166420054ef0SBlue Swirl             if (!(e2 & DESC_P_MASK)) {
1665100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, GETPC());
166620054ef0SBlue Swirl             }
1667eaa728eeSbellard             gate_cs = e1 >> 16;
1668eaa728eeSbellard             new_eip = (e1 & 0xffff);
166920054ef0SBlue Swirl             if (type == 12) {
1670eaa728eeSbellard                 new_eip |= (e2 & 0xffff0000);
167120054ef0SBlue Swirl             }
16720aca0605SAndrew Oates 
16730aca0605SAndrew Oates #ifdef TARGET_X86_64
16740aca0605SAndrew Oates             if (env->efer & MSR_EFER_LMA) {
16750aca0605SAndrew Oates                 /* load the upper 8 bytes of the 64-bit call gate */
16760aca0605SAndrew Oates                 if (load_segment_ra(env, &e1, &e2, new_cs + 8, GETPC())) {
16770aca0605SAndrew Oates                     raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc,
16780aca0605SAndrew Oates                                            GETPC());
16790aca0605SAndrew Oates                 }
16800aca0605SAndrew Oates                 type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
16810aca0605SAndrew Oates                 if (type != 0) {
16820aca0605SAndrew Oates                     raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc,
16830aca0605SAndrew Oates                                            GETPC());
16840aca0605SAndrew Oates                 }
16850aca0605SAndrew Oates                 new_eip |= ((target_ulong)e1) << 32;
16860aca0605SAndrew Oates             }
16870aca0605SAndrew Oates #endif
16880aca0605SAndrew Oates 
1689100ec099SPavel Dovgalyuk             if (load_segment_ra(env, &e1, &e2, gate_cs, GETPC()) != 0) {
1690100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC());
169120054ef0SBlue Swirl             }
1692eaa728eeSbellard             dpl = (e2 >> DESC_DPL_SHIFT) & 3;
1693eaa728eeSbellard             /* must be code segment */
1694eaa728eeSbellard             if (((e2 & (DESC_S_MASK | DESC_CS_MASK)) !=
169520054ef0SBlue Swirl                  (DESC_S_MASK | DESC_CS_MASK))) {
1696100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC());
169720054ef0SBlue Swirl             }
1698eaa728eeSbellard             if (((e2 & DESC_C_MASK) && (dpl > cpl)) ||
169920054ef0SBlue Swirl                 (!(e2 & DESC_C_MASK) && (dpl != cpl))) {
1700100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC());
170120054ef0SBlue Swirl             }
17020aca0605SAndrew Oates #ifdef TARGET_X86_64
17030aca0605SAndrew Oates             if (env->efer & MSR_EFER_LMA) {
17040aca0605SAndrew Oates                 if (!(e2 & DESC_L_MASK)) {
17050aca0605SAndrew Oates                     raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC());
17060aca0605SAndrew Oates                 }
17070aca0605SAndrew Oates                 if (e2 & DESC_B_MASK) {
17080aca0605SAndrew Oates                     raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC());
17090aca0605SAndrew Oates                 }
17100aca0605SAndrew Oates             }
17110aca0605SAndrew Oates #endif
171220054ef0SBlue Swirl             if (!(e2 & DESC_P_MASK)) {
1713100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC());
171420054ef0SBlue Swirl             }
1715eaa728eeSbellard             limit = get_seg_limit(e1, e2);
17160aca0605SAndrew Oates             if (new_eip > limit &&
17170aca0605SAndrew Oates                 (!(env->hflags & HF_LMA_MASK) || !(e2 & DESC_L_MASK))) {
1718100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC());
171920054ef0SBlue Swirl             }
1720eaa728eeSbellard             cpu_x86_load_seg_cache(env, R_CS, (gate_cs & 0xfffc) | cpl,
1721eaa728eeSbellard                                    get_seg_base(e1, e2), limit, e2);
1722a78d0eabSliguang             env->eip = new_eip;
1723eaa728eeSbellard             break;
1724eaa728eeSbellard         default:
1725100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
1726eaa728eeSbellard             break;
1727eaa728eeSbellard         }
1728eaa728eeSbellard     }
1729eaa728eeSbellard }
1730eaa728eeSbellard 
1731eaa728eeSbellard /* real mode call */
17322999a0b2SBlue Swirl void helper_lcall_real(CPUX86State *env, int new_cs, target_ulong new_eip1,
1733eaa728eeSbellard                        int shift, int next_eip)
1734eaa728eeSbellard {
1735eaa728eeSbellard     int new_eip;
1736eaa728eeSbellard     uint32_t esp, esp_mask;
1737eaa728eeSbellard     target_ulong ssp;
1738eaa728eeSbellard 
1739eaa728eeSbellard     new_eip = new_eip1;
174008b3ded6Sliguang     esp = env->regs[R_ESP];
1741eaa728eeSbellard     esp_mask = get_sp_mask(env->segs[R_SS].flags);
1742eaa728eeSbellard     ssp = env->segs[R_SS].base;
1743eaa728eeSbellard     if (shift) {
1744100ec099SPavel Dovgalyuk         PUSHL_RA(ssp, esp, esp_mask, env->segs[R_CS].selector, GETPC());
1745100ec099SPavel Dovgalyuk         PUSHL_RA(ssp, esp, esp_mask, next_eip, GETPC());
1746eaa728eeSbellard     } else {
1747100ec099SPavel Dovgalyuk         PUSHW_RA(ssp, esp, esp_mask, env->segs[R_CS].selector, GETPC());
1748100ec099SPavel Dovgalyuk         PUSHW_RA(ssp, esp, esp_mask, next_eip, GETPC());
1749eaa728eeSbellard     }
1750eaa728eeSbellard 
1751eaa728eeSbellard     SET_ESP(esp, esp_mask);
1752eaa728eeSbellard     env->eip = new_eip;
1753eaa728eeSbellard     env->segs[R_CS].selector = new_cs;
1754eaa728eeSbellard     env->segs[R_CS].base = (new_cs << 4);
1755eaa728eeSbellard }
1756eaa728eeSbellard 
1757eaa728eeSbellard /* protected mode call */
17582999a0b2SBlue Swirl void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip,
1759100ec099SPavel Dovgalyuk                             int shift, target_ulong next_eip)
1760eaa728eeSbellard {
1761eaa728eeSbellard     int new_stack, i;
17620aca0605SAndrew Oates     uint32_t e1, e2, cpl, dpl, rpl, selector, param_count;
17630aca0605SAndrew Oates     uint32_t ss = 0, ss_e1 = 0, ss_e2 = 0, type, ss_dpl, sp_mask;
1764eaa728eeSbellard     uint32_t val, limit, old_sp_mask;
17650aca0605SAndrew Oates     target_ulong ssp, old_ssp, offset, sp;
1766eaa728eeSbellard 
17670aca0605SAndrew Oates     LOG_PCALL("lcall %04x:" TARGET_FMT_lx " s=%d\n", new_cs, new_eip, shift);
17686aa9e42fSRichard Henderson     LOG_PCALL_STATE(env_cpu(env));
176920054ef0SBlue Swirl     if ((new_cs & 0xfffc) == 0) {
1770100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC());
177120054ef0SBlue Swirl     }
1772100ec099SPavel Dovgalyuk     if (load_segment_ra(env, &e1, &e2, new_cs, GETPC()) != 0) {
1773100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
177420054ef0SBlue Swirl     }
1775eaa728eeSbellard     cpl = env->hflags & HF_CPL_MASK;
1776d12d51d5Saliguori     LOG_PCALL("desc=%08x:%08x\n", e1, e2);
1777eaa728eeSbellard     if (e2 & DESC_S_MASK) {
177820054ef0SBlue Swirl         if (!(e2 & DESC_CS_MASK)) {
1779100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
178020054ef0SBlue Swirl         }
1781eaa728eeSbellard         dpl = (e2 >> DESC_DPL_SHIFT) & 3;
1782eaa728eeSbellard         if (e2 & DESC_C_MASK) {
1783eaa728eeSbellard             /* conforming code segment */
178420054ef0SBlue Swirl             if (dpl > cpl) {
1785100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
178620054ef0SBlue Swirl             }
1787eaa728eeSbellard         } else {
1788eaa728eeSbellard             /* non conforming code segment */
1789eaa728eeSbellard             rpl = new_cs & 3;
179020054ef0SBlue Swirl             if (rpl > cpl) {
1791100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
1792eaa728eeSbellard             }
179320054ef0SBlue Swirl             if (dpl != cpl) {
1794100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
179520054ef0SBlue Swirl             }
179620054ef0SBlue Swirl         }
179720054ef0SBlue Swirl         if (!(e2 & DESC_P_MASK)) {
1798100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, GETPC());
179920054ef0SBlue Swirl         }
1800eaa728eeSbellard 
1801eaa728eeSbellard #ifdef TARGET_X86_64
1802eaa728eeSbellard         /* XXX: check 16/32 bit cases in long mode */
1803eaa728eeSbellard         if (shift == 2) {
1804eaa728eeSbellard             target_ulong rsp;
180520054ef0SBlue Swirl 
1806eaa728eeSbellard             /* 64 bit case */
180708b3ded6Sliguang             rsp = env->regs[R_ESP];
1808100ec099SPavel Dovgalyuk             PUSHQ_RA(rsp, env->segs[R_CS].selector, GETPC());
1809100ec099SPavel Dovgalyuk             PUSHQ_RA(rsp, next_eip, GETPC());
1810eaa728eeSbellard             /* from this point, not restartable */
181108b3ded6Sliguang             env->regs[R_ESP] = rsp;
1812eaa728eeSbellard             cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
1813eaa728eeSbellard                                    get_seg_base(e1, e2),
1814eaa728eeSbellard                                    get_seg_limit(e1, e2), e2);
1815a78d0eabSliguang             env->eip = new_eip;
1816eaa728eeSbellard         } else
1817eaa728eeSbellard #endif
1818eaa728eeSbellard         {
181908b3ded6Sliguang             sp = env->regs[R_ESP];
1820eaa728eeSbellard             sp_mask = get_sp_mask(env->segs[R_SS].flags);
1821eaa728eeSbellard             ssp = env->segs[R_SS].base;
1822eaa728eeSbellard             if (shift) {
1823100ec099SPavel Dovgalyuk                 PUSHL_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC());
1824100ec099SPavel Dovgalyuk                 PUSHL_RA(ssp, sp, sp_mask, next_eip, GETPC());
1825eaa728eeSbellard             } else {
1826100ec099SPavel Dovgalyuk                 PUSHW_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC());
1827100ec099SPavel Dovgalyuk                 PUSHW_RA(ssp, sp, sp_mask, next_eip, GETPC());
1828eaa728eeSbellard             }
1829eaa728eeSbellard 
1830eaa728eeSbellard             limit = get_seg_limit(e1, e2);
183120054ef0SBlue Swirl             if (new_eip > limit) {
1832100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
183320054ef0SBlue Swirl             }
1834eaa728eeSbellard             /* from this point, not restartable */
1835eaa728eeSbellard             SET_ESP(sp, sp_mask);
1836eaa728eeSbellard             cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
1837eaa728eeSbellard                                    get_seg_base(e1, e2), limit, e2);
1838a78d0eabSliguang             env->eip = new_eip;
1839eaa728eeSbellard         }
1840eaa728eeSbellard     } else {
1841eaa728eeSbellard         /* check gate type */
1842eaa728eeSbellard         type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
1843eaa728eeSbellard         dpl = (e2 >> DESC_DPL_SHIFT) & 3;
1844eaa728eeSbellard         rpl = new_cs & 3;
18450aca0605SAndrew Oates 
18460aca0605SAndrew Oates #ifdef TARGET_X86_64
18470aca0605SAndrew Oates         if (env->efer & MSR_EFER_LMA) {
18480aca0605SAndrew Oates             if (type != 12) {
18490aca0605SAndrew Oates                 raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
18500aca0605SAndrew Oates             }
18510aca0605SAndrew Oates         }
18520aca0605SAndrew Oates #endif
18530aca0605SAndrew Oates 
1854eaa728eeSbellard         switch (type) {
1855eaa728eeSbellard         case 1: /* available 286 TSS */
1856eaa728eeSbellard         case 9: /* available 386 TSS */
1857eaa728eeSbellard         case 5: /* task gate */
185820054ef0SBlue Swirl             if (dpl < cpl || dpl < rpl) {
1859100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
186020054ef0SBlue Swirl             }
1861100ec099SPavel Dovgalyuk             switch_tss_ra(env, new_cs, e1, e2, SWITCH_TSS_CALL, next_eip, GETPC());
1862eaa728eeSbellard             return;
1863eaa728eeSbellard         case 4: /* 286 call gate */
1864eaa728eeSbellard         case 12: /* 386 call gate */
1865eaa728eeSbellard             break;
1866eaa728eeSbellard         default:
1867100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
1868eaa728eeSbellard             break;
1869eaa728eeSbellard         }
1870eaa728eeSbellard         shift = type >> 3;
1871eaa728eeSbellard 
187220054ef0SBlue Swirl         if (dpl < cpl || dpl < rpl) {
1873100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC());
187420054ef0SBlue Swirl         }
1875eaa728eeSbellard         /* check valid bit */
187620054ef0SBlue Swirl         if (!(e2 & DESC_P_MASK)) {
1877100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0B_NOSEG,  new_cs & 0xfffc, GETPC());
187820054ef0SBlue Swirl         }
1879eaa728eeSbellard         selector = e1 >> 16;
1880eaa728eeSbellard         param_count = e2 & 0x1f;
18810aca0605SAndrew Oates         offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff);
18820aca0605SAndrew Oates #ifdef TARGET_X86_64
18830aca0605SAndrew Oates         if (env->efer & MSR_EFER_LMA) {
18840aca0605SAndrew Oates             /* load the upper 8 bytes of the 64-bit call gate */
18850aca0605SAndrew Oates             if (load_segment_ra(env, &e1, &e2, new_cs + 8, GETPC())) {
18860aca0605SAndrew Oates                 raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc,
18870aca0605SAndrew Oates                                        GETPC());
18880aca0605SAndrew Oates             }
18890aca0605SAndrew Oates             type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
18900aca0605SAndrew Oates             if (type != 0) {
18910aca0605SAndrew Oates                 raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc,
18920aca0605SAndrew Oates                                        GETPC());
18930aca0605SAndrew Oates             }
18940aca0605SAndrew Oates             offset |= ((target_ulong)e1) << 32;
18950aca0605SAndrew Oates         }
18960aca0605SAndrew Oates #endif
189720054ef0SBlue Swirl         if ((selector & 0xfffc) == 0) {
1898100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC());
189920054ef0SBlue Swirl         }
1900eaa728eeSbellard 
1901100ec099SPavel Dovgalyuk         if (load_segment_ra(env, &e1, &e2, selector, GETPC()) != 0) {
1902100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
190320054ef0SBlue Swirl         }
190420054ef0SBlue Swirl         if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK))) {
1905100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
190620054ef0SBlue Swirl         }
1907eaa728eeSbellard         dpl = (e2 >> DESC_DPL_SHIFT) & 3;
190820054ef0SBlue Swirl         if (dpl > cpl) {
1909100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
191020054ef0SBlue Swirl         }
19110aca0605SAndrew Oates #ifdef TARGET_X86_64
19120aca0605SAndrew Oates         if (env->efer & MSR_EFER_LMA) {
19130aca0605SAndrew Oates             if (!(e2 & DESC_L_MASK)) {
19140aca0605SAndrew Oates                 raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
19150aca0605SAndrew Oates             }
19160aca0605SAndrew Oates             if (e2 & DESC_B_MASK) {
19170aca0605SAndrew Oates                 raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC());
19180aca0605SAndrew Oates             }
19190aca0605SAndrew Oates             shift++;
19200aca0605SAndrew Oates         }
19210aca0605SAndrew Oates #endif
192220054ef0SBlue Swirl         if (!(e2 & DESC_P_MASK)) {
1923100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0B_NOSEG, selector & 0xfffc, GETPC());
192420054ef0SBlue Swirl         }
1925eaa728eeSbellard 
1926eaa728eeSbellard         if (!(e2 & DESC_C_MASK) && dpl < cpl) {
1927eaa728eeSbellard             /* to inner privilege */
19280aca0605SAndrew Oates #ifdef TARGET_X86_64
19290aca0605SAndrew Oates             if (shift == 2) {
19300aca0605SAndrew Oates                 sp = get_rsp_from_tss(env, dpl);
19310aca0605SAndrew Oates                 ss = dpl;  /* SS = NULL selector with RPL = new CPL */
19320aca0605SAndrew Oates                 new_stack = 1;
19330aca0605SAndrew Oates                 sp_mask = 0;
19340aca0605SAndrew Oates                 ssp = 0;  /* SS base is always zero in IA-32e mode */
19350aca0605SAndrew Oates                 LOG_PCALL("new ss:rsp=%04x:%016llx env->regs[R_ESP]="
19360aca0605SAndrew Oates                           TARGET_FMT_lx "\n", ss, sp, env->regs[R_ESP]);
19370aca0605SAndrew Oates             } else
19380aca0605SAndrew Oates #endif
19390aca0605SAndrew Oates             {
19400aca0605SAndrew Oates                 uint32_t sp32;
19410aca0605SAndrew Oates                 get_ss_esp_from_tss(env, &ss, &sp32, dpl, GETPC());
194290a2541bSliguang                 LOG_PCALL("new ss:esp=%04x:%08x param_count=%d env->regs[R_ESP]="
19430aca0605SAndrew Oates                           TARGET_FMT_lx "\n", ss, sp32, param_count,
194490a2541bSliguang                           env->regs[R_ESP]);
19450aca0605SAndrew Oates                 sp = sp32;
194620054ef0SBlue Swirl                 if ((ss & 0xfffc) == 0) {
1947100ec099SPavel Dovgalyuk                     raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC());
194820054ef0SBlue Swirl                 }
194920054ef0SBlue Swirl                 if ((ss & 3) != dpl) {
1950100ec099SPavel Dovgalyuk                     raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC());
195120054ef0SBlue Swirl                 }
1952100ec099SPavel Dovgalyuk                 if (load_segment_ra(env, &ss_e1, &ss_e2, ss, GETPC()) != 0) {
1953100ec099SPavel Dovgalyuk                     raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC());
195420054ef0SBlue Swirl                 }
1955eaa728eeSbellard                 ss_dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3;
195620054ef0SBlue Swirl                 if (ss_dpl != dpl) {
1957100ec099SPavel Dovgalyuk                     raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC());
195820054ef0SBlue Swirl                 }
1959eaa728eeSbellard                 if (!(ss_e2 & DESC_S_MASK) ||
1960eaa728eeSbellard                     (ss_e2 & DESC_CS_MASK) ||
196120054ef0SBlue Swirl                     !(ss_e2 & DESC_W_MASK)) {
1962100ec099SPavel Dovgalyuk                     raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC());
196320054ef0SBlue Swirl                 }
196420054ef0SBlue Swirl                 if (!(ss_e2 & DESC_P_MASK)) {
1965100ec099SPavel Dovgalyuk                     raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC());
196620054ef0SBlue Swirl                 }
1967eaa728eeSbellard 
19680aca0605SAndrew Oates                 sp_mask = get_sp_mask(ss_e2);
19690aca0605SAndrew Oates                 ssp = get_seg_base(ss_e1, ss_e2);
19700aca0605SAndrew Oates             }
19710aca0605SAndrew Oates 
197220054ef0SBlue Swirl             /* push_size = ((param_count * 2) + 8) << shift; */
1973eaa728eeSbellard 
1974eaa728eeSbellard             old_sp_mask = get_sp_mask(env->segs[R_SS].flags);
1975eaa728eeSbellard             old_ssp = env->segs[R_SS].base;
19760aca0605SAndrew Oates #ifdef TARGET_X86_64
19770aca0605SAndrew Oates             if (shift == 2) {
19780aca0605SAndrew Oates                 /* XXX: verify if new stack address is canonical */
19790aca0605SAndrew Oates                 PUSHQ_RA(sp, env->segs[R_SS].selector, GETPC());
19800aca0605SAndrew Oates                 PUSHQ_RA(sp, env->regs[R_ESP], GETPC());
19810aca0605SAndrew Oates                 /* parameters aren't supported for 64-bit call gates */
19820aca0605SAndrew Oates             } else
19830aca0605SAndrew Oates #endif
19840aca0605SAndrew Oates             if (shift == 1) {
1985100ec099SPavel Dovgalyuk                 PUSHL_RA(ssp, sp, sp_mask, env->segs[R_SS].selector, GETPC());
1986100ec099SPavel Dovgalyuk                 PUSHL_RA(ssp, sp, sp_mask, env->regs[R_ESP], GETPC());
1987eaa728eeSbellard                 for (i = param_count - 1; i >= 0; i--) {
1988100ec099SPavel Dovgalyuk                     val = cpu_ldl_kernel_ra(env, old_ssp +
198990a2541bSliguang                                             ((env->regs[R_ESP] + i * 4) &
1990100ec099SPavel Dovgalyuk                                              old_sp_mask), GETPC());
1991100ec099SPavel Dovgalyuk                     PUSHL_RA(ssp, sp, sp_mask, val, GETPC());
1992eaa728eeSbellard                 }
1993eaa728eeSbellard             } else {
1994100ec099SPavel Dovgalyuk                 PUSHW_RA(ssp, sp, sp_mask, env->segs[R_SS].selector, GETPC());
1995100ec099SPavel Dovgalyuk                 PUSHW_RA(ssp, sp, sp_mask, env->regs[R_ESP], GETPC());
1996eaa728eeSbellard                 for (i = param_count - 1; i >= 0; i--) {
1997100ec099SPavel Dovgalyuk                     val = cpu_lduw_kernel_ra(env, old_ssp +
199890a2541bSliguang                                              ((env->regs[R_ESP] + i * 2) &
1999100ec099SPavel Dovgalyuk                                               old_sp_mask), GETPC());
2000100ec099SPavel Dovgalyuk                     PUSHW_RA(ssp, sp, sp_mask, val, GETPC());
2001eaa728eeSbellard                 }
2002eaa728eeSbellard             }
2003eaa728eeSbellard             new_stack = 1;
2004eaa728eeSbellard         } else {
2005eaa728eeSbellard             /* to same privilege */
200608b3ded6Sliguang             sp = env->regs[R_ESP];
2007eaa728eeSbellard             sp_mask = get_sp_mask(env->segs[R_SS].flags);
2008eaa728eeSbellard             ssp = env->segs[R_SS].base;
200920054ef0SBlue Swirl             /* push_size = (4 << shift); */
2010eaa728eeSbellard             new_stack = 0;
2011eaa728eeSbellard         }
2012eaa728eeSbellard 
20130aca0605SAndrew Oates #ifdef TARGET_X86_64
20140aca0605SAndrew Oates         if (shift == 2) {
20150aca0605SAndrew Oates             PUSHQ_RA(sp, env->segs[R_CS].selector, GETPC());
20160aca0605SAndrew Oates             PUSHQ_RA(sp, next_eip, GETPC());
20170aca0605SAndrew Oates         } else
20180aca0605SAndrew Oates #endif
20190aca0605SAndrew Oates         if (shift == 1) {
2020100ec099SPavel Dovgalyuk             PUSHL_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC());
2021100ec099SPavel Dovgalyuk             PUSHL_RA(ssp, sp, sp_mask, next_eip, GETPC());
2022eaa728eeSbellard         } else {
2023100ec099SPavel Dovgalyuk             PUSHW_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC());
2024100ec099SPavel Dovgalyuk             PUSHW_RA(ssp, sp, sp_mask, next_eip, GETPC());
2025eaa728eeSbellard         }
2026eaa728eeSbellard 
2027eaa728eeSbellard         /* from this point, not restartable */
2028eaa728eeSbellard 
2029eaa728eeSbellard         if (new_stack) {
20300aca0605SAndrew Oates #ifdef TARGET_X86_64
20310aca0605SAndrew Oates             if (shift == 2) {
20320aca0605SAndrew Oates                 cpu_x86_load_seg_cache(env, R_SS, ss, 0, 0, 0);
20330aca0605SAndrew Oates             } else
20340aca0605SAndrew Oates #endif
20350aca0605SAndrew Oates             {
2036eaa728eeSbellard                 ss = (ss & ~3) | dpl;
2037eaa728eeSbellard                 cpu_x86_load_seg_cache(env, R_SS, ss,
2038eaa728eeSbellard                                        ssp,
2039eaa728eeSbellard                                        get_seg_limit(ss_e1, ss_e2),
2040eaa728eeSbellard                                        ss_e2);
2041eaa728eeSbellard             }
20420aca0605SAndrew Oates         }
2043eaa728eeSbellard 
2044eaa728eeSbellard         selector = (selector & ~3) | dpl;
2045eaa728eeSbellard         cpu_x86_load_seg_cache(env, R_CS, selector,
2046eaa728eeSbellard                        get_seg_base(e1, e2),
2047eaa728eeSbellard                        get_seg_limit(e1, e2),
2048eaa728eeSbellard                        e2);
2049eaa728eeSbellard         SET_ESP(sp, sp_mask);
2050a78d0eabSliguang         env->eip = offset;
2051eaa728eeSbellard     }
2052eaa728eeSbellard }
2053eaa728eeSbellard 
2054eaa728eeSbellard /* real and vm86 mode iret */
20552999a0b2SBlue Swirl void helper_iret_real(CPUX86State *env, int shift)
2056eaa728eeSbellard {
2057eaa728eeSbellard     uint32_t sp, new_cs, new_eip, new_eflags, sp_mask;
2058eaa728eeSbellard     target_ulong ssp;
2059eaa728eeSbellard     int eflags_mask;
2060eaa728eeSbellard 
2061eaa728eeSbellard     sp_mask = 0xffff; /* XXXX: use SS segment size? */
206208b3ded6Sliguang     sp = env->regs[R_ESP];
2063eaa728eeSbellard     ssp = env->segs[R_SS].base;
2064eaa728eeSbellard     if (shift == 1) {
2065eaa728eeSbellard         /* 32 bits */
2066100ec099SPavel Dovgalyuk         POPL_RA(ssp, sp, sp_mask, new_eip, GETPC());
2067100ec099SPavel Dovgalyuk         POPL_RA(ssp, sp, sp_mask, new_cs, GETPC());
2068eaa728eeSbellard         new_cs &= 0xffff;
2069100ec099SPavel Dovgalyuk         POPL_RA(ssp, sp, sp_mask, new_eflags, GETPC());
2070eaa728eeSbellard     } else {
2071eaa728eeSbellard         /* 16 bits */
2072100ec099SPavel Dovgalyuk         POPW_RA(ssp, sp, sp_mask, new_eip, GETPC());
2073100ec099SPavel Dovgalyuk         POPW_RA(ssp, sp, sp_mask, new_cs, GETPC());
2074100ec099SPavel Dovgalyuk         POPW_RA(ssp, sp, sp_mask, new_eflags, GETPC());
2075eaa728eeSbellard     }
207608b3ded6Sliguang     env->regs[R_ESP] = (env->regs[R_ESP] & ~sp_mask) | (sp & sp_mask);
2077bdadc0b5Smalc     env->segs[R_CS].selector = new_cs;
2078bdadc0b5Smalc     env->segs[R_CS].base = (new_cs << 4);
2079eaa728eeSbellard     env->eip = new_eip;
208020054ef0SBlue Swirl     if (env->eflags & VM_MASK) {
208120054ef0SBlue Swirl         eflags_mask = TF_MASK | AC_MASK | ID_MASK | IF_MASK | RF_MASK |
208220054ef0SBlue Swirl             NT_MASK;
208320054ef0SBlue Swirl     } else {
208420054ef0SBlue Swirl         eflags_mask = TF_MASK | AC_MASK | ID_MASK | IF_MASK | IOPL_MASK |
208520054ef0SBlue Swirl             RF_MASK | NT_MASK;
208620054ef0SBlue Swirl     }
208720054ef0SBlue Swirl     if (shift == 0) {
2088eaa728eeSbellard         eflags_mask &= 0xffff;
208920054ef0SBlue Swirl     }
2090997ff0d9SBlue Swirl     cpu_load_eflags(env, new_eflags, eflags_mask);
2091db620f46Sbellard     env->hflags2 &= ~HF2_NMI_MASK;
2092eaa728eeSbellard }
2093eaa728eeSbellard 
20942999a0b2SBlue Swirl static inline void validate_seg(CPUX86State *env, int seg_reg, int cpl)
2095eaa728eeSbellard {
2096eaa728eeSbellard     int dpl;
2097eaa728eeSbellard     uint32_t e2;
2098eaa728eeSbellard 
2099eaa728eeSbellard     /* XXX: on x86_64, we do not want to nullify FS and GS because
2100eaa728eeSbellard        they may still contain a valid base. I would be interested to
2101eaa728eeSbellard        know how a real x86_64 CPU behaves */
2102eaa728eeSbellard     if ((seg_reg == R_FS || seg_reg == R_GS) &&
210320054ef0SBlue Swirl         (env->segs[seg_reg].selector & 0xfffc) == 0) {
2104eaa728eeSbellard         return;
210520054ef0SBlue Swirl     }
2106eaa728eeSbellard 
2107eaa728eeSbellard     e2 = env->segs[seg_reg].flags;
2108eaa728eeSbellard     dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2109eaa728eeSbellard     if (!(e2 & DESC_CS_MASK) || !(e2 & DESC_C_MASK)) {
2110eaa728eeSbellard         /* data or non conforming code segment */
2111eaa728eeSbellard         if (dpl < cpl) {
2112c2ba0515SBin Meng             cpu_x86_load_seg_cache(env, seg_reg, 0,
2113c2ba0515SBin Meng                                    env->segs[seg_reg].base,
2114c2ba0515SBin Meng                                    env->segs[seg_reg].limit,
2115c2ba0515SBin Meng                                    env->segs[seg_reg].flags & ~DESC_P_MASK);
2116eaa728eeSbellard         }
2117eaa728eeSbellard     }
2118eaa728eeSbellard }
2119eaa728eeSbellard 
2120eaa728eeSbellard /* protected mode iret */
21212999a0b2SBlue Swirl static inline void helper_ret_protected(CPUX86State *env, int shift,
2122100ec099SPavel Dovgalyuk                                         int is_iret, int addend,
2123100ec099SPavel Dovgalyuk                                         uintptr_t retaddr)
2124eaa728eeSbellard {
2125eaa728eeSbellard     uint32_t new_cs, new_eflags, new_ss;
2126eaa728eeSbellard     uint32_t new_es, new_ds, new_fs, new_gs;
2127eaa728eeSbellard     uint32_t e1, e2, ss_e1, ss_e2;
2128eaa728eeSbellard     int cpl, dpl, rpl, eflags_mask, iopl;
2129eaa728eeSbellard     target_ulong ssp, sp, new_eip, new_esp, sp_mask;
2130eaa728eeSbellard 
2131eaa728eeSbellard #ifdef TARGET_X86_64
213220054ef0SBlue Swirl     if (shift == 2) {
2133eaa728eeSbellard         sp_mask = -1;
213420054ef0SBlue Swirl     } else
2135eaa728eeSbellard #endif
213620054ef0SBlue Swirl     {
2137eaa728eeSbellard         sp_mask = get_sp_mask(env->segs[R_SS].flags);
213820054ef0SBlue Swirl     }
213908b3ded6Sliguang     sp = env->regs[R_ESP];
2140eaa728eeSbellard     ssp = env->segs[R_SS].base;
2141eaa728eeSbellard     new_eflags = 0; /* avoid warning */
2142eaa728eeSbellard #ifdef TARGET_X86_64
2143eaa728eeSbellard     if (shift == 2) {
2144100ec099SPavel Dovgalyuk         POPQ_RA(sp, new_eip, retaddr);
2145100ec099SPavel Dovgalyuk         POPQ_RA(sp, new_cs, retaddr);
2146eaa728eeSbellard         new_cs &= 0xffff;
2147eaa728eeSbellard         if (is_iret) {
2148100ec099SPavel Dovgalyuk             POPQ_RA(sp, new_eflags, retaddr);
2149eaa728eeSbellard         }
2150eaa728eeSbellard     } else
2151eaa728eeSbellard #endif
215220054ef0SBlue Swirl     {
2153eaa728eeSbellard         if (shift == 1) {
2154eaa728eeSbellard             /* 32 bits */
2155100ec099SPavel Dovgalyuk             POPL_RA(ssp, sp, sp_mask, new_eip, retaddr);
2156100ec099SPavel Dovgalyuk             POPL_RA(ssp, sp, sp_mask, new_cs, retaddr);
2157eaa728eeSbellard             new_cs &= 0xffff;
2158eaa728eeSbellard             if (is_iret) {
2159100ec099SPavel Dovgalyuk                 POPL_RA(ssp, sp, sp_mask, new_eflags, retaddr);
216020054ef0SBlue Swirl                 if (new_eflags & VM_MASK) {
2161eaa728eeSbellard                     goto return_to_vm86;
2162eaa728eeSbellard                 }
216320054ef0SBlue Swirl             }
2164eaa728eeSbellard         } else {
2165eaa728eeSbellard             /* 16 bits */
2166100ec099SPavel Dovgalyuk             POPW_RA(ssp, sp, sp_mask, new_eip, retaddr);
2167100ec099SPavel Dovgalyuk             POPW_RA(ssp, sp, sp_mask, new_cs, retaddr);
216820054ef0SBlue Swirl             if (is_iret) {
2169100ec099SPavel Dovgalyuk                 POPW_RA(ssp, sp, sp_mask, new_eflags, retaddr);
2170eaa728eeSbellard             }
217120054ef0SBlue Swirl         }
217220054ef0SBlue Swirl     }
2173d12d51d5Saliguori     LOG_PCALL("lret new %04x:" TARGET_FMT_lx " s=%d addend=0x%x\n",
2174eaa728eeSbellard               new_cs, new_eip, shift, addend);
21756aa9e42fSRichard Henderson     LOG_PCALL_STATE(env_cpu(env));
217620054ef0SBlue Swirl     if ((new_cs & 0xfffc) == 0) {
2177100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr);
2178eaa728eeSbellard     }
2179100ec099SPavel Dovgalyuk     if (load_segment_ra(env, &e1, &e2, new_cs, retaddr) != 0) {
2180100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr);
218120054ef0SBlue Swirl     }
218220054ef0SBlue Swirl     if (!(e2 & DESC_S_MASK) ||
218320054ef0SBlue Swirl         !(e2 & DESC_CS_MASK)) {
2184100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr);
218520054ef0SBlue Swirl     }
218620054ef0SBlue Swirl     cpl = env->hflags & HF_CPL_MASK;
218720054ef0SBlue Swirl     rpl = new_cs & 3;
218820054ef0SBlue Swirl     if (rpl < cpl) {
2189100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr);
219020054ef0SBlue Swirl     }
219120054ef0SBlue Swirl     dpl = (e2 >> DESC_DPL_SHIFT) & 3;
219220054ef0SBlue Swirl     if (e2 & DESC_C_MASK) {
219320054ef0SBlue Swirl         if (dpl > rpl) {
2194100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr);
219520054ef0SBlue Swirl         }
219620054ef0SBlue Swirl     } else {
219720054ef0SBlue Swirl         if (dpl != rpl) {
2198100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr);
219920054ef0SBlue Swirl         }
220020054ef0SBlue Swirl     }
220120054ef0SBlue Swirl     if (!(e2 & DESC_P_MASK)) {
2202100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, retaddr);
220320054ef0SBlue Swirl     }
2204eaa728eeSbellard 
2205eaa728eeSbellard     sp += addend;
2206eaa728eeSbellard     if (rpl == cpl && (!(env->hflags & HF_CS64_MASK) ||
2207eaa728eeSbellard                        ((env->hflags & HF_CS64_MASK) && !is_iret))) {
22081235fc06Sths         /* return to same privilege level */
2209eaa728eeSbellard         cpu_x86_load_seg_cache(env, R_CS, new_cs,
2210eaa728eeSbellard                        get_seg_base(e1, e2),
2211eaa728eeSbellard                        get_seg_limit(e1, e2),
2212eaa728eeSbellard                        e2);
2213eaa728eeSbellard     } else {
2214eaa728eeSbellard         /* return to different privilege level */
2215eaa728eeSbellard #ifdef TARGET_X86_64
2216eaa728eeSbellard         if (shift == 2) {
2217100ec099SPavel Dovgalyuk             POPQ_RA(sp, new_esp, retaddr);
2218100ec099SPavel Dovgalyuk             POPQ_RA(sp, new_ss, retaddr);
2219eaa728eeSbellard             new_ss &= 0xffff;
2220eaa728eeSbellard         } else
2221eaa728eeSbellard #endif
222220054ef0SBlue Swirl         {
2223eaa728eeSbellard             if (shift == 1) {
2224eaa728eeSbellard                 /* 32 bits */
2225100ec099SPavel Dovgalyuk                 POPL_RA(ssp, sp, sp_mask, new_esp, retaddr);
2226100ec099SPavel Dovgalyuk                 POPL_RA(ssp, sp, sp_mask, new_ss, retaddr);
2227eaa728eeSbellard                 new_ss &= 0xffff;
2228eaa728eeSbellard             } else {
2229eaa728eeSbellard                 /* 16 bits */
2230100ec099SPavel Dovgalyuk                 POPW_RA(ssp, sp, sp_mask, new_esp, retaddr);
2231100ec099SPavel Dovgalyuk                 POPW_RA(ssp, sp, sp_mask, new_ss, retaddr);
2232eaa728eeSbellard             }
223320054ef0SBlue Swirl         }
2234d12d51d5Saliguori         LOG_PCALL("new ss:esp=%04x:" TARGET_FMT_lx "\n",
2235eaa728eeSbellard                   new_ss, new_esp);
2236eaa728eeSbellard         if ((new_ss & 0xfffc) == 0) {
2237eaa728eeSbellard #ifdef TARGET_X86_64
2238eaa728eeSbellard             /* NULL ss is allowed in long mode if cpl != 3 */
2239eaa728eeSbellard             /* XXX: test CS64? */
2240eaa728eeSbellard             if ((env->hflags & HF_LMA_MASK) && rpl != 3) {
2241eaa728eeSbellard                 cpu_x86_load_seg_cache(env, R_SS, new_ss,
2242eaa728eeSbellard                                        0, 0xffffffff,
2243eaa728eeSbellard                                        DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2244eaa728eeSbellard                                        DESC_S_MASK | (rpl << DESC_DPL_SHIFT) |
2245eaa728eeSbellard                                        DESC_W_MASK | DESC_A_MASK);
2246eaa728eeSbellard                 ss_e2 = DESC_B_MASK; /* XXX: should not be needed? */
2247eaa728eeSbellard             } else
2248eaa728eeSbellard #endif
2249eaa728eeSbellard             {
2250100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, 0, retaddr);
2251eaa728eeSbellard             }
2252eaa728eeSbellard         } else {
225320054ef0SBlue Swirl             if ((new_ss & 3) != rpl) {
2254100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, new_ss & 0xfffc, retaddr);
225520054ef0SBlue Swirl             }
2256100ec099SPavel Dovgalyuk             if (load_segment_ra(env, &ss_e1, &ss_e2, new_ss, retaddr) != 0) {
2257100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, new_ss & 0xfffc, retaddr);
225820054ef0SBlue Swirl             }
2259eaa728eeSbellard             if (!(ss_e2 & DESC_S_MASK) ||
2260eaa728eeSbellard                 (ss_e2 & DESC_CS_MASK) ||
226120054ef0SBlue Swirl                 !(ss_e2 & DESC_W_MASK)) {
2262100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, new_ss & 0xfffc, retaddr);
226320054ef0SBlue Swirl             }
2264eaa728eeSbellard             dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3;
226520054ef0SBlue Swirl             if (dpl != rpl) {
2266100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0D_GPF, new_ss & 0xfffc, retaddr);
226720054ef0SBlue Swirl             }
226820054ef0SBlue Swirl             if (!(ss_e2 & DESC_P_MASK)) {
2269100ec099SPavel Dovgalyuk                 raise_exception_err_ra(env, EXCP0B_NOSEG, new_ss & 0xfffc, retaddr);
227020054ef0SBlue Swirl             }
2271eaa728eeSbellard             cpu_x86_load_seg_cache(env, R_SS, new_ss,
2272eaa728eeSbellard                                    get_seg_base(ss_e1, ss_e2),
2273eaa728eeSbellard                                    get_seg_limit(ss_e1, ss_e2),
2274eaa728eeSbellard                                    ss_e2);
2275eaa728eeSbellard         }
2276eaa728eeSbellard 
2277eaa728eeSbellard         cpu_x86_load_seg_cache(env, R_CS, new_cs,
2278eaa728eeSbellard                        get_seg_base(e1, e2),
2279eaa728eeSbellard                        get_seg_limit(e1, e2),
2280eaa728eeSbellard                        e2);
2281eaa728eeSbellard         sp = new_esp;
2282eaa728eeSbellard #ifdef TARGET_X86_64
228320054ef0SBlue Swirl         if (env->hflags & HF_CS64_MASK) {
2284eaa728eeSbellard             sp_mask = -1;
228520054ef0SBlue Swirl         } else
2286eaa728eeSbellard #endif
228720054ef0SBlue Swirl         {
2288eaa728eeSbellard             sp_mask = get_sp_mask(ss_e2);
228920054ef0SBlue Swirl         }
2290eaa728eeSbellard 
2291eaa728eeSbellard         /* validate data segments */
22922999a0b2SBlue Swirl         validate_seg(env, R_ES, rpl);
22932999a0b2SBlue Swirl         validate_seg(env, R_DS, rpl);
22942999a0b2SBlue Swirl         validate_seg(env, R_FS, rpl);
22952999a0b2SBlue Swirl         validate_seg(env, R_GS, rpl);
2296eaa728eeSbellard 
2297eaa728eeSbellard         sp += addend;
2298eaa728eeSbellard     }
2299eaa728eeSbellard     SET_ESP(sp, sp_mask);
2300eaa728eeSbellard     env->eip = new_eip;
2301eaa728eeSbellard     if (is_iret) {
2302eaa728eeSbellard         /* NOTE: 'cpl' is the _old_ CPL */
2303eaa728eeSbellard         eflags_mask = TF_MASK | AC_MASK | ID_MASK | RF_MASK | NT_MASK;
230420054ef0SBlue Swirl         if (cpl == 0) {
2305eaa728eeSbellard             eflags_mask |= IOPL_MASK;
230620054ef0SBlue Swirl         }
2307eaa728eeSbellard         iopl = (env->eflags >> IOPL_SHIFT) & 3;
230820054ef0SBlue Swirl         if (cpl <= iopl) {
2309eaa728eeSbellard             eflags_mask |= IF_MASK;
231020054ef0SBlue Swirl         }
231120054ef0SBlue Swirl         if (shift == 0) {
2312eaa728eeSbellard             eflags_mask &= 0xffff;
231320054ef0SBlue Swirl         }
2314997ff0d9SBlue Swirl         cpu_load_eflags(env, new_eflags, eflags_mask);
2315eaa728eeSbellard     }
2316eaa728eeSbellard     return;
2317eaa728eeSbellard 
2318eaa728eeSbellard  return_to_vm86:
2319100ec099SPavel Dovgalyuk     POPL_RA(ssp, sp, sp_mask, new_esp, retaddr);
2320100ec099SPavel Dovgalyuk     POPL_RA(ssp, sp, sp_mask, new_ss, retaddr);
2321100ec099SPavel Dovgalyuk     POPL_RA(ssp, sp, sp_mask, new_es, retaddr);
2322100ec099SPavel Dovgalyuk     POPL_RA(ssp, sp, sp_mask, new_ds, retaddr);
2323100ec099SPavel Dovgalyuk     POPL_RA(ssp, sp, sp_mask, new_fs, retaddr);
2324100ec099SPavel Dovgalyuk     POPL_RA(ssp, sp, sp_mask, new_gs, retaddr);
2325eaa728eeSbellard 
2326eaa728eeSbellard     /* modify processor state */
2327997ff0d9SBlue Swirl     cpu_load_eflags(env, new_eflags, TF_MASK | AC_MASK | ID_MASK |
2328997ff0d9SBlue Swirl                     IF_MASK | IOPL_MASK | VM_MASK | NT_MASK | VIF_MASK |
2329997ff0d9SBlue Swirl                     VIP_MASK);
23302999a0b2SBlue Swirl     load_seg_vm(env, R_CS, new_cs & 0xffff);
23312999a0b2SBlue Swirl     load_seg_vm(env, R_SS, new_ss & 0xffff);
23322999a0b2SBlue Swirl     load_seg_vm(env, R_ES, new_es & 0xffff);
23332999a0b2SBlue Swirl     load_seg_vm(env, R_DS, new_ds & 0xffff);
23342999a0b2SBlue Swirl     load_seg_vm(env, R_FS, new_fs & 0xffff);
23352999a0b2SBlue Swirl     load_seg_vm(env, R_GS, new_gs & 0xffff);
2336eaa728eeSbellard 
2337eaa728eeSbellard     env->eip = new_eip & 0xffff;
233808b3ded6Sliguang     env->regs[R_ESP] = new_esp;
2339eaa728eeSbellard }
2340eaa728eeSbellard 
23412999a0b2SBlue Swirl void helper_iret_protected(CPUX86State *env, int shift, int next_eip)
2342eaa728eeSbellard {
2343eaa728eeSbellard     int tss_selector, type;
2344eaa728eeSbellard     uint32_t e1, e2;
2345eaa728eeSbellard 
2346eaa728eeSbellard     /* specific case for TSS */
2347eaa728eeSbellard     if (env->eflags & NT_MASK) {
2348eaa728eeSbellard #ifdef TARGET_X86_64
234920054ef0SBlue Swirl         if (env->hflags & HF_LMA_MASK) {
2350100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC());
235120054ef0SBlue Swirl         }
2352eaa728eeSbellard #endif
2353100ec099SPavel Dovgalyuk         tss_selector = cpu_lduw_kernel_ra(env, env->tr.base + 0, GETPC());
235420054ef0SBlue Swirl         if (tss_selector & 4) {
2355100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, GETPC());
235620054ef0SBlue Swirl         }
2357100ec099SPavel Dovgalyuk         if (load_segment_ra(env, &e1, &e2, tss_selector, GETPC()) != 0) {
2358100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, GETPC());
235920054ef0SBlue Swirl         }
2360eaa728eeSbellard         type = (e2 >> DESC_TYPE_SHIFT) & 0x17;
2361eaa728eeSbellard         /* NOTE: we check both segment and busy TSS */
236220054ef0SBlue Swirl         if (type != 3) {
2363100ec099SPavel Dovgalyuk             raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, GETPC());
236420054ef0SBlue Swirl         }
2365100ec099SPavel Dovgalyuk         switch_tss_ra(env, tss_selector, e1, e2, SWITCH_TSS_IRET, next_eip, GETPC());
2366eaa728eeSbellard     } else {
2367100ec099SPavel Dovgalyuk         helper_ret_protected(env, shift, 1, 0, GETPC());
2368eaa728eeSbellard     }
2369db620f46Sbellard     env->hflags2 &= ~HF2_NMI_MASK;
2370eaa728eeSbellard }
2371eaa728eeSbellard 
23722999a0b2SBlue Swirl void helper_lret_protected(CPUX86State *env, int shift, int addend)
2373eaa728eeSbellard {
2374100ec099SPavel Dovgalyuk     helper_ret_protected(env, shift, 0, addend, GETPC());
2375eaa728eeSbellard }
2376eaa728eeSbellard 
23772999a0b2SBlue Swirl void helper_sysenter(CPUX86State *env)
2378eaa728eeSbellard {
2379eaa728eeSbellard     if (env->sysenter_cs == 0) {
2380100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC());
2381eaa728eeSbellard     }
2382eaa728eeSbellard     env->eflags &= ~(VM_MASK | IF_MASK | RF_MASK);
23832436b61aSbalrog 
23842436b61aSbalrog #ifdef TARGET_X86_64
23852436b61aSbalrog     if (env->hflags & HF_LMA_MASK) {
23862436b61aSbalrog         cpu_x86_load_seg_cache(env, R_CS, env->sysenter_cs & 0xfffc,
23872436b61aSbalrog                                0, 0xffffffff,
23882436b61aSbalrog                                DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
23892436b61aSbalrog                                DESC_S_MASK |
239020054ef0SBlue Swirl                                DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK |
239120054ef0SBlue Swirl                                DESC_L_MASK);
23922436b61aSbalrog     } else
23932436b61aSbalrog #endif
23942436b61aSbalrog     {
2395eaa728eeSbellard         cpu_x86_load_seg_cache(env, R_CS, env->sysenter_cs & 0xfffc,
2396eaa728eeSbellard                                0, 0xffffffff,
2397eaa728eeSbellard                                DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2398eaa728eeSbellard                                DESC_S_MASK |
2399eaa728eeSbellard                                DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
24002436b61aSbalrog     }
2401eaa728eeSbellard     cpu_x86_load_seg_cache(env, R_SS, (env->sysenter_cs + 8) & 0xfffc,
2402eaa728eeSbellard                            0, 0xffffffff,
2403eaa728eeSbellard                            DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2404eaa728eeSbellard                            DESC_S_MASK |
2405eaa728eeSbellard                            DESC_W_MASK | DESC_A_MASK);
240608b3ded6Sliguang     env->regs[R_ESP] = env->sysenter_esp;
2407a78d0eabSliguang     env->eip = env->sysenter_eip;
2408eaa728eeSbellard }
2409eaa728eeSbellard 
24102999a0b2SBlue Swirl void helper_sysexit(CPUX86State *env, int dflag)
2411eaa728eeSbellard {
2412eaa728eeSbellard     int cpl;
2413eaa728eeSbellard 
2414eaa728eeSbellard     cpl = env->hflags & HF_CPL_MASK;
2415eaa728eeSbellard     if (env->sysenter_cs == 0 || cpl != 0) {
2416100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC());
2417eaa728eeSbellard     }
24182436b61aSbalrog #ifdef TARGET_X86_64
24192436b61aSbalrog     if (dflag == 2) {
242020054ef0SBlue Swirl         cpu_x86_load_seg_cache(env, R_CS, ((env->sysenter_cs + 32) & 0xfffc) |
242120054ef0SBlue Swirl                                3, 0, 0xffffffff,
24222436b61aSbalrog                                DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
24232436b61aSbalrog                                DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
242420054ef0SBlue Swirl                                DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK |
242520054ef0SBlue Swirl                                DESC_L_MASK);
242620054ef0SBlue Swirl         cpu_x86_load_seg_cache(env, R_SS, ((env->sysenter_cs + 40) & 0xfffc) |
242720054ef0SBlue Swirl                                3, 0, 0xffffffff,
24282436b61aSbalrog                                DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
24292436b61aSbalrog                                DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
24302436b61aSbalrog                                DESC_W_MASK | DESC_A_MASK);
24312436b61aSbalrog     } else
24322436b61aSbalrog #endif
24332436b61aSbalrog     {
243420054ef0SBlue Swirl         cpu_x86_load_seg_cache(env, R_CS, ((env->sysenter_cs + 16) & 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_CS_MASK | DESC_R_MASK | DESC_A_MASK);
243920054ef0SBlue Swirl         cpu_x86_load_seg_cache(env, R_SS, ((env->sysenter_cs + 24) & 0xfffc) |
244020054ef0SBlue Swirl                                3, 0, 0xffffffff,
2441eaa728eeSbellard                                DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
2442eaa728eeSbellard                                DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
2443eaa728eeSbellard                                DESC_W_MASK | DESC_A_MASK);
24442436b61aSbalrog     }
244508b3ded6Sliguang     env->regs[R_ESP] = env->regs[R_ECX];
2446a78d0eabSliguang     env->eip = env->regs[R_EDX];
2447eaa728eeSbellard }
2448eaa728eeSbellard 
24492999a0b2SBlue Swirl target_ulong helper_lsl(CPUX86State *env, target_ulong selector1)
2450eaa728eeSbellard {
2451eaa728eeSbellard     unsigned int limit;
2452eaa728eeSbellard     uint32_t e1, e2, eflags, selector;
2453eaa728eeSbellard     int rpl, dpl, cpl, type;
2454eaa728eeSbellard 
2455eaa728eeSbellard     selector = selector1 & 0xffff;
2456f0967a1aSBlue Swirl     eflags = cpu_cc_compute_all(env, CC_OP);
245720054ef0SBlue Swirl     if ((selector & 0xfffc) == 0) {
2458dc1ded53Saliguori         goto fail;
245920054ef0SBlue Swirl     }
2460100ec099SPavel Dovgalyuk     if (load_segment_ra(env, &e1, &e2, selector, GETPC()) != 0) {
2461eaa728eeSbellard         goto fail;
246220054ef0SBlue Swirl     }
2463eaa728eeSbellard     rpl = selector & 3;
2464eaa728eeSbellard     dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2465eaa728eeSbellard     cpl = env->hflags & HF_CPL_MASK;
2466eaa728eeSbellard     if (e2 & DESC_S_MASK) {
2467eaa728eeSbellard         if ((e2 & DESC_CS_MASK) && (e2 & DESC_C_MASK)) {
2468eaa728eeSbellard             /* conforming */
2469eaa728eeSbellard         } else {
247020054ef0SBlue Swirl             if (dpl < cpl || dpl < rpl) {
2471eaa728eeSbellard                 goto fail;
2472eaa728eeSbellard             }
247320054ef0SBlue Swirl         }
2474eaa728eeSbellard     } else {
2475eaa728eeSbellard         type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
2476eaa728eeSbellard         switch (type) {
2477eaa728eeSbellard         case 1:
2478eaa728eeSbellard         case 2:
2479eaa728eeSbellard         case 3:
2480eaa728eeSbellard         case 9:
2481eaa728eeSbellard         case 11:
2482eaa728eeSbellard             break;
2483eaa728eeSbellard         default:
2484eaa728eeSbellard             goto fail;
2485eaa728eeSbellard         }
2486eaa728eeSbellard         if (dpl < cpl || dpl < rpl) {
2487eaa728eeSbellard         fail:
2488eaa728eeSbellard             CC_SRC = eflags & ~CC_Z;
2489eaa728eeSbellard             return 0;
2490eaa728eeSbellard         }
2491eaa728eeSbellard     }
2492eaa728eeSbellard     limit = get_seg_limit(e1, e2);
2493eaa728eeSbellard     CC_SRC = eflags | CC_Z;
2494eaa728eeSbellard     return limit;
2495eaa728eeSbellard }
2496eaa728eeSbellard 
24972999a0b2SBlue Swirl target_ulong helper_lar(CPUX86State *env, target_ulong selector1)
2498eaa728eeSbellard {
2499eaa728eeSbellard     uint32_t e1, e2, eflags, selector;
2500eaa728eeSbellard     int rpl, dpl, cpl, type;
2501eaa728eeSbellard 
2502eaa728eeSbellard     selector = selector1 & 0xffff;
2503f0967a1aSBlue Swirl     eflags = cpu_cc_compute_all(env, CC_OP);
250420054ef0SBlue Swirl     if ((selector & 0xfffc) == 0) {
2505eaa728eeSbellard         goto fail;
250620054ef0SBlue Swirl     }
2507100ec099SPavel Dovgalyuk     if (load_segment_ra(env, &e1, &e2, selector, GETPC()) != 0) {
2508eaa728eeSbellard         goto fail;
250920054ef0SBlue Swirl     }
2510eaa728eeSbellard     rpl = selector & 3;
2511eaa728eeSbellard     dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2512eaa728eeSbellard     cpl = env->hflags & HF_CPL_MASK;
2513eaa728eeSbellard     if (e2 & DESC_S_MASK) {
2514eaa728eeSbellard         if ((e2 & DESC_CS_MASK) && (e2 & DESC_C_MASK)) {
2515eaa728eeSbellard             /* conforming */
2516eaa728eeSbellard         } else {
251720054ef0SBlue Swirl             if (dpl < cpl || dpl < rpl) {
2518eaa728eeSbellard                 goto fail;
2519eaa728eeSbellard             }
252020054ef0SBlue Swirl         }
2521eaa728eeSbellard     } else {
2522eaa728eeSbellard         type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
2523eaa728eeSbellard         switch (type) {
2524eaa728eeSbellard         case 1:
2525eaa728eeSbellard         case 2:
2526eaa728eeSbellard         case 3:
2527eaa728eeSbellard         case 4:
2528eaa728eeSbellard         case 5:
2529eaa728eeSbellard         case 9:
2530eaa728eeSbellard         case 11:
2531eaa728eeSbellard         case 12:
2532eaa728eeSbellard             break;
2533eaa728eeSbellard         default:
2534eaa728eeSbellard             goto fail;
2535eaa728eeSbellard         }
2536eaa728eeSbellard         if (dpl < cpl || dpl < rpl) {
2537eaa728eeSbellard         fail:
2538eaa728eeSbellard             CC_SRC = eflags & ~CC_Z;
2539eaa728eeSbellard             return 0;
2540eaa728eeSbellard         }
2541eaa728eeSbellard     }
2542eaa728eeSbellard     CC_SRC = eflags | CC_Z;
2543eaa728eeSbellard     return e2 & 0x00f0ff00;
2544eaa728eeSbellard }
2545eaa728eeSbellard 
25462999a0b2SBlue Swirl void helper_verr(CPUX86State *env, target_ulong selector1)
2547eaa728eeSbellard {
2548eaa728eeSbellard     uint32_t e1, e2, eflags, selector;
2549eaa728eeSbellard     int rpl, dpl, cpl;
2550eaa728eeSbellard 
2551eaa728eeSbellard     selector = selector1 & 0xffff;
2552f0967a1aSBlue Swirl     eflags = cpu_cc_compute_all(env, CC_OP);
255320054ef0SBlue Swirl     if ((selector & 0xfffc) == 0) {
2554eaa728eeSbellard         goto fail;
255520054ef0SBlue Swirl     }
2556100ec099SPavel Dovgalyuk     if (load_segment_ra(env, &e1, &e2, selector, GETPC()) != 0) {
2557eaa728eeSbellard         goto fail;
255820054ef0SBlue Swirl     }
255920054ef0SBlue Swirl     if (!(e2 & DESC_S_MASK)) {
2560eaa728eeSbellard         goto fail;
256120054ef0SBlue Swirl     }
2562eaa728eeSbellard     rpl = selector & 3;
2563eaa728eeSbellard     dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2564eaa728eeSbellard     cpl = env->hflags & HF_CPL_MASK;
2565eaa728eeSbellard     if (e2 & DESC_CS_MASK) {
256620054ef0SBlue Swirl         if (!(e2 & DESC_R_MASK)) {
2567eaa728eeSbellard             goto fail;
256820054ef0SBlue Swirl         }
2569eaa728eeSbellard         if (!(e2 & DESC_C_MASK)) {
257020054ef0SBlue Swirl             if (dpl < cpl || dpl < rpl) {
2571eaa728eeSbellard                 goto fail;
2572eaa728eeSbellard             }
257320054ef0SBlue Swirl         }
2574eaa728eeSbellard     } else {
2575eaa728eeSbellard         if (dpl < cpl || dpl < rpl) {
2576eaa728eeSbellard         fail:
2577eaa728eeSbellard             CC_SRC = eflags & ~CC_Z;
2578eaa728eeSbellard             return;
2579eaa728eeSbellard         }
2580eaa728eeSbellard     }
2581eaa728eeSbellard     CC_SRC = eflags | CC_Z;
2582eaa728eeSbellard }
2583eaa728eeSbellard 
25842999a0b2SBlue Swirl void helper_verw(CPUX86State *env, target_ulong selector1)
2585eaa728eeSbellard {
2586eaa728eeSbellard     uint32_t e1, e2, eflags, selector;
2587eaa728eeSbellard     int rpl, dpl, cpl;
2588eaa728eeSbellard 
2589eaa728eeSbellard     selector = selector1 & 0xffff;
2590f0967a1aSBlue Swirl     eflags = cpu_cc_compute_all(env, CC_OP);
259120054ef0SBlue Swirl     if ((selector & 0xfffc) == 0) {
2592eaa728eeSbellard         goto fail;
259320054ef0SBlue Swirl     }
2594100ec099SPavel Dovgalyuk     if (load_segment_ra(env, &e1, &e2, selector, GETPC()) != 0) {
2595eaa728eeSbellard         goto fail;
259620054ef0SBlue Swirl     }
259720054ef0SBlue Swirl     if (!(e2 & DESC_S_MASK)) {
2598eaa728eeSbellard         goto fail;
259920054ef0SBlue Swirl     }
2600eaa728eeSbellard     rpl = selector & 3;
2601eaa728eeSbellard     dpl = (e2 >> DESC_DPL_SHIFT) & 3;
2602eaa728eeSbellard     cpl = env->hflags & HF_CPL_MASK;
2603eaa728eeSbellard     if (e2 & DESC_CS_MASK) {
2604eaa728eeSbellard         goto fail;
2605eaa728eeSbellard     } else {
260620054ef0SBlue Swirl         if (dpl < cpl || dpl < rpl) {
2607eaa728eeSbellard             goto fail;
260820054ef0SBlue Swirl         }
2609eaa728eeSbellard         if (!(e2 & DESC_W_MASK)) {
2610eaa728eeSbellard         fail:
2611eaa728eeSbellard             CC_SRC = eflags & ~CC_Z;
2612eaa728eeSbellard             return;
2613eaa728eeSbellard         }
2614eaa728eeSbellard     }
2615eaa728eeSbellard     CC_SRC = eflags | CC_Z;
2616eaa728eeSbellard }
2617eaa728eeSbellard 
26183e457172SBlue Swirl #if defined(CONFIG_USER_ONLY)
26192999a0b2SBlue Swirl void cpu_x86_load_seg(CPUX86State *env, int seg_reg, int selector)
26203e457172SBlue Swirl {
26213e457172SBlue Swirl     if (!(env->cr[0] & CR0_PE_MASK) || (env->eflags & VM_MASK)) {
2622b98dbc90SPaolo Bonzini         int dpl = (env->eflags & VM_MASK) ? 3 : 0;
26233e457172SBlue Swirl         selector &= 0xffff;
26243e457172SBlue Swirl         cpu_x86_load_seg_cache(env, seg_reg, selector,
2625b98dbc90SPaolo Bonzini                                (selector << 4), 0xffff,
2626b98dbc90SPaolo Bonzini                                DESC_P_MASK | DESC_S_MASK | DESC_W_MASK |
2627b98dbc90SPaolo Bonzini                                DESC_A_MASK | (dpl << DESC_DPL_SHIFT));
26283e457172SBlue Swirl     } else {
26292999a0b2SBlue Swirl         helper_load_seg(env, seg_reg, selector);
26303e457172SBlue Swirl     }
26313e457172SBlue Swirl }
26323e457172SBlue Swirl #endif
263381cf8d8aSPaolo Bonzini 
263481cf8d8aSPaolo Bonzini /* check if Port I/O is allowed in TSS */
2635100ec099SPavel Dovgalyuk static inline void check_io(CPUX86State *env, int addr, int size,
2636100ec099SPavel Dovgalyuk                             uintptr_t retaddr)
263781cf8d8aSPaolo Bonzini {
263881cf8d8aSPaolo Bonzini     int io_offset, val, mask;
263981cf8d8aSPaolo Bonzini 
264081cf8d8aSPaolo Bonzini     /* TSS must be a valid 32 bit one */
264181cf8d8aSPaolo Bonzini     if (!(env->tr.flags & DESC_P_MASK) ||
264281cf8d8aSPaolo Bonzini         ((env->tr.flags >> DESC_TYPE_SHIFT) & 0xf) != 9 ||
264381cf8d8aSPaolo Bonzini         env->tr.limit < 103) {
264481cf8d8aSPaolo Bonzini         goto fail;
264581cf8d8aSPaolo Bonzini     }
2646100ec099SPavel Dovgalyuk     io_offset = cpu_lduw_kernel_ra(env, env->tr.base + 0x66, retaddr);
264781cf8d8aSPaolo Bonzini     io_offset += (addr >> 3);
264881cf8d8aSPaolo Bonzini     /* Note: the check needs two bytes */
264981cf8d8aSPaolo Bonzini     if ((io_offset + 1) > env->tr.limit) {
265081cf8d8aSPaolo Bonzini         goto fail;
265181cf8d8aSPaolo Bonzini     }
2652100ec099SPavel Dovgalyuk     val = cpu_lduw_kernel_ra(env, env->tr.base + io_offset, retaddr);
265381cf8d8aSPaolo Bonzini     val >>= (addr & 7);
265481cf8d8aSPaolo Bonzini     mask = (1 << size) - 1;
265581cf8d8aSPaolo Bonzini     /* all bits must be zero to allow the I/O */
265681cf8d8aSPaolo Bonzini     if ((val & mask) != 0) {
265781cf8d8aSPaolo Bonzini     fail:
2658100ec099SPavel Dovgalyuk         raise_exception_err_ra(env, EXCP0D_GPF, 0, retaddr);
265981cf8d8aSPaolo Bonzini     }
266081cf8d8aSPaolo Bonzini }
266181cf8d8aSPaolo Bonzini 
266281cf8d8aSPaolo Bonzini void helper_check_iob(CPUX86State *env, uint32_t t0)
266381cf8d8aSPaolo Bonzini {
2664100ec099SPavel Dovgalyuk     check_io(env, t0, 1, GETPC());
266581cf8d8aSPaolo Bonzini }
266681cf8d8aSPaolo Bonzini 
266781cf8d8aSPaolo Bonzini void helper_check_iow(CPUX86State *env, uint32_t t0)
266881cf8d8aSPaolo Bonzini {
2669100ec099SPavel Dovgalyuk     check_io(env, t0, 2, GETPC());
267081cf8d8aSPaolo Bonzini }
267181cf8d8aSPaolo Bonzini 
267281cf8d8aSPaolo Bonzini void helper_check_iol(CPUX86State *env, uint32_t t0)
267381cf8d8aSPaolo Bonzini {
2674100ec099SPavel Dovgalyuk     check_io(env, t0, 4, GETPC());
267581cf8d8aSPaolo Bonzini }
2676