static void do_interrupt64(CPUX86State *env, int intno, int is_int,

                           int error_code, target_ulong next_eip, int is_hw)

{

    SegmentCache *dt;

    target_ulong ptr;

    int type, dpl, selector, cpl, ist;

    int has_error_code, new_stack;

    uint32_t e1, e2, e3, ss;

    target_ulong old_eip, esp, offset;



    has_error_code = 0;

    if (!is_int && !is_hw) {

        has_error_code = exception_has_error_code(intno);

    }

    if (is_int) {

        old_eip = next_eip;

    } else {

        old_eip = env->eip;

    }



    dt = &env->idt;

    if (intno * 16 + 15 > dt->limit) {

        raise_exception_err(env, EXCP0D_GPF, intno * 16 + 2);

    }

    ptr = dt->base + intno * 16;

    e1 = cpu_ldl_kernel(env, ptr);

    e2 = cpu_ldl_kernel(env, ptr + 4);

    e3 = cpu_ldl_kernel(env, ptr + 8);

    /* check gate type */

    type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;

    switch (type) {

    case 14: /* 386 interrupt gate */

    case 15: /* 386 trap gate */

        break;

    default:

        raise_exception_err(env, EXCP0D_GPF, intno * 16 + 2);

        break;

    }

    dpl = (e2 >> DESC_DPL_SHIFT) & 3;

    cpl = env->hflags & HF_CPL_MASK;

    /* check privilege if software int */

    if (is_int && dpl < cpl) {

        raise_exception_err(env, EXCP0D_GPF, intno * 16 + 2);

    }

    /* check valid bit */

    if (!(e2 & DESC_P_MASK)) {

        raise_exception_err(env, EXCP0B_NOSEG, intno * 16 + 2);

    }

    selector = e1 >> 16;

    offset = ((target_ulong)e3 << 32) | (e2 & 0xffff0000) | (e1 & 0x0000ffff);

    ist = e2 & 7;

    if ((selector & 0xfffc) == 0) {

        raise_exception_err(env, EXCP0D_GPF, 0);

    }



    if (load_segment(env, &e1, &e2, selector) != 0) {

        raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);

    }

    if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK))) {

        raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);

    }

    dpl = (e2 >> DESC_DPL_SHIFT) & 3;

    if (dpl > cpl) {

        raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);

    }

    if (!(e2 & DESC_P_MASK)) {

        raise_exception_err(env, EXCP0B_NOSEG, selector & 0xfffc);

    }

    if (!(e2 & DESC_L_MASK) || (e2 & DESC_B_MASK)) {

        raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);

    }

    if ((!(e2 & DESC_C_MASK) && dpl < cpl) || ist != 0) {

        /* to inner privilege */

        if (ist != 0) {

            esp = get_rsp_from_tss(env, ist + 3);

        } else {

            esp = get_rsp_from_tss(env, dpl);

        }

        esp &= ~0xfLL; /* align stack */

        ss = 0;

        new_stack = 1;

    } else if ((e2 & DESC_C_MASK) || dpl == cpl) {

        /* to same privilege */

        if (env->eflags & VM_MASK) {

            raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);

        }

        new_stack = 0;

        if (ist != 0) {

            esp = get_rsp_from_tss(env, ist + 3);

        } else {

            esp = env->regs[R_ESP];

        }

        esp &= ~0xfLL; /* align stack */

        dpl = cpl;

    } else {

        raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc);

        new_stack = 0; /* avoid warning */

        esp = 0; /* avoid warning */

    }



    PUSHQ(esp, env->segs[R_SS].selector);

    PUSHQ(esp, env->regs[R_ESP]);

    PUSHQ(esp, cpu_compute_eflags(env));

    PUSHQ(esp, env->segs[R_CS].selector);

    PUSHQ(esp, old_eip);

    if (has_error_code) {

        PUSHQ(esp, error_code);

    }



    /* interrupt gate clear IF mask */

    if ((type & 1) == 0) {

        env->eflags &= ~IF_MASK;

    }

    env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK);



    if (new_stack) {

        ss = 0 | dpl;

        cpu_x86_load_seg_cache(env, R_SS, ss, 0, 0, 0);

    }

    env->regs[R_ESP] = esp;



    selector = (selector & ~3) | dpl;

    cpu_x86_load_seg_cache(env, R_CS, selector,

                   get_seg_base(e1, e2),

                   get_seg_limit(e1, e2),

                   e2);

    env->eip = offset;

}
