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