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