128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner/*
228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner *  x86 segmentation related helpers:
328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner *  TSS, interrupts, system calls, jumps and call/task gates, descriptors
428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner *
528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner *  Copyright (c) 2003 Fabrice Bellard
628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner *
728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner * This library is free software; you can redistribute it and/or
828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner * modify it under the terms of the GNU Lesser General Public
928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner * License as published by the Free Software Foundation; either
1028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner * version 2 of the License, or (at your option) any later version.
1128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner *
1228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner * This library is distributed in the hope that it will be useful,
1328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner * but WITHOUT ANY WARRANTY; without even the implied warranty of
1428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
1528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner * Lesser General Public License for more details.
1628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner *
1728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner * You should have received a copy of the GNU Lesser General Public
1828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner * License along with this library; if not, see <http://www.gnu.org/licenses/>.
1928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner */
2028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
2128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#include "cpu.h"
2228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#include "qemu/log.h"
2328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#include "helper.h"
2428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
2528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#if !defined(CONFIG_USER_ONLY)
2628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#include "exec/softmmu_exec.h"
2728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif /* !defined(CONFIG_USER_ONLY) */
2828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
2928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner//#define DEBUG_PCALL
3028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
3128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
3228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#ifdef DEBUG_PCALL
3328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#  define LOG_PCALL(...) qemu_log_mask(CPU_LOG_PCALL, ## __VA_ARGS__)
3428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#  define LOG_PCALL_STATE(env) \
3528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner          log_cpu_state_mask(CPU_LOG_PCALL, (env), X86_DUMP_CCOP)
3628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#else
3728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#  define LOG_PCALL(...) do { } while (0)
3828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#  define LOG_PCALL_STATE(env) do { } while (0)
3928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
4028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
4128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner/* return non zero if error */
4228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnerstatic inline int load_segment(CPUX86State *env,
4328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               uint32_t *e1_ptr, uint32_t *e2_ptr,
4428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               int selector)
4528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
4628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    SegmentCache *dt;
4728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    int index;
4828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    target_ulong ptr;
4928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
5028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (selector & 0x4)
5128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        dt = &env->ldt;
5228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    else
5328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        dt = &env->gdt;
5428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    index = selector & ~7;
5528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if ((index + 7) > dt->limit)
5628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        return -1;
5728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    ptr = dt->base + index;
5828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    *e1_ptr = cpu_ldl_kernel(env, ptr);
5928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    *e2_ptr = cpu_ldl_kernel(env, ptr + 4);
6028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    return 0;
6128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
6228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
6328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnerstatic inline unsigned int get_seg_limit(uint32_t e1, uint32_t e2)
6428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
6528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    unsigned int limit;
6628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    limit = (e1 & 0xffff) | (e2 & 0x000f0000);
6728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (e2 & DESC_G_MASK)
6828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        limit = (limit << 12) | 0xfff;
6928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    return limit;
7028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
7128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
7228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnerstatic inline uint32_t get_seg_base(uint32_t e1, uint32_t e2)
7328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
7428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    return ((e1 >> 16) | ((e2 & 0xff) << 16) | (e2 & 0xff000000));
7528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
7628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
7728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnerstatic inline void load_seg_cache_raw_dt(SegmentCache *sc, uint32_t e1, uint32_t e2)
7828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
7928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    sc->base = get_seg_base(e1, e2);
8028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    sc->limit = get_seg_limit(e1, e2);
8128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    sc->flags = e2;
8228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
8328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
8428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner/* init the segment cache in vm86 mode. */
8528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnerstatic inline void load_seg_vm(CPUX86State *env, int seg, int selector)
8628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
8728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    selector &= 0xffff;
8828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    cpu_x86_load_seg_cache(env, seg, selector,
8928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                           (selector << 4), 0xffff, 0);
9028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
9128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
9228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnerstatic inline void get_ss_esp_from_tss(CPUX86State *env,
9328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                                       uint32_t *ss_ptr,
9428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                                       uint32_t *esp_ptr, int dpl)
9528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
9628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    int type, index, shift;
9728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
9828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#if 0
9928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    {
10028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        int i;
10128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        printf("TR: base=%p limit=%x\n", env->tr.base, env->tr.limit);
10228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        for(i=0;i<env->tr.limit;i++) {
10328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            printf("%02x ", env->tr.base[i]);
10428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if ((i & 7) == 7) printf("\n");
10528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
10628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        printf("\n");
10728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
10828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
10928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
11028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (!(env->tr.flags & DESC_P_MASK))
11128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_abort(env, "invalid tss");
11228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf;
11328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if ((type & 7) != 1)
11428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_abort(env, "invalid tss type");
11528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    shift = type >> 3;
11628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    index = (dpl * 4 + 2) << shift;
11728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (index + (4 << shift) - 1 > env->tr.limit)
11828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0A_TSS, env->tr.selector & 0xfffc);
11928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (shift == 0) {
12028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        *esp_ptr = cpu_lduw_kernel(env, env->tr.base + index);
12128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        *ss_ptr = cpu_lduw_kernel(env, env->tr.base + index + 2);
12228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else {
12328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        *esp_ptr = cpu_ldl_kernel(env, env->tr.base + index);
12428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        *ss_ptr = cpu_lduw_kernel(env, env->tr.base + index + 4);
12528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
12628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
12728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
12828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner/* XXX: merge with load_seg() */
12928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnerstatic void tss_load_seg(CPUX86State *env, int seg_reg, int selector)
13028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
13128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    uint32_t e1, e2;
13228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    int rpl, dpl, cpl;
13328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
13428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if ((selector & 0xfffc) != 0) {
13528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (load_segment(env, &e1, &e2, selector) != 0)
13628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc);
13728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (!(e2 & DESC_S_MASK))
13828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc);
13928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        rpl = selector & 3;
14028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        dpl = (e2 >> DESC_DPL_SHIFT) & 3;
14128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpl = env->hflags & HF_CPL_MASK;
14228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (seg_reg == R_CS) {
14328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (!(e2 & DESC_CS_MASK))
14428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc);
14528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            /* XXX: is it correct ? */
14628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (dpl != rpl)
14728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc);
14828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if ((e2 & DESC_C_MASK) && dpl > rpl)
14928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc);
15028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        } else if (seg_reg == R_SS) {
15128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            /* SS must be writable data */
15228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK))
15328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc);
15428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (dpl != cpl || dpl != rpl)
15528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc);
15628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        } else {
15728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            /* not readable code */
15828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if ((e2 & DESC_CS_MASK) && !(e2 & DESC_R_MASK))
15928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc);
16028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            /* if data or non conforming code, checks the rights */
16128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (((e2 >> DESC_TYPE_SHIFT) & 0xf) < 12) {
16228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                if (dpl < cpl || dpl < rpl)
16328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                    raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc);
16428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            }
16528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
16628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (!(e2 & DESC_P_MASK))
16728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0B_NOSEG, selector & 0xfffc);
16828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_x86_load_seg_cache(env, seg_reg, selector,
16928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                       get_seg_base(e1, e2),
17028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                       get_seg_limit(e1, e2),
17128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                       e2);
17228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else {
17328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (seg_reg == R_SS || seg_reg == R_CS)
17428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc);
17528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
17628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
17728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
17828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#define SWITCH_TSS_JMP  0
17928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#define SWITCH_TSS_IRET 1
18028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#define SWITCH_TSS_CALL 2
18128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
18228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner/* XXX: restore CPU state in registers (PowerPC case) */
18328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnerstatic void switch_tss(CPUX86State *env,
18428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                       int tss_selector,
18528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                       uint32_t e1, uint32_t e2, int source,
18628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                       uint32_t next_eip)
18728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
18828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    int tss_limit, tss_limit_max, type, old_tss_limit_max, old_type, v1, v2, i;
18928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    target_ulong tss_base;
19028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    uint32_t new_regs[8], new_segs[6];
19128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    uint32_t new_eflags, new_eip, new_cr3, new_ldt;
19228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    uint32_t old_eflags, eflags_mask;
19328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    SegmentCache *dt;
19428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    int index;
19528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    target_ulong ptr;
19628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
19728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
19828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    LOG_PCALL("switch_tss: sel=0x%04x type=%d src=%d\n", tss_selector, type, source);
19928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
20028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* if task gate, we read the TSS segment and we load it */
20128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (type == 5) {
20228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (!(e2 & DESC_P_MASK))
20328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0B_NOSEG, tss_selector & 0xfffc);
20428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        tss_selector = e1 >> 16;
20528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (tss_selector & 4)
20628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0A_TSS, tss_selector & 0xfffc);
20728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (load_segment(env, &e1, &e2, tss_selector) != 0)
20828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0D_GPF, tss_selector & 0xfffc);
20928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (e2 & DESC_S_MASK)
21028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0D_GPF, tss_selector & 0xfffc);
21128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
21228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if ((type & 7) != 1)
21328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0D_GPF, tss_selector & 0xfffc);
21428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
21528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
21628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (!(e2 & DESC_P_MASK))
21728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0B_NOSEG, tss_selector & 0xfffc);
21828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
21928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (type & 8)
22028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        tss_limit_max = 103;
22128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    else
22228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        tss_limit_max = 43;
22328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    tss_limit = get_seg_limit(e1, e2);
22428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    tss_base = get_seg_base(e1, e2);
22528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if ((tss_selector & 4) != 0 ||
22628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        tss_limit < tss_limit_max)
22728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0A_TSS, tss_selector & 0xfffc);
22828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    old_type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf;
22928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (old_type & 8)
23028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        old_tss_limit_max = 103;
23128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    else
23228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        old_tss_limit_max = 43;
23328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
23428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* read all the registers from the new TSS */
23528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (type & 8) {
23628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* 32 bit */
23728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        new_cr3 = cpu_ldl_kernel(env, tss_base + 0x1c);
23828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        new_eip = cpu_ldl_kernel(env, tss_base + 0x20);
23928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        new_eflags = cpu_ldl_kernel(env, tss_base + 0x24);
24028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        for(i = 0; i < 8; i++)
24128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            new_regs[i] = cpu_ldl_kernel(env, tss_base + (0x28 + i * 4));
24228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        for(i = 0; i < 6; i++)
24328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            new_segs[i] = cpu_lduw_kernel(env, tss_base + (0x48 + i * 4));
24428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        new_ldt = cpu_lduw_kernel(env, tss_base + 0x60);
24528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_ldl_kernel(env, tss_base + 0x64);
24628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else {
24728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* 16 bit */
24828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        new_cr3 = 0;
24928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        new_eip = cpu_lduw_kernel(env, tss_base + 0x0e);
25028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        new_eflags = cpu_lduw_kernel(env, tss_base + 0x10);
25128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        for(i = 0; i < 8; i++)
25228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            new_regs[i] = cpu_lduw_kernel(env, tss_base + (0x12 + i * 2)) | 0xffff0000;
25328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        for(i = 0; i < 4; i++)
25428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            new_segs[i] = cpu_lduw_kernel(env, tss_base + (0x22 + i * 4));
25528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        new_ldt = cpu_lduw_kernel(env, tss_base + 0x2a);
25628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        new_segs[R_FS] = 0;
25728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        new_segs[R_GS] = 0;
25828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
25928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
26028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* NOTE: we must avoid memory exceptions during the task switch,
26128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner       so we make dummy accesses before */
26228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* XXX: it can still fail in some cases, so a bigger hack is
26328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner       necessary to valid the TLB after having done the accesses */
26428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
26528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    v1 = cpu_ldub_kernel(env, env->tr.base);
26628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    v2 = cpu_ldub_kernel(env, env->tr.base + old_tss_limit_max);
26728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    cpu_stb_kernel(env, env->tr.base, v1);
26828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    cpu_stb_kernel(env, env->tr.base + old_tss_limit_max, v2);
26928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
27028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* clear busy bit (it is restartable) */
27128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_IRET) {
27228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        target_ulong ptr;
27328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        uint32_t e2;
27428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        ptr = env->gdt.base + (env->tr.selector & ~7);
27528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        e2 = cpu_ldl_kernel(env, ptr + 4);
27628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        e2 &= ~DESC_TSS_BUSY_MASK;
27728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_stl_kernel(env, ptr + 4, e2);
27828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
27928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    old_eflags = cpu_compute_eflags(env);
28028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (source == SWITCH_TSS_IRET)
28128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        old_eflags &= ~NT_MASK;
28228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
28328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* save the current state in the old TSS */
28428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (type & 8) {
28528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* 32 bit */
28628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_stl_kernel(env, env->tr.base + 0x20, next_eip);
28728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_stl_kernel(env, env->tr.base + 0x24, old_eflags);
28828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_stl_kernel(env, env->tr.base + (0x28 + 0 * 4), EAX);
28928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_stl_kernel(env, env->tr.base + (0x28 + 1 * 4), ECX);
29028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_stl_kernel(env, env->tr.base + (0x28 + 2 * 4), EDX);
29128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_stl_kernel(env, env->tr.base + (0x28 + 3 * 4), EBX);
29228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_stl_kernel(env, env->tr.base + (0x28 + 4 * 4), ESP);
29328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_stl_kernel(env, env->tr.base + (0x28 + 5 * 4), EBP);
29428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_stl_kernel(env, env->tr.base + (0x28 + 6 * 4), ESI);
29528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_stl_kernel(env, env->tr.base + (0x28 + 7 * 4), EDI);
29628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        for(i = 0; i < 6; i++)
29728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            cpu_stw_kernel(env, env->tr.base + (0x48 + i * 4), env->segs[i].selector);
29828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else {
29928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* 16 bit */
30028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_stw_kernel(env, env->tr.base + 0x0e, next_eip);
30128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_stw_kernel(env, env->tr.base + 0x10, old_eflags);
30228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_stw_kernel(env, env->tr.base + (0x12 + 0 * 2), EAX);
30328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_stw_kernel(env, env->tr.base + (0x12 + 1 * 2), ECX);
30428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_stw_kernel(env, env->tr.base + (0x12 + 2 * 2), EDX);
30528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_stw_kernel(env, env->tr.base + (0x12 + 3 * 2), EBX);
30628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_stw_kernel(env, env->tr.base + (0x12 + 4 * 2), ESP);
30728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_stw_kernel(env, env->tr.base + (0x12 + 5 * 2), EBP);
30828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_stw_kernel(env, env->tr.base + (0x12 + 6 * 2), ESI);
30928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_stw_kernel(env, env->tr.base + (0x12 + 7 * 2), EDI);
31028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        for(i = 0; i < 4; i++)
31128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            cpu_stw_kernel(env, env->tr.base + (0x22 + i * 4), env->segs[i].selector);
31228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
31328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
31428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* now if an exception occurs, it will occurs in the next task
31528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner       context */
31628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
31728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (source == SWITCH_TSS_CALL) {
31828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_stw_kernel(env, tss_base, env->tr.selector);
31928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        new_eflags |= NT_MASK;
32028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
32128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
32228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* set busy bit */
32328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_CALL) {
32428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        target_ulong ptr;
32528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        uint32_t e2;
32628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        ptr = env->gdt.base + (tss_selector & ~7);
32728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        e2 = cpu_ldl_kernel(env, ptr + 4);
32828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        e2 |= DESC_TSS_BUSY_MASK;
32928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_stl_kernel(env, ptr + 4, e2);
33028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
33128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
33228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* set the new CPU state */
33328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* from this point, any exception which occurs can give problems */
33428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->cr[0] |= CR0_TS_MASK;
33528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->hflags |= HF_TS_MASK;
33628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->tr.selector = tss_selector;
33728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->tr.base = tss_base;
33828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->tr.limit = tss_limit;
33928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->tr.flags = e2 & ~DESC_TSS_BUSY_MASK;
34028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
34128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if ((type & 8) && (env->cr[0] & CR0_PG_MASK)) {
34228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_x86_update_cr3(env, new_cr3);
34328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
34428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
34528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* load all registers without an exception, then reload them with
34628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner       possible exception */
34728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->eip = new_eip;
34828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    eflags_mask = TF_MASK | AC_MASK | ID_MASK |
34928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | NT_MASK;
35028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (!(type & 8))
35128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        eflags_mask &= 0xffff;
35228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    cpu_load_eflags(env, new_eflags, eflags_mask);
35328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* XXX: what to do in 16 bit case ? */
35428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    EAX = new_regs[0];
35528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    ECX = new_regs[1];
35628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    EDX = new_regs[2];
35728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    EBX = new_regs[3];
35828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    ESP = new_regs[4];
35928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    EBP = new_regs[5];
36028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    ESI = new_regs[6];
36128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    EDI = new_regs[7];
36228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (new_eflags & VM_MASK) {
36328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        for(i = 0; i < 6; i++)
36428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            load_seg_vm(env, i, new_segs[i]);
36528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* in vm86, CPL is always 3 */
36628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_x86_set_cpl(env, 3);
36728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else {
36828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* CPL is set the RPL of CS */
36928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_x86_set_cpl(env, new_segs[R_CS] & 3);
37028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* first just selectors as the rest may trigger exceptions */
37128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        for(i = 0; i < 6; i++)
37228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            cpu_x86_load_seg_cache(env, i, new_segs[i], 0, 0, 0);
37328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
37428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
37528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->ldt.selector = new_ldt & ~4;
37628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->ldt.base = 0;
37728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->ldt.limit = 0;
37828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->ldt.flags = 0;
37928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
38028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* load the LDT */
38128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (new_ldt & 4)
38228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0A_TSS, new_ldt & 0xfffc);
38328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
38428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if ((new_ldt & 0xfffc) != 0) {
38528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        dt = &env->gdt;
38628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        index = new_ldt & ~7;
38728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if ((index + 7) > dt->limit)
38828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0A_TSS, new_ldt & 0xfffc);
38928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        ptr = dt->base + index;
39028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        e1 = cpu_ldl_kernel(env, ptr);
39128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        e2 = cpu_ldl_kernel(env, ptr + 4);
39228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2)
39328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0A_TSS, new_ldt & 0xfffc);
39428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (!(e2 & DESC_P_MASK))
39528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0A_TSS, new_ldt & 0xfffc);
39628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        load_seg_cache_raw_dt(&env->ldt, e1, e2);
39728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
39828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
39928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* load the segments */
40028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (!(new_eflags & VM_MASK)) {
40128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        tss_load_seg(env, R_CS, new_segs[R_CS]);
40228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        tss_load_seg(env, R_SS, new_segs[R_SS]);
40328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        tss_load_seg(env, R_ES, new_segs[R_ES]);
40428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        tss_load_seg(env, R_DS, new_segs[R_DS]);
40528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        tss_load_seg(env, R_FS, new_segs[R_FS]);
40628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        tss_load_seg(env, R_GS, new_segs[R_GS]);
40728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
40828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
40928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* check that EIP is in the CS segment limits */
41028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (new_eip > env->segs[R_CS].limit) {
41128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* XXX: different exception if CALL ? */
41228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0D_GPF, 0);
41328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
41428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
41528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#ifndef CONFIG_USER_ONLY
41628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* reset local breakpoints */
41728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (env->dr[7] & 0x55) {
41828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        for (i = 0; i < 4; i++) {
41928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (hw_breakpoint_enabled(env->dr[7], i) == 0x1)
42028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                hw_breakpoint_remove(env, i);
42128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
42228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        env->dr[7] &= ~0x55;
42328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
42428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
42528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
42628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
42728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnerstatic inline unsigned int get_sp_mask(unsigned int e2)
42828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
42928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (e2 & DESC_B_MASK)
43028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        return 0xffffffff;
43128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    else
43228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        return 0xffff;
43328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
43428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
43528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnerstatic int exeption_has_error_code(int intno)
43628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
43728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        switch(intno) {
43828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        case 8:
43928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        case 10:
44028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        case 11:
44128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        case 12:
44228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        case 13:
44328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        case 14:
44428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        case 17:
44528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            return 1;
44628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
44728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        return 0;
44828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
44928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
45028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#ifdef TARGET_X86_64
45128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#define SET_ESP(val, sp_mask)\
45228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnerdo {\
45328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if ((sp_mask) == 0xffff)\
45428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        ESP = (ESP & ~0xffff) | ((val) & 0xffff);\
45528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    else if ((sp_mask) == 0xffffffffLL)\
45628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        ESP = (uint32_t)(val);\
45728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    else\
45828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        ESP = (val);\
45928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner} while (0)
46028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#else
46128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#define SET_ESP(val, sp_mask) ESP = (ESP & ~(sp_mask)) | ((val) & (sp_mask))
46228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
46328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
46428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner/* in 64-bit machines, this can overflow. So this segment addition macro
46528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner * can be used to trim the value to 32-bit whenever needed */
46628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#define SEG_ADDL(ssp, sp, sp_mask) ((uint32_t)((ssp) + (sp & (sp_mask))))
46728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
46828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner/* XXX: add a is_user flag to have proper security support */
46928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#define PUSHW(ssp, sp, sp_mask, val)\
47028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{\
47128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    sp -= 2;\
47228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    cpu_stw_kernel(env, (ssp) + (sp & (sp_mask)), (val));\
47328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
47428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
47528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#define PUSHL(ssp, sp, sp_mask, val)\
47628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{\
47728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    sp -= 4;\
47828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    cpu_stl_kernel(env, SEG_ADDL(ssp, sp, sp_mask), (uint32_t)(val));\
47928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
48028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
48128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#define POPW(ssp, sp, sp_mask, val)\
48228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{\
48328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    val = cpu_lduw_kernel(env, (ssp) + (sp & (sp_mask)));\
48428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    sp += 2;\
48528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
48628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
48728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#define POPL(ssp, sp, sp_mask, val)\
48828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{\
48928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    val = (uint32_t)cpu_ldl_kernel(env, SEG_ADDL(ssp, sp, sp_mask));\
49028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    sp += 4;\
49128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
49228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
49328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner/* protected mode interrupt */
49428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnerstatic void do_interrupt_protected(CPUX86State *env,
49528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                                   int intno, int is_int, int error_code,
49628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                                   unsigned int next_eip, int is_hw)
49728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
49828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    SegmentCache *dt;
49928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    target_ulong ptr, ssp;
50028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    int type, dpl, selector, ss_dpl, cpl;
50128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    int has_error_code, new_stack, shift;
50228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    uint32_t e1, e2, offset, ss = 0, esp, ss_e1 = 0, ss_e2 = 0;
50328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    uint32_t old_eip, sp_mask;
50428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
50528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    has_error_code = 0;
50628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (!is_int && !is_hw)
50728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        has_error_code = exeption_has_error_code(intno);
50828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (is_int)
50928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        old_eip = next_eip;
51028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    else
51128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        old_eip = env->eip;
51228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
51328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    dt = &env->idt;
51428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (intno * 8 + 7 > dt->limit)
51528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0D_GPF, intno * 8 + 2);
51628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    ptr = dt->base + intno * 8;
51728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    e1 = cpu_ldl_kernel(env, ptr);
51828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    e2 = cpu_ldl_kernel(env, ptr + 4);
51928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* check gate type */
52028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
52128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    switch(type) {
52228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    case 5: /* task gate */
52328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* must do that check here to return the correct error code */
52428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (!(e2 & DESC_P_MASK))
52528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0B_NOSEG, intno * 8 + 2);
52628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        switch_tss(env, intno * 8, e1, e2, SWITCH_TSS_CALL, old_eip);
52728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (has_error_code) {
52828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            int type;
52928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            uint32_t mask;
53028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            /* push the error code */
53128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf;
53228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            shift = type >> 3;
53328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (env->segs[R_SS].flags & DESC_B_MASK)
53428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                mask = 0xffffffff;
53528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            else
53628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                mask = 0xffff;
53728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            esp = (ESP - (2 << shift)) & mask;
53828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            ssp = env->segs[R_SS].base + esp;
53928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (shift)
54028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                cpu_stl_kernel(env, ssp, error_code);
54128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            else
54228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                cpu_stw_kernel(env, ssp, error_code);
54328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            SET_ESP(esp, mask);
54428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
54528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        return;
54628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    case 6: /* 286 interrupt gate */
54728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    case 7: /* 286 trap gate */
54828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    case 14: /* 386 interrupt gate */
54928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    case 15: /* 386 trap gate */
55028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        break;
55128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    default:
55228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0D_GPF, intno * 8 + 2);
55328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        break;
55428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
55528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    dpl = (e2 >> DESC_DPL_SHIFT) & 3;
55628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    cpl = env->hflags & HF_CPL_MASK;
55728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* check privilege if software int */
55828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (is_int && dpl < cpl)
55928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0D_GPF, intno * 8 + 2);
56028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* check valid bit */
56128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (!(e2 & DESC_P_MASK))
56228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0B_NOSEG, intno * 8 + 2);
56328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    selector = e1 >> 16;
56428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff);
56528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if ((selector & 0xfffc) == 0)
56628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0D_GPF, 0);
56728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
56828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (load_segment(env, &e1, &e2, selector) != 0)
56928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
57028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK)))
57128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
57228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    dpl = (e2 >> DESC_DPL_SHIFT) & 3;
57328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (dpl > cpl)
57428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
57528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (!(e2 & DESC_P_MASK))
57628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0B_NOSEG, selector & 0xfffc);
57728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (!(e2 & DESC_C_MASK) && dpl < cpl) {
57828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* to inner privilege */
57928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        get_ss_esp_from_tss(env, &ss, &esp, dpl);
58028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if ((ss & 0xfffc) == 0)
58128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc);
58228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if ((ss & 3) != dpl)
58328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc);
58428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (load_segment(env, &ss_e1, &ss_e2, ss) != 0)
58528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc);
58628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        ss_dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3;
58728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (ss_dpl != dpl)
58828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc);
58928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (!(ss_e2 & DESC_S_MASK) ||
59028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            (ss_e2 & DESC_CS_MASK) ||
59128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            !(ss_e2 & DESC_W_MASK))
59228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc);
59328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (!(ss_e2 & DESC_P_MASK))
59428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc);
59528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        new_stack = 1;
59628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        sp_mask = get_sp_mask(ss_e2);
59728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        ssp = get_seg_base(ss_e1, ss_e2);
59828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else if ((e2 & DESC_C_MASK) || dpl == cpl) {
59928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* to same privilege */
60028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (env->eflags & VM_MASK)
60128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
60228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        new_stack = 0;
60328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        sp_mask = get_sp_mask(env->segs[R_SS].flags);
60428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        ssp = env->segs[R_SS].base;
60528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        esp = ESP;
60628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        dpl = cpl;
60728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else {
60828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
60928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        new_stack = 0; /* avoid warning */
61028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        sp_mask = 0; /* avoid warning */
61128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        ssp = 0; /* avoid warning */
61228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        esp = 0; /* avoid warning */
61328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
61428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
61528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    shift = type >> 3;
61628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
61728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#if 0
61828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* XXX: check that enough room is available */
61928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    push_size = 6 + (new_stack << 2) + (has_error_code << 1);
62028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (env->eflags & VM_MASK)
62128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        push_size += 8;
62228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    push_size <<= shift;
62328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
62428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (shift == 1) {
62528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (new_stack) {
62628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (env->eflags & VM_MASK) {
62728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                PUSHL(ssp, esp, sp_mask, env->segs[R_GS].selector);
62828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                PUSHL(ssp, esp, sp_mask, env->segs[R_FS].selector);
62928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                PUSHL(ssp, esp, sp_mask, env->segs[R_DS].selector);
63028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                PUSHL(ssp, esp, sp_mask, env->segs[R_ES].selector);
63128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            }
63228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            PUSHL(ssp, esp, sp_mask, env->segs[R_SS].selector);
63328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            PUSHL(ssp, esp, sp_mask, ESP);
63428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
63528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        PUSHL(ssp, esp, sp_mask, cpu_compute_eflags(env));
63628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        PUSHL(ssp, esp, sp_mask, env->segs[R_CS].selector);
63728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        PUSHL(ssp, esp, sp_mask, old_eip);
63828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (has_error_code) {
63928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            PUSHL(ssp, esp, sp_mask, error_code);
64028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
64128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else {
64228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (new_stack) {
64328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (env->eflags & VM_MASK) {
64428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                PUSHW(ssp, esp, sp_mask, env->segs[R_GS].selector);
64528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                PUSHW(ssp, esp, sp_mask, env->segs[R_FS].selector);
64628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                PUSHW(ssp, esp, sp_mask, env->segs[R_DS].selector);
64728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                PUSHW(ssp, esp, sp_mask, env->segs[R_ES].selector);
64828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            }
64928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            PUSHW(ssp, esp, sp_mask, env->segs[R_SS].selector);
65028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            PUSHW(ssp, esp, sp_mask, ESP);
65128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
65228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        PUSHW(ssp, esp, sp_mask, cpu_compute_eflags(env));
65328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        PUSHW(ssp, esp, sp_mask, env->segs[R_CS].selector);
65428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        PUSHW(ssp, esp, sp_mask, old_eip);
65528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (has_error_code) {
65628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            PUSHW(ssp, esp, sp_mask, error_code);
65728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
65828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
65928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
66028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (new_stack) {
66128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (env->eflags & VM_MASK) {
66228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            cpu_x86_load_seg_cache(env, R_ES, 0, 0, 0, 0);
66328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0, 0);
66428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            cpu_x86_load_seg_cache(env, R_FS, 0, 0, 0, 0);
66528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0, 0);
66628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
66728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        ss = (ss & ~3) | dpl;
66828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_x86_load_seg_cache(env, R_SS, ss,
66928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               ssp, get_seg_limit(ss_e1, ss_e2), ss_e2);
67028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
67128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    SET_ESP(esp, sp_mask);
67228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
67328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    selector = (selector & ~3) | dpl;
67428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    cpu_x86_load_seg_cache(env, R_CS, selector,
67528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                   get_seg_base(e1, e2),
67628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                   get_seg_limit(e1, e2),
67728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                   e2);
67828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    cpu_x86_set_cpl(env, dpl);
67928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->eip = offset;
68028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
68128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* interrupt gate clear IF mask */
68228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if ((type & 1) == 0) {
68328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        env->eflags &= ~IF_MASK;
68428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
68528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK);
68628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
68728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
68828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#ifdef TARGET_X86_64
68928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
69028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#define PUSHQ(sp, val)\
69128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{\
69228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    sp -= 8;\
69328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    cpu_stq_kernel(env, sp, (val));\
69428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
69528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
69628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#define POPQ(sp, val)\
69728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{\
69828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    val = cpu_ldq_kernel(env, sp);\
69928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    sp += 8;\
70028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
70128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
70228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnerstatic inline target_ulong get_rsp_from_tss(CPUX86State *env, int level)
70328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
70428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    int index;
70528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
70628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#if 0
70728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    printf("TR: base=" TARGET_FMT_lx " limit=%x\n",
70828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner           env->tr.base, env->tr.limit);
70928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
71028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
71128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (!(env->tr.flags & DESC_P_MASK))
71228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_abort(env, "invalid tss");
71328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    index = 8 * level + 4;
71428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if ((index + 7) > env->tr.limit)
71528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0A_TSS, env->tr.selector & 0xfffc);
71628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    return cpu_ldq_kernel(env, env->tr.base + index);
71728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
71828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
71928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner/* 64 bit interrupt */
72028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnerstatic void do_interrupt64(CPUX86State *env,
72128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                           int intno, int is_int, int error_code,
72228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                           target_ulong next_eip, int is_hw)
72328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
72428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    SegmentCache *dt;
72528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    target_ulong ptr;
72628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    int type, dpl, selector, cpl, ist;
72728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    int has_error_code, new_stack;
72828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    uint32_t e1, e2, e3, ss;
72928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    target_ulong old_eip, esp, offset;
73028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
73128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    has_error_code = 0;
73228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (!is_int && !is_hw)
73328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        has_error_code = exeption_has_error_code(intno);
73428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (is_int)
73528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        old_eip = next_eip;
73628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    else
73728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        old_eip = env->eip;
73828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
73928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    dt = &env->idt;
74028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (intno * 16 + 15 > dt->limit)
74128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0D_GPF, intno * 16 + 2);
74228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    ptr = dt->base + intno * 16;
74328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    e1 = cpu_ldl_kernel(env, ptr);
74428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    e2 = cpu_ldl_kernel(env, ptr + 4);
74528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    e3 = cpu_ldl_kernel(env, ptr + 8);
74628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* check gate type */
74728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
74828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    switch(type) {
74928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    case 14: /* 386 interrupt gate */
75028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    case 15: /* 386 trap gate */
75128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        break;
75228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    default:
75328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0D_GPF, intno * 16 + 2);
75428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        break;
75528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
75628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    dpl = (e2 >> DESC_DPL_SHIFT) & 3;
75728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    cpl = env->hflags & HF_CPL_MASK;
75828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* check privilege if software int */
75928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (is_int && dpl < cpl)
76028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0D_GPF, intno * 16 + 2);
76128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* check valid bit */
76228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (!(e2 & DESC_P_MASK))
76328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0B_NOSEG, intno * 16 + 2);
76428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    selector = e1 >> 16;
76528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    offset = ((target_ulong)e3 << 32) | (e2 & 0xffff0000) | (e1 & 0x0000ffff);
76628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    ist = e2 & 7;
76728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if ((selector & 0xfffc) == 0)
76828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0D_GPF, 0);
76928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
77028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (load_segment(env, &e1, &e2, selector) != 0)
77128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
77228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK)))
77328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
77428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    dpl = (e2 >> DESC_DPL_SHIFT) & 3;
77528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (dpl > cpl)
77628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
77728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (!(e2 & DESC_P_MASK))
77828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0B_NOSEG, selector & 0xfffc);
77928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (!(e2 & DESC_L_MASK) || (e2 & DESC_B_MASK))
78028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
78128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if ((!(e2 & DESC_C_MASK) && dpl < cpl) || ist != 0) {
78228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* to inner privilege */
78328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (ist != 0)
78428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            esp = get_rsp_from_tss(env, ist + 3);
78528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        else
78628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            esp = get_rsp_from_tss(env, dpl);
78728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        esp &= ~0xfLL; /* align stack */
78828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        ss = 0;
78928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        new_stack = 1;
79028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else if ((e2 & DESC_C_MASK) || dpl == cpl) {
79128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* to same privilege */
79228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (env->eflags & VM_MASK)
79328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
79428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        new_stack = 0;
79528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (ist != 0)
79628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            esp = get_rsp_from_tss(env, ist + 3);
79728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        else
79828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            esp = ESP;
79928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        esp &= ~0xfLL; /* align stack */
80028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        dpl = cpl;
80128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else {
80228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
80328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        new_stack = 0; /* avoid warning */
80428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        esp = 0; /* avoid warning */
80528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
80628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
80728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    PUSHQ(esp, env->segs[R_SS].selector);
80828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    PUSHQ(esp, ESP);
80928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    PUSHQ(esp, cpu_compute_eflags(env));
81028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    PUSHQ(esp, env->segs[R_CS].selector);
81128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    PUSHQ(esp, old_eip);
81228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (has_error_code) {
81328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        PUSHQ(esp, error_code);
81428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
81528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
81628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (new_stack) {
81728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        ss = 0 | dpl;
81828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_x86_load_seg_cache(env, R_SS, ss, 0, 0, 0);
81928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
82028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    ESP = esp;
82128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
82228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    selector = (selector & ~3) | dpl;
82328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    cpu_x86_load_seg_cache(env, R_CS, selector,
82428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                   get_seg_base(e1, e2),
82528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                   get_seg_limit(e1, e2),
82628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                   e2);
82728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    cpu_x86_set_cpl(env, dpl);
82828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->eip = offset;
82928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
83028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* interrupt gate clear IF mask */
83128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if ((type & 1) == 0) {
83228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        env->eflags &= ~IF_MASK;
83328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
83428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK);
83528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
83628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
83728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
83828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#ifdef TARGET_X86_64
83928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#if defined(CONFIG_USER_ONLY)
84028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnervoid helper_syscall(CPUX86State *env, int next_eip_addend)
84128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
84228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->exception_index = EXCP_SYSCALL;
84328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->exception_next_eip = env->eip + next_eip_addend;
84428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    cpu_loop_exit(env);
84528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
84628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#else
84728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnervoid helper_syscall(CPUX86State *env, int next_eip_addend)
84828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
84928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    int selector;
85028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
85128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (!(env->efer & MSR_EFER_SCE)) {
85228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP06_ILLOP, 0);
85328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
85428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    selector = (env->star >> 32) & 0xffff;
85528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (env->hflags & HF_LMA_MASK) {
85628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        int code64;
85728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
85828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        ECX = env->eip + next_eip_addend;
85928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        env->regs[11] = cpu_compute_eflags(env);
86028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
86128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        code64 = env->hflags & HF_CS64_MASK;
86228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
86328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_x86_set_cpl(env, 0);
86428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc,
86528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                           0, 0xffffffff,
86628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_G_MASK | DESC_P_MASK |
86728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_S_MASK |
86828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | DESC_L_MASK);
86928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc,
87028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               0, 0xffffffff,
87128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
87228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_S_MASK |
87328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_W_MASK | DESC_A_MASK);
87428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        env->eflags &= ~env->fmask;
87528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_load_eflags(env, env->eflags, 0);
87628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (code64)
87728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            env->eip = env->lstar;
87828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        else
87928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            env->eip = env->cstar;
88028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else {
88128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        ECX = (uint32_t)(env->eip + next_eip_addend);
88228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
88328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_x86_set_cpl(env, 0);
88428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc,
88528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                           0, 0xffffffff,
88628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
88728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_S_MASK |
88828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
88928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc,
89028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               0, 0xffffffff,
89128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
89228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_S_MASK |
89328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_W_MASK | DESC_A_MASK);
89428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        env->eflags &= ~(IF_MASK | RF_MASK | VM_MASK);
89528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        env->eip = (uint32_t)env->star;
89628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
89728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
89828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
89928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
90028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
90128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#ifdef TARGET_X86_64
90228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnervoid helper_sysret(CPUX86State *env, int dflag)
90328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
90428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    int cpl, selector;
90528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
90628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (!(env->efer & MSR_EFER_SCE)) {
90728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP06_ILLOP, 0);
90828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
90928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    cpl = env->hflags & HF_CPL_MASK;
91028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (!(env->cr[0] & CR0_PE_MASK) || cpl != 0) {
91128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0D_GPF, 0);
91228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
91328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    selector = (env->star >> 48) & 0xffff;
91428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (env->hflags & HF_LMA_MASK) {
91528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (dflag == 2) {
91628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            cpu_x86_load_seg_cache(env, R_CS, (selector + 16) | 3,
91728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                                   0, 0xffffffff,
91828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                                   DESC_G_MASK | DESC_P_MASK |
91928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                                   DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
92028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                                   DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK |
92128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                                   DESC_L_MASK);
92228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            env->eip = ECX;
92328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        } else {
92428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            cpu_x86_load_seg_cache(env, R_CS, selector | 3,
92528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                                   0, 0xffffffff,
92628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                                   DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
92728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                                   DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
92828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                                   DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
92928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            env->eip = (uint32_t)ECX;
93028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
93128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_x86_load_seg_cache(env, R_SS, selector + 8,
93228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               0, 0xffffffff,
93328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
93428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
93528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_W_MASK | DESC_A_MASK);
93628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_load_eflags(env, (uint32_t)(env->regs[11]), TF_MASK | AC_MASK | ID_MASK |
93728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                    IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | NT_MASK);
93828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_x86_set_cpl(env, 3);
93928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else {
94028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_x86_load_seg_cache(env, R_CS, selector | 3,
94128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               0, 0xffffffff,
94228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
94328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
94428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
94528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        env->eip = (uint32_t)ECX;
94628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_x86_load_seg_cache(env, R_SS, selector + 8,
94728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               0, 0xffffffff,
94828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
94928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
95028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_W_MASK | DESC_A_MASK);
95128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        env->eflags |= IF_MASK;
95228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_x86_set_cpl(env, 3);
95328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
95428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
95528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
95628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
95728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner/* real mode interrupt */
95828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnerstatic void do_interrupt_real(CPUX86State *env,
95928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                              int intno, int is_int, int error_code,
96028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                              unsigned int next_eip)
96128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
96228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    SegmentCache *dt;
96328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    target_ulong ptr, ssp;
96428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    int selector;
96528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    uint32_t offset, esp;
96628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    uint32_t old_cs, old_eip;
96728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
96828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* real mode (simpler !) */
96928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    dt = &env->idt;
97028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (intno * 4 + 3 > dt->limit)
97128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0D_GPF, intno * 8 + 2);
97228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    ptr = dt->base + intno * 4;
97328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    offset = cpu_lduw_kernel(env, ptr);
97428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    selector = cpu_lduw_kernel(env, ptr + 2);
97528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    esp = ESP;
97628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    ssp = env->segs[R_SS].base;
97728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (is_int)
97828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        old_eip = next_eip;
97928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    else
98028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        old_eip = env->eip;
98128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    old_cs = env->segs[R_CS].selector;
98228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* XXX: use SS segment size ? */
98328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    PUSHW(ssp, esp, 0xffff, cpu_compute_eflags(env));
98428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    PUSHW(ssp, esp, 0xffff, old_cs);
98528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    PUSHW(ssp, esp, 0xffff, old_eip);
98628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
98728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* update processor state */
98828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    ESP = (ESP & ~0xffff) | (esp & 0xffff);
98928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->eip = offset;
99028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->segs[R_CS].selector = selector;
99128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->segs[R_CS].base = (selector << 4);
99228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->eflags &= ~(IF_MASK | TF_MASK | AC_MASK | RF_MASK);
99328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
99428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
99528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#if defined(CONFIG_USER_ONLY)
99628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner/* fake user mode interrupt */
99728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnerstatic void do_interrupt_user(CPUX86State *env,
99828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                              int intno, int is_int, int error_code,
99928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                              target_ulong next_eip)
100028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
100128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    SegmentCache *dt;
100228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    target_ulong ptr;
100328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    int dpl, cpl, shift;
100428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    uint32_t e2;
100528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
100628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    dt = &env->idt;
100728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (env->hflags & HF_LMA_MASK) {
100828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        shift = 4;
100928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else {
101028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        shift = 3;
101128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
101228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    ptr = dt->base + (intno << shift);
101328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    e2 = cpu_ldl_kernel(env, ptr + 4);
101428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
101528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    dpl = (e2 >> DESC_DPL_SHIFT) & 3;
101628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    cpl = env->hflags & HF_CPL_MASK;
101728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* check privilege if software int */
101828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (is_int && dpl < cpl)
101928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0D_GPF, (intno << shift) + 2);
102028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
102128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* Since we emulate only user space, we cannot do more than
102228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner       exiting the emulation with the suitable exception and error
102328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner       code */
102428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (is_int)
102528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        EIP = next_eip;
102628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
102728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
102828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#else
102928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
103028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnerstatic void handle_even_inj(CPUX86State *env,
103128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                            int intno, int is_int, int error_code,
103228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                            int is_hw, int rm)
103328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
103428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    uint32_t event_inj = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj));
103528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (!(event_inj & SVM_EVTINJ_VALID)) {
103628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            int type;
103728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (is_int)
103828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                    type = SVM_EVTINJ_TYPE_SOFT;
103928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            else
104028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                    type = SVM_EVTINJ_TYPE_EXEPT;
104128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            event_inj = intno | type | SVM_EVTINJ_VALID;
104228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (!rm && exeption_has_error_code(intno)) {
104328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                    event_inj |= SVM_EVTINJ_VALID_ERR;
104428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                    stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj_err), error_code);
104528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            }
104628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj), event_inj);
104728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
104828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
104928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
105028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
105128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner/*
105228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner * Begin execution of an interruption. is_int is TRUE if coming from
105328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner * the int instruction. next_eip is the EIP value AFTER the interrupt
105428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner * instruction. It is only relevant if is_int is TRUE.
105528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner */
105628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnerstatic void do_interrupt_all(CPUX86State *env,
105728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                             int intno, int is_int, int error_code,
105828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                             target_ulong next_eip, int is_hw)
105928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
106028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (qemu_loglevel_mask(CPU_LOG_INT)) {
106128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if ((env->cr[0] & CR0_PE_MASK)) {
106228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            static int count;
106328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            qemu_log("%6d: v=%02x e=%04x i=%d cpl=%d IP=%04x:" TARGET_FMT_lx " pc=" TARGET_FMT_lx " SP=%04x:" TARGET_FMT_lx,
106428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                    count, intno, error_code, is_int,
106528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                    env->hflags & HF_CPL_MASK,
106628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                    env->segs[R_CS].selector, EIP,
106728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                    (int)env->segs[R_CS].base + EIP,
106828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                    env->segs[R_SS].selector, ESP);
106928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (intno == 0x0e) {
107028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                qemu_log(" CR2=" TARGET_FMT_lx, env->cr[2]);
107128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            } else {
107228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                qemu_log(" EAX=" TARGET_FMT_lx, EAX);
107328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            }
107428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            qemu_log("\n");
107528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            log_cpu_state(ENV_GET_CPU(env), X86_DUMP_CCOP);
107628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#if 0
107728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            {
107828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                int i;
107928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                uint8_t *ptr;
108028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                qemu_log("       code=");
108128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                ptr = env->segs[R_CS].base + env->eip;
108228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                for(i = 0; i < 16; i++) {
108328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                    qemu_log(" %02x", ldub(ptr + i));
108428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                }
108528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                qemu_log("\n");
108628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            }
108728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
108828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            count++;
108928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
109028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
109128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (env->cr[0] & CR0_PE_MASK) {
109228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#if !defined(CONFIG_USER_ONLY)
109328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (env->hflags & HF_SVMI_MASK)
109428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            handle_even_inj(env, intno, is_int, error_code, is_hw, 0);
109528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
109628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#ifdef TARGET_X86_64
109728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (env->hflags & HF_LMA_MASK) {
109828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            do_interrupt64(env, intno, is_int, error_code, next_eip, is_hw);
109928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        } else
110028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
110128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        {
110228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            do_interrupt_protected(env, intno, is_int, error_code, next_eip, is_hw);
110328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
110428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else {
110528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#if !defined(CONFIG_USER_ONLY)
110628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (env->hflags & HF_SVMI_MASK)
110728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            handle_even_inj(env, intno, is_int, error_code, is_hw, 1);
110828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
110928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        do_interrupt_real(env, intno, is_int, error_code, next_eip);
111028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
111128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
111228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#if !defined(CONFIG_USER_ONLY)
111328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (env->hflags & HF_SVMI_MASK) {
111428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        uint32_t event_inj = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj));
111528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj), event_inj & ~SVM_EVTINJ_VALID);
111628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
111728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
111828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
111928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
112028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnervoid do_interrupt(CPUX86State *env)
112128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
112228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#if defined(CONFIG_USER_ONLY)
112328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* if user mode only, we simulate a fake exception
112428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner       which will be handled outside the cpu execution
112528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner       loop */
112628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    do_interrupt_user(env,
112728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                      env->exception_index,
112828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                      env->exception_is_int,
112928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                      env->error_code,
113028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                      env->exception_next_eip);
113128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* successfully delivered */
113228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->old_exception = -1;
113328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#else
113428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* simulate a real cpu exception. On i386, it can
113528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner       trigger new exceptions, but we do not handle
113628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner       double or triple faults yet. */
113728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    do_interrupt_all(env,
113828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                     env->exception_index,
113928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                     env->exception_is_int,
114028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                     env->error_code,
114128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                     env->exception_next_eip, 0);
114228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* successfully delivered */
114328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->old_exception = -1;
114428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
114528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
114628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
114728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnervoid do_interrupt_x86_hardirq(CPUX86State *env, int intno, int is_hw)
114828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
114928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    do_interrupt_all(env, intno, 0, 0, 0, is_hw);
115028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
115128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
115228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner/* This should come from sysemu.h - if we could include it here... */
115328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnervoid qemu_system_reset_request(void);
115428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
115528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnervoid helper_enter_level(CPUX86State *env,
115628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                        int level, int data32, target_ulong t1)
115728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
115828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    target_ulong ssp;
115928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    uint32_t esp_mask, esp, ebp;
116028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
116128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    esp_mask = get_sp_mask(env->segs[R_SS].flags);
116228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    ssp = env->segs[R_SS].base;
116328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    ebp = EBP;
116428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    esp = ESP;
116528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (data32) {
116628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* 32 bit */
116728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        esp -= 4;
116828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        while (--level) {
116928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            esp -= 4;
117028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            ebp -= 4;
117128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            cpu_stl_data(env, ssp + (esp & esp_mask),
117228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                         cpu_ldl_data(env, ssp + (ebp & esp_mask)));
117328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
117428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        esp -= 4;
117528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_stl_data(env, ssp + (esp & esp_mask), t1);
117628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else {
117728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* 16 bit */
117828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        esp -= 2;
117928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        while (--level) {
118028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            esp -= 2;
118128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            ebp -= 2;
118228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            cpu_stw_data(env, ssp + (esp & esp_mask),
118328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                         cpu_lduw_data(env, ssp + (ebp & esp_mask)));
118428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
118528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        esp -= 2;
118628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_stw_data(env, ssp + (esp & esp_mask), t1);
118728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
118828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
118928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
119028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#ifdef TARGET_X86_64
119128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnervoid helper_enter64_level(CPUX86State *env,
119228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                          int level, int data64, target_ulong t1)
119328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
119428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    target_ulong esp, ebp;
119528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    ebp = EBP;
119628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    esp = ESP;
119728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
119828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (data64) {
119928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* 64 bit */
120028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        esp -= 8;
120128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        while (--level) {
120228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            esp -= 8;
120328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            ebp -= 8;
120428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            cpu_stq_data(env, esp, cpu_ldq_data(env, ebp));
120528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
120628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        esp -= 8;
120728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_stq_data(env, esp, t1);
120828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else {
120928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* 16 bit */
121028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        esp -= 2;
121128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        while (--level) {
121228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            esp -= 2;
121328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            ebp -= 2;
121428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            cpu_stw_data(env, esp, cpu_lduw_data(env, ebp));
121528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
121628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        esp -= 2;
121728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_stw_data(env, esp, t1);
121828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
121928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
122028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
122128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
122228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnervoid helper_lldt(CPUX86State *env, int selector)
122328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
122428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    SegmentCache *dt;
122528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    uint32_t e1, e2;
122628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    int index, entry_limit;
122728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    target_ulong ptr;
122828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
122928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    selector &= 0xffff;
123028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if ((selector & 0xfffc) == 0) {
123128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* XXX: NULL selector case: invalid LDT */
123228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        env->ldt.base = 0;
123328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        env->ldt.limit = 0;
123428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else {
123528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (selector & 0x4)
123628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
123728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        dt = &env->gdt;
123828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        index = selector & ~7;
123928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#ifdef TARGET_X86_64
124028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (env->hflags & HF_LMA_MASK)
124128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            entry_limit = 15;
124228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        else
124328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
124428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            entry_limit = 7;
124528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if ((index + entry_limit) > dt->limit)
124628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
124728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        ptr = dt->base + index;
124828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        e1 = cpu_ldl_kernel(env, ptr);
124928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        e2 = cpu_ldl_kernel(env, ptr + 4);
125028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2)
125128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
125228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (!(e2 & DESC_P_MASK))
125328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0B_NOSEG, selector & 0xfffc);
125428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#ifdef TARGET_X86_64
125528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (env->hflags & HF_LMA_MASK) {
125628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            uint32_t e3;
125728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            e3 = cpu_ldl_kernel(env, ptr + 8);
125828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            load_seg_cache_raw_dt(&env->ldt, e1, e2);
125928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            env->ldt.base |= (target_ulong)e3 << 32;
126028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        } else
126128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
126228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        {
126328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            load_seg_cache_raw_dt(&env->ldt, e1, e2);
126428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
126528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
126628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->ldt.selector = selector;
126728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
126828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
126928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnervoid helper_ltr(CPUX86State *env, int selector)
127028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
127128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    SegmentCache *dt;
127228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    uint32_t e1, e2;
127328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    int index, type, entry_limit;
127428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    target_ulong ptr;
127528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
127628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    selector &= 0xffff;
127728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if ((selector & 0xfffc) == 0) {
127828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* NULL selector case: invalid TR */
127928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        env->tr.base = 0;
128028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        env->tr.limit = 0;
128128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        env->tr.flags = 0;
128228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else {
128328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (selector & 0x4)
128428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
128528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        dt = &env->gdt;
128628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        index = selector & ~7;
128728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#ifdef TARGET_X86_64
128828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (env->hflags & HF_LMA_MASK)
128928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            entry_limit = 15;
129028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        else
129128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
129228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            entry_limit = 7;
129328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if ((index + entry_limit) > dt->limit)
129428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
129528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        ptr = dt->base + index;
129628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        e1 = cpu_ldl_kernel(env, ptr);
129728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        e2 = cpu_ldl_kernel(env, ptr + 4);
129828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
129928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if ((e2 & DESC_S_MASK) ||
130028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            (type != 1 && type != 9))
130128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
130228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (!(e2 & DESC_P_MASK))
130328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0B_NOSEG, selector & 0xfffc);
130428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#ifdef TARGET_X86_64
130528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (env->hflags & HF_LMA_MASK) {
130628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            uint32_t e3, e4;
130728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            e3 = cpu_ldl_kernel(env, ptr + 8);
130828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            e4 = cpu_ldl_kernel(env, ptr + 12);
130928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if ((e4 >> DESC_TYPE_SHIFT) & 0xf)
131028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
131128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            load_seg_cache_raw_dt(&env->tr, e1, e2);
131228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            env->tr.base |= (target_ulong)e3 << 32;
131328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        } else
131428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
131528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        {
131628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            load_seg_cache_raw_dt(&env->tr, e1, e2);
131728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
131828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        e2 |= DESC_TSS_BUSY_MASK;
131928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_stl_kernel(env, ptr + 4, e2);
132028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
132128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->tr.selector = selector;
132228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
132328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
132428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner/* only works if protected mode and not VM86. seg_reg must be != R_CS */
132528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnervoid helper_load_seg(CPUX86State *env, int seg_reg, int selector)
132628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
132728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    uint32_t e1, e2;
132828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    int cpl, dpl, rpl;
132928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    SegmentCache *dt;
133028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    int index;
133128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    target_ulong ptr;
133228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
133328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    selector &= 0xffff;
133428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    cpl = env->hflags & HF_CPL_MASK;
133528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if ((selector & 0xfffc) == 0) {
133628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* null selector case */
133728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (seg_reg == R_SS
133828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#ifdef TARGET_X86_64
133928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            && (!(env->hflags & HF_CS64_MASK) || cpl == 3)
134028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
134128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            )
134228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0D_GPF, 0);
134328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_x86_load_seg_cache(env, seg_reg, selector, 0, 0, 0);
134428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else {
134528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
134628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (selector & 0x4)
134728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            dt = &env->ldt;
134828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        else
134928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            dt = &env->gdt;
135028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        index = selector & ~7;
135128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if ((index + 7) > dt->limit)
135228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
135328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        ptr = dt->base + index;
135428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        e1 = cpu_ldl_kernel(env, ptr);
135528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        e2 = cpu_ldl_kernel(env, ptr + 4);
135628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
135728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (!(e2 & DESC_S_MASK))
135828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
135928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        rpl = selector & 3;
136028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        dpl = (e2 >> DESC_DPL_SHIFT) & 3;
136128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (seg_reg == R_SS) {
136228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            /* must be writable segment */
136328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK))
136428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
136528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (rpl != cpl || dpl != cpl)
136628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
136728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        } else {
136828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            /* must be readable segment */
136928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if ((e2 & (DESC_CS_MASK | DESC_R_MASK)) == DESC_CS_MASK)
137028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
137128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
137228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (!(e2 & DESC_CS_MASK) || !(e2 & DESC_C_MASK)) {
137328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                /* if not conforming code, test rights */
137428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                if (dpl < cpl || dpl < rpl)
137528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                    raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
137628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            }
137728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
137828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
137928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (!(e2 & DESC_P_MASK)) {
138028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (seg_reg == R_SS)
138128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0C_STACK, selector & 0xfffc);
138228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            else
138328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0B_NOSEG, selector & 0xfffc);
138428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
138528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
138628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* set the access bit if not already set */
138728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (!(e2 & DESC_A_MASK)) {
138828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            e2 |= DESC_A_MASK;
138928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            cpu_stl_kernel(env, ptr + 4, e2);
139028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
139128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
139228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_x86_load_seg_cache(env, seg_reg, selector,
139328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                       get_seg_base(e1, e2),
139428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                       get_seg_limit(e1, e2),
139528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                       e2);
139628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#if 0
139728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        qemu_log("load_seg: sel=0x%04x base=0x%08lx limit=0x%08lx flags=%08x\n",
139828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                selector, (unsigned long)sc->base, sc->limit, sc->flags);
139928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
140028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
140128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
140228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
140328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner/* protected mode jump */
140428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnervoid helper_ljmp_protected(CPUX86State *env,
140528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                           int new_cs, target_ulong new_eip,
140628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                           int next_eip_addend)
140728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
140828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    int gate_cs, type;
140928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    uint32_t e1, e2, cpl, dpl, rpl, limit;
141028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    target_ulong next_eip;
141128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
141228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if ((new_cs & 0xfffc) == 0)
141328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0D_GPF, 0);
141428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (load_segment(env, &e1, &e2, new_cs) != 0)
141528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc);
141628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    cpl = env->hflags & HF_CPL_MASK;
141728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (e2 & DESC_S_MASK) {
141828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (!(e2 & DESC_CS_MASK))
141928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc);
142028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        dpl = (e2 >> DESC_DPL_SHIFT) & 3;
142128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (e2 & DESC_C_MASK) {
142228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            /* conforming code segment */
142328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (dpl > cpl)
142428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc);
142528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        } else {
142628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            /* non conforming code segment */
142728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            rpl = new_cs & 3;
142828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (rpl > cpl)
142928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc);
143028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (dpl != cpl)
143128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc);
143228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
143328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (!(e2 & DESC_P_MASK))
143428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0B_NOSEG, new_cs & 0xfffc);
143528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        limit = get_seg_limit(e1, e2);
143628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (new_eip > limit &&
143728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            !(env->hflags & HF_LMA_MASK) && !(e2 & DESC_L_MASK))
143828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc);
143928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
144028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                       get_seg_base(e1, e2), limit, e2);
144128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        EIP = new_eip;
144228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else {
144328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* jump to call or task gate */
144428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        dpl = (e2 >> DESC_DPL_SHIFT) & 3;
144528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        rpl = new_cs & 3;
144628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpl = env->hflags & HF_CPL_MASK;
144728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
144828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        switch(type) {
144928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        case 1: /* 286 TSS */
145028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        case 9: /* 386 TSS */
145128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        case 5: /* task gate */
145228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (dpl < cpl || dpl < rpl)
145328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc);
145428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            next_eip = env->eip + next_eip_addend;
145528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            switch_tss(env, new_cs, e1, e2, SWITCH_TSS_JMP, next_eip);
145628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            CC_OP = CC_OP_EFLAGS;
145728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            break;
145828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        case 4: /* 286 call gate */
145928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        case 12: /* 386 call gate */
146028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if ((dpl < cpl) || (dpl < rpl))
146128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc);
146228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (!(e2 & DESC_P_MASK))
146328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0B_NOSEG, new_cs & 0xfffc);
146428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            gate_cs = e1 >> 16;
146528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            new_eip = (e1 & 0xffff);
146628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (type == 12)
146728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                new_eip |= (e2 & 0xffff0000);
146828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (load_segment(env, &e1, &e2, gate_cs) != 0)
146928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0D_GPF, gate_cs & 0xfffc);
147028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            dpl = (e2 >> DESC_DPL_SHIFT) & 3;
147128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            /* must be code segment */
147228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (((e2 & (DESC_S_MASK | DESC_CS_MASK)) !=
147328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                 (DESC_S_MASK | DESC_CS_MASK)))
147428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0D_GPF, gate_cs & 0xfffc);
147528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (((e2 & DESC_C_MASK) && (dpl > cpl)) ||
147628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                (!(e2 & DESC_C_MASK) && (dpl != cpl)))
147728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0D_GPF, gate_cs & 0xfffc);
147828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (!(e2 & DESC_P_MASK))
147928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0D_GPF, gate_cs & 0xfffc);
148028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            limit = get_seg_limit(e1, e2);
148128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (new_eip > limit)
148228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0D_GPF, 0);
148328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            cpu_x86_load_seg_cache(env, R_CS, (gate_cs & 0xfffc) | cpl,
148428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                                   get_seg_base(e1, e2), limit, e2);
148528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            EIP = new_eip;
148628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            break;
148728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        default:
148828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc);
148928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            break;
149028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
149128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
149228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
149328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
149428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner/* real mode call */
149528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnervoid helper_lcall_real(CPUX86State *env,
149628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                       int new_cs, target_ulong new_eip1,
149728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                       int shift, int next_eip)
149828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
149928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    int new_eip;
150028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    uint32_t esp, esp_mask;
150128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    target_ulong ssp;
150228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
150328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    new_eip = new_eip1;
150428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    esp = ESP;
150528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    esp_mask = get_sp_mask(env->segs[R_SS].flags);
150628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    ssp = env->segs[R_SS].base;
150728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (shift) {
150828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        PUSHL(ssp, esp, esp_mask, env->segs[R_CS].selector);
150928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        PUSHL(ssp, esp, esp_mask, next_eip);
151028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else {
151128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        PUSHW(ssp, esp, esp_mask, env->segs[R_CS].selector);
151228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        PUSHW(ssp, esp, esp_mask, next_eip);
151328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
151428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
151528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    SET_ESP(esp, esp_mask);
151628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->eip = new_eip;
151728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->segs[R_CS].selector = new_cs;
151828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->segs[R_CS].base = (new_cs << 4);
151928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
152028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
152128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner/* protected mode call */
152228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnervoid helper_lcall_protected(CPUX86State *env,
152328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                            int new_cs, target_ulong new_eip,
152428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                            int shift, int next_eip_addend)
152528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
152628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    int new_stack, i;
152728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    uint32_t e1, e2, cpl, dpl, rpl, selector, offset, param_count;
152828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    uint32_t ss = 0, ss_e1 = 0, ss_e2 = 0, sp, type, ss_dpl, sp_mask;
152928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    uint32_t val, limit, old_sp_mask;
153028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    target_ulong ssp, old_ssp, next_eip;
153128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
153228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    next_eip = env->eip + next_eip_addend;
153328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    LOG_PCALL("lcall %04x:%08x s=%d\n", new_cs, (uint32_t)new_eip, shift);
153428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    LOG_PCALL_STATE(env);
153528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if ((new_cs & 0xfffc) == 0)
153628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0D_GPF, 0);
153728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (load_segment(env, &e1, &e2, new_cs) != 0)
153828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc);
153928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    cpl = env->hflags & HF_CPL_MASK;
154028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    LOG_PCALL("desc=%08x:%08x\n", e1, e2);
154128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (e2 & DESC_S_MASK) {
154228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (!(e2 & DESC_CS_MASK))
154328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc);
154428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        dpl = (e2 >> DESC_DPL_SHIFT) & 3;
154528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (e2 & DESC_C_MASK) {
154628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            /* conforming code segment */
154728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (dpl > cpl)
154828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc);
154928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        } else {
155028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            /* non conforming code segment */
155128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            rpl = new_cs & 3;
155228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (rpl > cpl)
155328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc);
155428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (dpl != cpl)
155528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc);
155628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
155728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (!(e2 & DESC_P_MASK))
155828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0B_NOSEG, new_cs & 0xfffc);
155928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
156028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#ifdef TARGET_X86_64
156128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* XXX: check 16/32 bit cases in long mode */
156228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (shift == 2) {
156328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            target_ulong rsp;
156428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            /* 64 bit case */
156528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            rsp = ESP;
156628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            PUSHQ(rsp, env->segs[R_CS].selector);
156728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            PUSHQ(rsp, next_eip);
156828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            /* from this point, not restartable */
156928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            ESP = rsp;
157028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
157128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                                   get_seg_base(e1, e2),
157228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                                   get_seg_limit(e1, e2), e2);
157328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            EIP = new_eip;
157428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        } else
157528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
157628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        {
157728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            sp = ESP;
157828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            sp_mask = get_sp_mask(env->segs[R_SS].flags);
157928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            ssp = env->segs[R_SS].base;
158028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (shift) {
158128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                PUSHL(ssp, sp, sp_mask, env->segs[R_CS].selector);
158228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                PUSHL(ssp, sp, sp_mask, next_eip);
158328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            } else {
158428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                PUSHW(ssp, sp, sp_mask, env->segs[R_CS].selector);
158528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                PUSHW(ssp, sp, sp_mask, next_eip);
158628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            }
158728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
158828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            limit = get_seg_limit(e1, e2);
158928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (new_eip > limit)
159028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc);
159128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            /* from this point, not restartable */
159228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            SET_ESP(sp, sp_mask);
159328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
159428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                                   get_seg_base(e1, e2), limit, e2);
159528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            EIP = new_eip;
159628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
159728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else {
159828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* check gate type */
159928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
160028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        dpl = (e2 >> DESC_DPL_SHIFT) & 3;
160128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        rpl = new_cs & 3;
160228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        switch(type) {
160328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        case 1: /* available 286 TSS */
160428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        case 9: /* available 386 TSS */
160528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        case 5: /* task gate */
160628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (dpl < cpl || dpl < rpl)
160728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc);
160828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            switch_tss(env, new_cs, e1, e2, SWITCH_TSS_CALL, next_eip);
160928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            CC_OP = CC_OP_EFLAGS;
161028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            return;
161128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        case 4: /* 286 call gate */
161228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        case 12: /* 386 call gate */
161328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            break;
161428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        default:
161528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc);
161628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            break;
161728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
161828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        shift = type >> 3;
161928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
162028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (dpl < cpl || dpl < rpl)
162128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc);
162228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* check valid bit */
162328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (!(e2 & DESC_P_MASK))
162428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0B_NOSEG,  new_cs & 0xfffc);
162528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        selector = e1 >> 16;
162628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff);
162728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        param_count = e2 & 0x1f;
162828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if ((selector & 0xfffc) == 0)
162928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0D_GPF, 0);
163028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
163128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (load_segment(env, &e1, &e2, selector) != 0)
163228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
163328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK)))
163428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
163528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        dpl = (e2 >> DESC_DPL_SHIFT) & 3;
163628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (dpl > cpl)
163728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);
163828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (!(e2 & DESC_P_MASK))
163928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0B_NOSEG, selector & 0xfffc);
164028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
164128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (!(e2 & DESC_C_MASK) && dpl < cpl) {
164228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            /* to inner privilege */
164328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            get_ss_esp_from_tss(env, &ss, &sp, dpl);
164428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            LOG_PCALL("new ss:esp=%04x:%08x param_count=%d ESP=" TARGET_FMT_lx "\n",
164528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                        ss, sp, param_count, ESP);
164628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if ((ss & 0xfffc) == 0)
164728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc);
164828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if ((ss & 3) != dpl)
164928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc);
165028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (load_segment(env, &ss_e1, &ss_e2, ss) != 0)
165128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc);
165228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            ss_dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3;
165328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (ss_dpl != dpl)
165428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc);
165528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (!(ss_e2 & DESC_S_MASK) ||
165628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                (ss_e2 & DESC_CS_MASK) ||
165728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                !(ss_e2 & DESC_W_MASK))
165828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc);
165928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (!(ss_e2 & DESC_P_MASK))
166028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc);
166128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
166228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            //            push_size = ((param_count * 2) + 8) << shift;
166328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
166428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            old_sp_mask = get_sp_mask(env->segs[R_SS].flags);
166528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            old_ssp = env->segs[R_SS].base;
166628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
166728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            sp_mask = get_sp_mask(ss_e2);
166828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            ssp = get_seg_base(ss_e1, ss_e2);
166928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (shift) {
167028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                PUSHL(ssp, sp, sp_mask, env->segs[R_SS].selector);
167128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                PUSHL(ssp, sp, sp_mask, ESP);
167228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                for(i = param_count - 1; i >= 0; i--) {
167328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                    val = cpu_ldl_kernel(env, old_ssp + ((ESP + i * 4) & old_sp_mask));
167428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                    PUSHL(ssp, sp, sp_mask, val);
167528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                }
167628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            } else {
167728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                PUSHW(ssp, sp, sp_mask, env->segs[R_SS].selector);
167828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                PUSHW(ssp, sp, sp_mask, ESP);
167928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                for(i = param_count - 1; i >= 0; i--) {
168028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                    val = cpu_lduw_kernel(env, old_ssp + ((ESP + i * 2) & old_sp_mask));
168128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                    PUSHW(ssp, sp, sp_mask, val);
168228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                }
168328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            }
168428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            new_stack = 1;
168528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        } else {
168628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            /* to same privilege */
168728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            sp = ESP;
168828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            sp_mask = get_sp_mask(env->segs[R_SS].flags);
168928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            ssp = env->segs[R_SS].base;
169028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            //            push_size = (4 << shift);
169128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            new_stack = 0;
169228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
169328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
169428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (shift) {
169528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            PUSHL(ssp, sp, sp_mask, env->segs[R_CS].selector);
169628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            PUSHL(ssp, sp, sp_mask, next_eip);
169728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        } else {
169828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            PUSHW(ssp, sp, sp_mask, env->segs[R_CS].selector);
169928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            PUSHW(ssp, sp, sp_mask, next_eip);
170028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
170128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
170228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* from this point, not restartable */
170328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
170428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (new_stack) {
170528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            ss = (ss & ~3) | dpl;
170628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            cpu_x86_load_seg_cache(env, R_SS, ss,
170728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                                   ssp,
170828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                                   get_seg_limit(ss_e1, ss_e2),
170928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                                   ss_e2);
171028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
171128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
171228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        selector = (selector & ~3) | dpl;
171328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_x86_load_seg_cache(env, R_CS, selector,
171428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                       get_seg_base(e1, e2),
171528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                       get_seg_limit(e1, e2),
171628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                       e2);
171728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_x86_set_cpl(env, dpl);
171828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        SET_ESP(sp, sp_mask);
171928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        EIP = offset;
172028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
172128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
172228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
172328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner/* real and vm86 mode iret */
172428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnervoid helper_iret_real(CPUX86State *env, int shift)
172528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
172628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    uint32_t sp, new_cs, new_eip, new_eflags, sp_mask;
172728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    target_ulong ssp;
172828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    int eflags_mask;
172928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
173028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    sp_mask = 0xffff; /* XXXX: use SS segment size ? */
173128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    sp = ESP;
173228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    ssp = env->segs[R_SS].base;
173328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (shift == 1) {
173428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* 32 bits */
173528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        POPL(ssp, sp, sp_mask, new_eip);
173628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        POPL(ssp, sp, sp_mask, new_cs);
173728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        new_cs &= 0xffff;
173828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        POPL(ssp, sp, sp_mask, new_eflags);
173928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else {
174028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* 16 bits */
174128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        POPW(ssp, sp, sp_mask, new_eip);
174228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        POPW(ssp, sp, sp_mask, new_cs);
174328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        POPW(ssp, sp, sp_mask, new_eflags);
174428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
174528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    ESP = (ESP & ~sp_mask) | (sp & sp_mask);
174628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->segs[R_CS].selector = new_cs;
174728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->segs[R_CS].base = (new_cs << 4);
174828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->eip = new_eip;
174928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (env->eflags & VM_MASK)
175028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        eflags_mask = TF_MASK | AC_MASK | ID_MASK | IF_MASK | RF_MASK | NT_MASK;
175128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    else
175228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        eflags_mask = TF_MASK | AC_MASK | ID_MASK | IF_MASK | IOPL_MASK | RF_MASK | NT_MASK;
175328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (shift == 0)
175428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        eflags_mask &= 0xffff;
175528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    cpu_load_eflags(env, new_eflags, eflags_mask);
175628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->hflags2 &= ~HF2_NMI_MASK;
175728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
175828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
175928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnerstatic inline void validate_seg(CPUX86State *env, int seg_reg, int cpl)
176028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
176128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    int dpl;
176228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    uint32_t e2;
176328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
176428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* XXX: on x86_64, we do not want to nullify FS and GS because
176528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner       they may still contain a valid base. I would be interested to
176628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner       know how a real x86_64 CPU behaves */
176728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if ((seg_reg == R_FS || seg_reg == R_GS) &&
176828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        (env->segs[seg_reg].selector & 0xfffc) == 0)
176928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        return;
177028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
177128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    e2 = env->segs[seg_reg].flags;
177228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    dpl = (e2 >> DESC_DPL_SHIFT) & 3;
177328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (!(e2 & DESC_CS_MASK) || !(e2 & DESC_C_MASK)) {
177428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* data or non conforming code segment */
177528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (dpl < cpl) {
177628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            cpu_x86_load_seg_cache(env, seg_reg, 0, 0, 0, 0);
177728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
177828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
177928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
178028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
178128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner/* protected mode iret */
178228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnerstatic inline void helper_ret_protected(CPUX86State *env,
178328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                                        int shift, int is_iret, int addend)
178428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
178528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    uint32_t new_cs, new_eflags, new_ss;
178628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    uint32_t new_es, new_ds, new_fs, new_gs;
178728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    uint32_t e1, e2, ss_e1, ss_e2;
178828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    int cpl, dpl, rpl, eflags_mask, iopl;
178928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    target_ulong ssp, sp, new_eip, new_esp, sp_mask;
179028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
179128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#ifdef TARGET_X86_64
179228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (shift == 2)
179328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        sp_mask = -1;
179428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    else
179528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
179628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        sp_mask = get_sp_mask(env->segs[R_SS].flags);
179728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    sp = ESP;
179828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    ssp = env->segs[R_SS].base;
179928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    new_eflags = 0; /* avoid warning */
180028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#ifdef TARGET_X86_64
180128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (shift == 2) {
180228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        POPQ(sp, new_eip);
180328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        POPQ(sp, new_cs);
180428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        new_cs &= 0xffff;
180528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (is_iret) {
180628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            POPQ(sp, new_eflags);
180728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
180828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else
180928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
181028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (shift == 1) {
181128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* 32 bits */
181228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        POPL(ssp, sp, sp_mask, new_eip);
181328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        POPL(ssp, sp, sp_mask, new_cs);
181428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        new_cs &= 0xffff;
181528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (is_iret) {
181628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            POPL(ssp, sp, sp_mask, new_eflags);
181728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (new_eflags & VM_MASK)
181828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                goto return_to_vm86;
181928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
182028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else {
182128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* 16 bits */
182228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        POPW(ssp, sp, sp_mask, new_eip);
182328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        POPW(ssp, sp, sp_mask, new_cs);
182428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (is_iret)
182528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            POPW(ssp, sp, sp_mask, new_eflags);
182628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
182728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    LOG_PCALL("lret new %04x:" TARGET_FMT_lx " s=%d addend=0x%x\n",
182828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner              new_cs, new_eip, shift, addend);
182928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    LOG_PCALL_STATE(env);
183028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if ((new_cs & 0xfffc) == 0)
183128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc);
183228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (load_segment(env, &e1, &e2, new_cs) != 0)
183328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc);
183428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (!(e2 & DESC_S_MASK) ||
183528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        !(e2 & DESC_CS_MASK))
183628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc);
183728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    cpl = env->hflags & HF_CPL_MASK;
183828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    rpl = new_cs & 3;
183928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (rpl < cpl)
184028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc);
184128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    dpl = (e2 >> DESC_DPL_SHIFT) & 3;
184228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (e2 & DESC_C_MASK) {
184328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (dpl > rpl)
184428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc);
184528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else {
184628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (dpl != rpl)
184728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc);
184828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
184928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (!(e2 & DESC_P_MASK))
185028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0B_NOSEG, new_cs & 0xfffc);
185128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
185228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    sp += addend;
185328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (rpl == cpl && (!(env->hflags & HF_CS64_MASK) ||
185428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                       ((env->hflags & HF_CS64_MASK) && !is_iret))) {
185528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* return to same privilege level */
185628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_x86_load_seg_cache(env, R_CS, new_cs,
185728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                       get_seg_base(e1, e2),
185828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                       get_seg_limit(e1, e2),
185928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                       e2);
186028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else {
186128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* return to different privilege level */
186228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#ifdef TARGET_X86_64
186328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (shift == 2) {
186428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            POPQ(sp, new_esp);
186528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            POPQ(sp, new_ss);
186628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            new_ss &= 0xffff;
186728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        } else
186828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
186928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (shift == 1) {
187028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            /* 32 bits */
187128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            POPL(ssp, sp, sp_mask, new_esp);
187228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            POPL(ssp, sp, sp_mask, new_ss);
187328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            new_ss &= 0xffff;
187428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        } else {
187528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            /* 16 bits */
187628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            POPW(ssp, sp, sp_mask, new_esp);
187728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            POPW(ssp, sp, sp_mask, new_ss);
187828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
187928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        LOG_PCALL("new ss:esp=%04x:" TARGET_FMT_lx "\n",
188028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                    new_ss, new_esp);
188128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if ((new_ss & 0xfffc) == 0) {
188228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#ifdef TARGET_X86_64
188328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            /* NULL ss is allowed in long mode if cpl != 3*/
188428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            /* XXX: test CS64 ? */
188528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if ((env->hflags & HF_LMA_MASK) && rpl != 3) {
188628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                cpu_x86_load_seg_cache(env, R_SS, new_ss,
188728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                                       0, 0xffffffff,
188828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                                       DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
188928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                                       DESC_S_MASK | (rpl << DESC_DPL_SHIFT) |
189028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                                       DESC_W_MASK | DESC_A_MASK);
189128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                ss_e2 = DESC_B_MASK; /* XXX: should not be needed ? */
189228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            } else
189328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
189428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            {
189528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0D_GPF, 0);
189628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            }
189728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        } else {
189828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if ((new_ss & 3) != rpl)
189928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0D_GPF, new_ss & 0xfffc);
190028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (load_segment(env, &ss_e1, &ss_e2, new_ss) != 0)
190128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0D_GPF, new_ss & 0xfffc);
190228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (!(ss_e2 & DESC_S_MASK) ||
190328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                (ss_e2 & DESC_CS_MASK) ||
190428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                !(ss_e2 & DESC_W_MASK))
190528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0D_GPF, new_ss & 0xfffc);
190628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3;
190728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (dpl != rpl)
190828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0D_GPF, new_ss & 0xfffc);
190928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (!(ss_e2 & DESC_P_MASK))
191028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                raise_exception_err(env, EXCP0B_NOSEG, new_ss & 0xfffc);
191128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            cpu_x86_load_seg_cache(env, R_SS, new_ss,
191228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                                   get_seg_base(ss_e1, ss_e2),
191328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                                   get_seg_limit(ss_e1, ss_e2),
191428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                                   ss_e2);
191528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
191628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
191728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_x86_load_seg_cache(env, R_CS, new_cs,
191828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                       get_seg_base(e1, e2),
191928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                       get_seg_limit(e1, e2),
192028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                       e2);
192128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_x86_set_cpl(env, rpl);
192228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        sp = new_esp;
192328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#ifdef TARGET_X86_64
192428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (env->hflags & HF_CS64_MASK)
192528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            sp_mask = -1;
192628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        else
192728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
192828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            sp_mask = get_sp_mask(ss_e2);
192928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
193028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* validate data segments */
193128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        validate_seg(env, R_ES, rpl);
193228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        validate_seg(env, R_DS, rpl);
193328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        validate_seg(env, R_FS, rpl);
193428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        validate_seg(env, R_GS, rpl);
193528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
193628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        sp += addend;
193728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
193828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    SET_ESP(sp, sp_mask);
193928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->eip = new_eip;
194028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (is_iret) {
194128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* NOTE: 'cpl' is the _old_ CPL */
194228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        eflags_mask = TF_MASK | AC_MASK | ID_MASK | RF_MASK | NT_MASK;
194328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (cpl == 0)
194428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            eflags_mask |= IOPL_MASK;
194528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        iopl = (env->eflags >> IOPL_SHIFT) & 3;
194628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (cpl <= iopl)
194728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            eflags_mask |= IF_MASK;
194828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (shift == 0)
194928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            eflags_mask &= 0xffff;
195028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_load_eflags(env, new_eflags, eflags_mask);
195128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
195228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    return;
195328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
195428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner return_to_vm86:
195528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    POPL(ssp, sp, sp_mask, new_esp);
195628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    POPL(ssp, sp, sp_mask, new_ss);
195728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    POPL(ssp, sp, sp_mask, new_es);
195828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    POPL(ssp, sp, sp_mask, new_ds);
195928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    POPL(ssp, sp, sp_mask, new_fs);
196028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    POPL(ssp, sp, sp_mask, new_gs);
196128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
196228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* modify processor state */
196328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    cpu_load_eflags(env, new_eflags, TF_MASK | AC_MASK | ID_MASK |
196428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                IF_MASK | IOPL_MASK | VM_MASK | NT_MASK | VIF_MASK | VIP_MASK);
196528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    load_seg_vm(env, R_CS, new_cs & 0xffff);
196628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    cpu_x86_set_cpl(env, 3);
196728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    load_seg_vm(env, R_SS, new_ss & 0xffff);
196828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    load_seg_vm(env, R_ES, new_es & 0xffff);
196928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    load_seg_vm(env, R_DS, new_ds & 0xffff);
197028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    load_seg_vm(env, R_FS, new_fs & 0xffff);
197128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    load_seg_vm(env, R_GS, new_gs & 0xffff);
197228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
197328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->eip = new_eip & 0xffff;
197428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    ESP = new_esp;
197528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
197628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
197728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnervoid helper_iret_protected(CPUX86State *env, int shift, int next_eip)
197828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
197928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    int tss_selector, type;
198028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    uint32_t e1, e2;
198128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
198228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    /* specific case for TSS */
198328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (env->eflags & NT_MASK) {
198428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#ifdef TARGET_X86_64
198528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (env->hflags & HF_LMA_MASK)
198628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0D_GPF, 0);
198728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
198828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        tss_selector = cpu_lduw_kernel(env, env->tr.base + 0);
198928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (tss_selector & 4)
199028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0A_TSS, tss_selector & 0xfffc);
199128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (load_segment(env, &e1, &e2, tss_selector) != 0)
199228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0A_TSS, tss_selector & 0xfffc);
199328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        type = (e2 >> DESC_TYPE_SHIFT) & 0x17;
199428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        /* NOTE: we check both segment and busy TSS */
199528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (type != 3)
199628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            raise_exception_err(env, EXCP0A_TSS, tss_selector & 0xfffc);
199728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        switch_tss(env, tss_selector, e1, e2, SWITCH_TSS_IRET, next_eip);
199828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else {
199928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        helper_ret_protected(env, shift, 1, 0);
200028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
200128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->hflags2 &= ~HF2_NMI_MASK;
200228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
200328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
200428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnervoid helper_lret_protected(CPUX86State *env, int shift, int addend)
200528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
200628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    helper_ret_protected(env, shift, 0, addend);
200728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
200828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
200928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnervoid helper_sysenter(CPUX86State *env)
201028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
201128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (env->sysenter_cs == 0) {
201228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0D_GPF, 0);
201328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
201428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    env->eflags &= ~(VM_MASK | IF_MASK | RF_MASK);
201528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    cpu_x86_set_cpl(env, 0);
201628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
201728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#ifdef TARGET_X86_64
201828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (env->hflags & HF_LMA_MASK) {
201928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_x86_load_seg_cache(env, R_CS, env->sysenter_cs & 0xfffc,
202028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               0, 0xffffffff,
202128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
202228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_S_MASK |
202328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | DESC_L_MASK);
202428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else
202528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
202628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    {
202728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_x86_load_seg_cache(env, R_CS, env->sysenter_cs & 0xfffc,
202828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               0, 0xffffffff,
202928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
203028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_S_MASK |
203128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
203228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
203328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    cpu_x86_load_seg_cache(env, R_SS, (env->sysenter_cs + 8) & 0xfffc,
203428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                           0, 0xffffffff,
203528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                           DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
203628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                           DESC_S_MASK |
203728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                           DESC_W_MASK | DESC_A_MASK);
203828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    ESP = env->sysenter_esp;
203928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    EIP = env->sysenter_eip;
204028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
204128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
204228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnervoid helper_sysexit(CPUX86State *env, int dflag)
204328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
204428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    int cpl;
204528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
204628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    cpl = env->hflags & HF_CPL_MASK;
204728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (env->sysenter_cs == 0 || cpl != 0) {
204828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        raise_exception_err(env, EXCP0D_GPF, 0);
204928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
205028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    cpu_x86_set_cpl(env, 3);
205128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#ifdef TARGET_X86_64
205228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (dflag == 2) {
205328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_x86_load_seg_cache(env, R_CS, ((env->sysenter_cs + 32) & 0xfffc) | 3,
205428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               0, 0xffffffff,
205528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
205628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
205728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | DESC_L_MASK);
205828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_x86_load_seg_cache(env, R_SS, ((env->sysenter_cs + 40) & 0xfffc) | 3,
205928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               0, 0xffffffff,
206028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
206128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
206228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_W_MASK | DESC_A_MASK);
206328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else
206428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner#endif
206528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    {
206628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_x86_load_seg_cache(env, R_CS, ((env->sysenter_cs + 16) & 0xfffc) | 3,
206728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               0, 0xffffffff,
206828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
206928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
207028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
207128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        cpu_x86_load_seg_cache(env, R_SS, ((env->sysenter_cs + 24) & 0xfffc) | 3,
207228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               0, 0xffffffff,
207328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
207428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_S_MASK | (3 << DESC_DPL_SHIFT) |
207528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                               DESC_W_MASK | DESC_A_MASK);
207628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
207728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    ESP = ECX;
207828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    EIP = EDX;
207928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
208028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
208128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnertarget_ulong helper_lsl(CPUX86State *env, target_ulong selector1)
208228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
208328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    unsigned int limit;
208428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    uint32_t e1, e2, eflags, selector;
208528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    int rpl, dpl, cpl, type;
208628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
208728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    selector = selector1 & 0xffff;
208828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    eflags = helper_cc_compute_all(env, CC_OP);
208928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if ((selector & 0xfffc) == 0)
209028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        goto fail;
209128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (load_segment(env, &e1, &e2, selector) != 0)
209228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        goto fail;
209328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    rpl = selector & 3;
209428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    dpl = (e2 >> DESC_DPL_SHIFT) & 3;
209528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    cpl = env->hflags & HF_CPL_MASK;
209628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (e2 & DESC_S_MASK) {
209728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if ((e2 & DESC_CS_MASK) && (e2 & DESC_C_MASK)) {
209828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            /* conforming */
209928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        } else {
210028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (dpl < cpl || dpl < rpl)
210128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                goto fail;
210228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
210328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else {
210428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
210528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        switch(type) {
210628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        case 1:
210728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        case 2:
210828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        case 3:
210928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        case 9:
211028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        case 11:
211128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            break;
211228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        default:
211328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            goto fail;
211428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
211528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (dpl < cpl || dpl < rpl) {
211628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        fail:
211728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            CC_SRC = eflags & ~CC_Z;
211828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            return 0;
211928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
212028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
212128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    limit = get_seg_limit(e1, e2);
212228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    CC_SRC = eflags | CC_Z;
212328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    return limit;
212428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
212528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
212628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnertarget_ulong helper_lar(CPUX86State *env, target_ulong selector1)
212728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
212828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    uint32_t e1, e2, eflags, selector;
212928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    int rpl, dpl, cpl, type;
213028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
213128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    selector = selector1 & 0xffff;
213228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    eflags = helper_cc_compute_all(env, CC_OP);
213328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if ((selector & 0xfffc) == 0)
213428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        goto fail;
213528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (load_segment(env, &e1, &e2, selector) != 0)
213628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        goto fail;
213728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    rpl = selector & 3;
213828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    dpl = (e2 >> DESC_DPL_SHIFT) & 3;
213928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    cpl = env->hflags & HF_CPL_MASK;
214028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (e2 & DESC_S_MASK) {
214128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if ((e2 & DESC_CS_MASK) && (e2 & DESC_C_MASK)) {
214228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            /* conforming */
214328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        } else {
214428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (dpl < cpl || dpl < rpl)
214528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                goto fail;
214628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
214728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else {
214828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        type = (e2 >> DESC_TYPE_SHIFT) & 0xf;
214928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        switch(type) {
215028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        case 1:
215128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        case 2:
215228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        case 3:
215328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        case 4:
215428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        case 5:
215528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        case 9:
215628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        case 11:
215728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        case 12:
215828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            break;
215928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        default:
216028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            goto fail;
216128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
216228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (dpl < cpl || dpl < rpl) {
216328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        fail:
216428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            CC_SRC = eflags & ~CC_Z;
216528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            return 0;
216628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
216728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
216828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    CC_SRC = eflags | CC_Z;
216928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    return e2 & 0x00f0ff00;
217028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
217128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
217228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnervoid helper_verr(CPUX86State *env, target_ulong selector1)
217328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
217428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    uint32_t e1, e2, eflags, selector;
217528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    int rpl, dpl, cpl;
217628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
217728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    selector = selector1 & 0xffff;
217828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    eflags = helper_cc_compute_all(env, CC_OP);
217928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if ((selector & 0xfffc) == 0)
218028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        goto fail;
218128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (load_segment(env, &e1, &e2, selector) != 0)
218228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        goto fail;
218328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (!(e2 & DESC_S_MASK))
218428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        goto fail;
218528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    rpl = selector & 3;
218628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    dpl = (e2 >> DESC_DPL_SHIFT) & 3;
218728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    cpl = env->hflags & HF_CPL_MASK;
218828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (e2 & DESC_CS_MASK) {
218928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (!(e2 & DESC_R_MASK))
219028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            goto fail;
219128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (!(e2 & DESC_C_MASK)) {
219228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            if (dpl < cpl || dpl < rpl)
219328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner                goto fail;
219428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
219528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else {
219628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (dpl < cpl || dpl < rpl) {
219728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        fail:
219828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            CC_SRC = eflags & ~CC_Z;
219928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            return;
220028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
220128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
220228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    CC_SRC = eflags | CC_Z;
220328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
220428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
220528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turnervoid helper_verw(CPUX86State *env, target_ulong selector1)
220628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner{
220728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    uint32_t e1, e2, eflags, selector;
220828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    int rpl, dpl, cpl;
220928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner
221028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    selector = selector1 & 0xffff;
221128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    eflags = helper_cc_compute_all(env, CC_OP);
221228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if ((selector & 0xfffc) == 0)
221328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        goto fail;
221428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (load_segment(env, &e1, &e2, selector) != 0)
221528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        goto fail;
221628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (!(e2 & DESC_S_MASK))
221728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        goto fail;
221828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    rpl = selector & 3;
221928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    dpl = (e2 >> DESC_DPL_SHIFT) & 3;
222028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    cpl = env->hflags & HF_CPL_MASK;
222128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    if (e2 & DESC_CS_MASK) {
222228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        goto fail;
222328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    } else {
222428f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (dpl < cpl || dpl < rpl)
222528f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            goto fail;
222628f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        if (!(e2 & DESC_W_MASK)) {
222728f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        fail:
222828f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            CC_SRC = eflags & ~CC_Z;
222928f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner            return;
223028f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner        }
223128f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    }
223228f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner    CC_SRC = eflags | CC_Z;
223328f41828f754f73cf3b48aa6476f4a1856c96b92David 'Digit' Turner}
2234