diff options
| author | fishsoupisgood <github@madingley.org> | 2019-04-29 01:17:54 +0100 | 
|---|---|---|
| committer | fishsoupisgood <github@madingley.org> | 2019-05-27 03:43:43 +0100 | 
| commit | 3f2546b2ef55b661fd8dd69682b38992225e86f6 (patch) | |
| tree | 65ca85f13617aee1dce474596800950f266a456c /target-i386 | |
| download | qemu-master.tar.gz qemu-master.tar.bz2 qemu-master.zip  | |
Diffstat (limited to 'target-i386')
29 files changed, 29346 insertions, 0 deletions
diff --git a/target-i386/Makefile.objs b/target-i386/Makefile.objs new file mode 100644 index 00000000..7a1df2c9 --- /dev/null +++ b/target-i386/Makefile.objs @@ -0,0 +1,7 @@ +obj-y += translate.o helper.o cpu.o +obj-y += excp_helper.o fpu_helper.o cc_helper.o int_helper.o svm_helper.o +obj-y += smm_helper.o misc_helper.o mem_helper.o seg_helper.o +obj-y += gdbstub.o +obj-$(CONFIG_SOFTMMU) += machine.o arch_memory_mapping.o arch_dump.o +obj-$(CONFIG_KVM) += kvm.o +obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o diff --git a/target-i386/TODO b/target-i386/TODO new file mode 100644 index 00000000..a8d69cf8 --- /dev/null +++ b/target-i386/TODO @@ -0,0 +1,31 @@ +Correctness issues: + +- some eflags manipulation incorrectly reset the bit 0x2. +- SVM: test, cpu save/restore, SMM save/restore.  +- x86_64: lcall/ljmp intel/amd differences ? +- better code fetch (different exception handling + CS.limit support) +- user/kernel PUSHL/POPL in helper.c +- add missing cpuid tests +- return UD exception if LOCK prefix incorrectly used +- test ldt limit < 7 ? +- fix some 16 bit sp push/pop overflow (pusha/popa, lcall lret) +- full support of segment limit/rights  +- full x87 exception support +- improve x87 bit exactness (use bochs code ?) +- DRx register support +- CR0.AC emulation +- SSE alignment checks + +Optimizations/Features: + +- add SVM nested paging support +- add VMX support +- add AVX support +- add SSE5 support +- fxsave/fxrstor AMD extensions +- improve monitor/mwait support +- faster EFLAGS update: consider SZAP, C, O can be updated separately +  with a bit field in CC_OP and more state variables. +- evaluate x87 stack pointer statically +- find a way to avoid translating several time the same TB if CR0.TS +  is set or not. diff --git a/target-i386/arch_dump.c b/target-i386/arch_dump.c new file mode 100644 index 00000000..eccd8031 --- /dev/null +++ b/target-i386/arch_dump.c @@ -0,0 +1,452 @@ +/* + * i386 memory mapping + * + * Copyright Fujitsu, Corp. 2011, 2012 + * + * Authors: + *     Wen Congyang <wency@cn.fujitsu.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "cpu.h" +#include "exec/cpu-all.h" +#include "sysemu/dump.h" +#include "elf.h" +#include "sysemu/memory_mapping.h" + +#ifdef TARGET_X86_64 +typedef struct { +    target_ulong r15, r14, r13, r12, rbp, rbx, r11, r10; +    target_ulong r9, r8, rax, rcx, rdx, rsi, rdi, orig_rax; +    target_ulong rip, cs, eflags; +    target_ulong rsp, ss; +    target_ulong fs_base, gs_base; +    target_ulong ds, es, fs, gs; +} x86_64_user_regs_struct; + +typedef struct { +    char pad1[32]; +    uint32_t pid; +    char pad2[76]; +    x86_64_user_regs_struct regs; +    char pad3[8]; +} x86_64_elf_prstatus; + +static int x86_64_write_elf64_note(WriteCoreDumpFunction f, +                                   CPUX86State *env, int id, +                                   void *opaque) +{ +    x86_64_user_regs_struct regs; +    Elf64_Nhdr *note; +    char *buf; +    int descsz, note_size, name_size = 5; +    const char *name = "CORE"; +    int ret; + +    regs.r15 = env->regs[15]; +    regs.r14 = env->regs[14]; +    regs.r13 = env->regs[13]; +    regs.r12 = env->regs[12]; +    regs.r11 = env->regs[11]; +    regs.r10 = env->regs[10]; +    regs.r9  = env->regs[9]; +    regs.r8  = env->regs[8]; +    regs.rbp = env->regs[R_EBP]; +    regs.rsp = env->regs[R_ESP]; +    regs.rdi = env->regs[R_EDI]; +    regs.rsi = env->regs[R_ESI]; +    regs.rdx = env->regs[R_EDX]; +    regs.rcx = env->regs[R_ECX]; +    regs.rbx = env->regs[R_EBX]; +    regs.rax = env->regs[R_EAX]; +    regs.rip = env->eip; +    regs.eflags = env->eflags; + +    regs.orig_rax = 0; /* FIXME */ +    regs.cs = env->segs[R_CS].selector; +    regs.ss = env->segs[R_SS].selector; +    regs.fs_base = env->segs[R_FS].base; +    regs.gs_base = env->segs[R_GS].base; +    regs.ds = env->segs[R_DS].selector; +    regs.es = env->segs[R_ES].selector; +    regs.fs = env->segs[R_FS].selector; +    regs.gs = env->segs[R_GS].selector; + +    descsz = sizeof(x86_64_elf_prstatus); +    note_size = ((sizeof(Elf64_Nhdr) + 3) / 4 + (name_size + 3) / 4 + +                (descsz + 3) / 4) * 4; +    note = g_malloc0(note_size); +    note->n_namesz = cpu_to_le32(name_size); +    note->n_descsz = cpu_to_le32(descsz); +    note->n_type = cpu_to_le32(NT_PRSTATUS); +    buf = (char *)note; +    buf += ((sizeof(Elf64_Nhdr) + 3) / 4) * 4; +    memcpy(buf, name, name_size); +    buf += ((name_size + 3) / 4) * 4; +    memcpy(buf + 32, &id, 4); /* pr_pid */ +    buf += descsz - sizeof(x86_64_user_regs_struct)-sizeof(target_ulong); +    memcpy(buf, ®s, sizeof(x86_64_user_regs_struct)); + +    ret = f(note, note_size, opaque); +    g_free(note); +    if (ret < 0) { +        return -1; +    } + +    return 0; +} +#endif + +typedef struct { +    uint32_t ebx, ecx, edx, esi, edi, ebp, eax; +    unsigned short ds, __ds, es, __es; +    unsigned short fs, __fs, gs, __gs; +    uint32_t orig_eax, eip; +    unsigned short cs, __cs; +    uint32_t eflags, esp; +    unsigned short ss, __ss; +} x86_user_regs_struct; + +typedef struct { +    char pad1[24]; +    uint32_t pid; +    char pad2[44]; +    x86_user_regs_struct regs; +    char pad3[4]; +} x86_elf_prstatus; + +static void x86_fill_elf_prstatus(x86_elf_prstatus *prstatus, CPUX86State *env, +                                  int id) +{ +    memset(prstatus, 0, sizeof(x86_elf_prstatus)); +    prstatus->regs.ebp = env->regs[R_EBP] & 0xffffffff; +    prstatus->regs.esp = env->regs[R_ESP] & 0xffffffff; +    prstatus->regs.edi = env->regs[R_EDI] & 0xffffffff; +    prstatus->regs.esi = env->regs[R_ESI] & 0xffffffff; +    prstatus->regs.edx = env->regs[R_EDX] & 0xffffffff; +    prstatus->regs.ecx = env->regs[R_ECX] & 0xffffffff; +    prstatus->regs.ebx = env->regs[R_EBX] & 0xffffffff; +    prstatus->regs.eax = env->regs[R_EAX] & 0xffffffff; +    prstatus->regs.eip = env->eip & 0xffffffff; +    prstatus->regs.eflags = env->eflags & 0xffffffff; + +    prstatus->regs.cs = env->segs[R_CS].selector; +    prstatus->regs.ss = env->segs[R_SS].selector; +    prstatus->regs.ds = env->segs[R_DS].selector; +    prstatus->regs.es = env->segs[R_ES].selector; +    prstatus->regs.fs = env->segs[R_FS].selector; +    prstatus->regs.gs = env->segs[R_GS].selector; + +    prstatus->pid = id; +} + +static int x86_write_elf64_note(WriteCoreDumpFunction f, CPUX86State *env, +                                int id, void *opaque) +{ +    x86_elf_prstatus prstatus; +    Elf64_Nhdr *note; +    char *buf; +    int descsz, note_size, name_size = 5; +    const char *name = "CORE"; +    int ret; + +    x86_fill_elf_prstatus(&prstatus, env, id); +    descsz = sizeof(x86_elf_prstatus); +    note_size = ((sizeof(Elf64_Nhdr) + 3) / 4 + (name_size + 3) / 4 + +                (descsz + 3) / 4) * 4; +    note = g_malloc0(note_size); +    note->n_namesz = cpu_to_le32(name_size); +    note->n_descsz = cpu_to_le32(descsz); +    note->n_type = cpu_to_le32(NT_PRSTATUS); +    buf = (char *)note; +    buf += ((sizeof(Elf64_Nhdr) + 3) / 4) * 4; +    memcpy(buf, name, name_size); +    buf += ((name_size + 3) / 4) * 4; +    memcpy(buf, &prstatus, sizeof(prstatus)); + +    ret = f(note, note_size, opaque); +    g_free(note); +    if (ret < 0) { +        return -1; +    } + +    return 0; +} + +int x86_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, +                             int cpuid, void *opaque) +{ +    X86CPU *cpu = X86_CPU(cs); +    int ret; +#ifdef TARGET_X86_64 +    X86CPU *first_x86_cpu = X86_CPU(first_cpu); +    bool lma = !!(first_x86_cpu->env.hflags & HF_LMA_MASK); + +    if (lma) { +        ret = x86_64_write_elf64_note(f, &cpu->env, cpuid, opaque); +    } else { +#endif +        ret = x86_write_elf64_note(f, &cpu->env, cpuid, opaque); +#ifdef TARGET_X86_64 +    } +#endif + +    return ret; +} + +int x86_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, +                             int cpuid, void *opaque) +{ +    X86CPU *cpu = X86_CPU(cs); +    x86_elf_prstatus prstatus; +    Elf32_Nhdr *note; +    char *buf; +    int descsz, note_size, name_size = 5; +    const char *name = "CORE"; +    int ret; + +    x86_fill_elf_prstatus(&prstatus, &cpu->env, cpuid); +    descsz = sizeof(x86_elf_prstatus); +    note_size = ((sizeof(Elf32_Nhdr) + 3) / 4 + (name_size + 3) / 4 + +                (descsz + 3) / 4) * 4; +    note = g_malloc0(note_size); +    note->n_namesz = cpu_to_le32(name_size); +    note->n_descsz = cpu_to_le32(descsz); +    note->n_type = cpu_to_le32(NT_PRSTATUS); +    buf = (char *)note; +    buf += ((sizeof(Elf32_Nhdr) + 3) / 4) * 4; +    memcpy(buf, name, name_size); +    buf += ((name_size + 3) / 4) * 4; +    memcpy(buf, &prstatus, sizeof(prstatus)); + +    ret = f(note, note_size, opaque); +    g_free(note); +    if (ret < 0) { +        return -1; +    } + +    return 0; +} + +/* + * please count up QEMUCPUSTATE_VERSION if you have changed definition of + * QEMUCPUState, and modify the tools using this information accordingly. + */ +#define QEMUCPUSTATE_VERSION (1) + +struct QEMUCPUSegment { +    uint32_t selector; +    uint32_t limit; +    uint32_t flags; +    uint32_t pad; +    uint64_t base; +}; + +typedef struct QEMUCPUSegment QEMUCPUSegment; + +struct QEMUCPUState { +    uint32_t version; +    uint32_t size; +    uint64_t rax, rbx, rcx, rdx, rsi, rdi, rsp, rbp; +    uint64_t r8, r9, r10, r11, r12, r13, r14, r15; +    uint64_t rip, rflags; +    QEMUCPUSegment cs, ds, es, fs, gs, ss; +    QEMUCPUSegment ldt, tr, gdt, idt; +    uint64_t cr[5]; +}; + +typedef struct QEMUCPUState QEMUCPUState; + +static void copy_segment(QEMUCPUSegment *d, SegmentCache *s) +{ +    d->pad = 0; +    d->selector = s->selector; +    d->limit = s->limit; +    d->flags = s->flags; +    d->base = s->base; +} + +static void qemu_get_cpustate(QEMUCPUState *s, CPUX86State *env) +{ +    memset(s, 0, sizeof(QEMUCPUState)); + +    s->version = QEMUCPUSTATE_VERSION; +    s->size = sizeof(QEMUCPUState); + +    s->rax = env->regs[R_EAX]; +    s->rbx = env->regs[R_EBX]; +    s->rcx = env->regs[R_ECX]; +    s->rdx = env->regs[R_EDX]; +    s->rsi = env->regs[R_ESI]; +    s->rdi = env->regs[R_EDI]; +    s->rsp = env->regs[R_ESP]; +    s->rbp = env->regs[R_EBP]; +#ifdef TARGET_X86_64 +    s->r8  = env->regs[8]; +    s->r9  = env->regs[9]; +    s->r10 = env->regs[10]; +    s->r11 = env->regs[11]; +    s->r12 = env->regs[12]; +    s->r13 = env->regs[13]; +    s->r14 = env->regs[14]; +    s->r15 = env->regs[15]; +#endif +    s->rip = env->eip; +    s->rflags = env->eflags; + +    copy_segment(&s->cs, &env->segs[R_CS]); +    copy_segment(&s->ds, &env->segs[R_DS]); +    copy_segment(&s->es, &env->segs[R_ES]); +    copy_segment(&s->fs, &env->segs[R_FS]); +    copy_segment(&s->gs, &env->segs[R_GS]); +    copy_segment(&s->ss, &env->segs[R_SS]); +    copy_segment(&s->ldt, &env->ldt); +    copy_segment(&s->tr, &env->tr); +    copy_segment(&s->gdt, &env->gdt); +    copy_segment(&s->idt, &env->idt); + +    s->cr[0] = env->cr[0]; +    s->cr[1] = env->cr[1]; +    s->cr[2] = env->cr[2]; +    s->cr[3] = env->cr[3]; +    s->cr[4] = env->cr[4]; +} + +static inline int cpu_write_qemu_note(WriteCoreDumpFunction f, +                                      CPUX86State *env, +                                      void *opaque, +                                      int type) +{ +    QEMUCPUState state; +    Elf64_Nhdr *note64; +    Elf32_Nhdr *note32; +    void *note; +    char *buf; +    int descsz, note_size, name_size = 5, note_head_size; +    const char *name = "QEMU"; +    int ret; + +    qemu_get_cpustate(&state, env); + +    descsz = sizeof(state); +    if (type == 0) { +        note_head_size = sizeof(Elf32_Nhdr); +    } else { +        note_head_size = sizeof(Elf64_Nhdr); +    } +    note_size = ((note_head_size + 3) / 4 + (name_size + 3) / 4 + +                (descsz + 3) / 4) * 4; +    note = g_malloc0(note_size); +    if (type == 0) { +        note32 = note; +        note32->n_namesz = cpu_to_le32(name_size); +        note32->n_descsz = cpu_to_le32(descsz); +        note32->n_type = 0; +    } else { +        note64 = note; +        note64->n_namesz = cpu_to_le32(name_size); +        note64->n_descsz = cpu_to_le32(descsz); +        note64->n_type = 0; +    } +    buf = note; +    buf += ((note_head_size + 3) / 4) * 4; +    memcpy(buf, name, name_size); +    buf += ((name_size + 3) / 4) * 4; +    memcpy(buf, &state, sizeof(state)); + +    ret = f(note, note_size, opaque); +    g_free(note); +    if (ret < 0) { +        return -1; +    } + +    return 0; +} + +int x86_cpu_write_elf64_qemunote(WriteCoreDumpFunction f, CPUState *cs, +                                 void *opaque) +{ +    X86CPU *cpu = X86_CPU(cs); + +    return cpu_write_qemu_note(f, &cpu->env, opaque, 1); +} + +int x86_cpu_write_elf32_qemunote(WriteCoreDumpFunction f, CPUState *cs, +                                 void *opaque) +{ +    X86CPU *cpu = X86_CPU(cs); + +    return cpu_write_qemu_note(f, &cpu->env, opaque, 0); +} + +int cpu_get_dump_info(ArchDumpInfo *info, +                      const GuestPhysBlockList *guest_phys_blocks) +{ +    bool lma = false; +    GuestPhysBlock *block; + +#ifdef TARGET_X86_64 +    X86CPU *first_x86_cpu = X86_CPU(first_cpu); + +    lma = !!(first_x86_cpu->env.hflags & HF_LMA_MASK); +#endif + +    if (lma) { +        info->d_machine = EM_X86_64; +    } else { +        info->d_machine = EM_386; +    } +    info->d_endian = ELFDATA2LSB; + +    if (lma) { +        info->d_class = ELFCLASS64; +    } else { +        info->d_class = ELFCLASS32; + +        QTAILQ_FOREACH(block, &guest_phys_blocks->head, next) { +            if (block->target_end > UINT_MAX) { +                /* The memory size is greater than 4G */ +                info->d_class = ELFCLASS64; +                break; +            } +        } +    } + +    return 0; +} + +ssize_t cpu_get_note_size(int class, int machine, int nr_cpus) +{ +    int name_size = 5; /* "CORE" or "QEMU" */ +    size_t elf_note_size = 0; +    size_t qemu_note_size = 0; +    int elf_desc_size = 0; +    int qemu_desc_size = 0; +    int note_head_size; + +    if (class == ELFCLASS32) { +        note_head_size = sizeof(Elf32_Nhdr); +    } else { +        note_head_size = sizeof(Elf64_Nhdr); +    } + +    if (machine == EM_386) { +        elf_desc_size = sizeof(x86_elf_prstatus); +    } +#ifdef TARGET_X86_64 +    else { +        elf_desc_size = sizeof(x86_64_elf_prstatus); +    } +#endif +    qemu_desc_size = sizeof(QEMUCPUState); + +    elf_note_size = ((note_head_size + 3) / 4 + (name_size + 3) / 4 + +                     (elf_desc_size + 3) / 4) * 4; +    qemu_note_size = ((note_head_size + 3) / 4 + (name_size + 3) / 4 + +                      (qemu_desc_size + 3) / 4) * 4; + +    return (elf_note_size + qemu_note_size) * nr_cpus; +} diff --git a/target-i386/arch_memory_mapping.c b/target-i386/arch_memory_mapping.c new file mode 100644 index 00000000..01563fec --- /dev/null +++ b/target-i386/arch_memory_mapping.c @@ -0,0 +1,280 @@ +/* + * i386 memory mapping + * + * Copyright Fujitsu, Corp. 2011, 2012 + * + * Authors: + *     Wen Congyang <wency@cn.fujitsu.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "cpu.h" +#include "exec/cpu-all.h" +#include "sysemu/memory_mapping.h" + +/* PAE Paging or IA-32e Paging */ +static void walk_pte(MemoryMappingList *list, AddressSpace *as, +                     hwaddr pte_start_addr, +                     int32_t a20_mask, target_ulong start_line_addr) +{ +    hwaddr pte_addr, start_paddr; +    uint64_t pte; +    target_ulong start_vaddr; +    int i; + +    for (i = 0; i < 512; i++) { +        pte_addr = (pte_start_addr + i * 8) & a20_mask; +        pte = address_space_ldq(as, pte_addr, MEMTXATTRS_UNSPECIFIED, NULL); +        if (!(pte & PG_PRESENT_MASK)) { +            /* not present */ +            continue; +        } + +        start_paddr = (pte & ~0xfff) & ~(0x1ULL << 63); +        if (cpu_physical_memory_is_io(start_paddr)) { +            /* I/O region */ +            continue; +        } + +        start_vaddr = start_line_addr | ((i & 0x1ff) << 12); +        memory_mapping_list_add_merge_sorted(list, start_paddr, +                                             start_vaddr, 1 << 12); +    } +} + +/* 32-bit Paging */ +static void walk_pte2(MemoryMappingList *list, AddressSpace *as, +                      hwaddr pte_start_addr, int32_t a20_mask, +                      target_ulong start_line_addr) +{ +    hwaddr pte_addr, start_paddr; +    uint32_t pte; +    target_ulong start_vaddr; +    int i; + +    for (i = 0; i < 1024; i++) { +        pte_addr = (pte_start_addr + i * 4) & a20_mask; +        pte = address_space_ldl(as, pte_addr, MEMTXATTRS_UNSPECIFIED, NULL); +        if (!(pte & PG_PRESENT_MASK)) { +            /* not present */ +            continue; +        } + +        start_paddr = pte & ~0xfff; +        if (cpu_physical_memory_is_io(start_paddr)) { +            /* I/O region */ +            continue; +        } + +        start_vaddr = start_line_addr | ((i & 0x3ff) << 12); +        memory_mapping_list_add_merge_sorted(list, start_paddr, +                                             start_vaddr, 1 << 12); +    } +} + +/* PAE Paging or IA-32e Paging */ +#define PLM4_ADDR_MASK 0xffffffffff000ULL /* selects bits 51:12 */ + +static void walk_pde(MemoryMappingList *list, AddressSpace *as, +                     hwaddr pde_start_addr, +                     int32_t a20_mask, target_ulong start_line_addr) +{ +    hwaddr pde_addr, pte_start_addr, start_paddr; +    uint64_t pde; +    target_ulong line_addr, start_vaddr; +    int i; + +    for (i = 0; i < 512; i++) { +        pde_addr = (pde_start_addr + i * 8) & a20_mask; +        pde = address_space_ldq(as, pde_addr, MEMTXATTRS_UNSPECIFIED, NULL); +        if (!(pde & PG_PRESENT_MASK)) { +            /* not present */ +            continue; +        } + +        line_addr = start_line_addr | ((i & 0x1ff) << 21); +        if (pde & PG_PSE_MASK) { +            /* 2 MB page */ +            start_paddr = (pde & ~0x1fffff) & ~(0x1ULL << 63); +            if (cpu_physical_memory_is_io(start_paddr)) { +                /* I/O region */ +                continue; +            } +            start_vaddr = line_addr; +            memory_mapping_list_add_merge_sorted(list, start_paddr, +                                                 start_vaddr, 1 << 21); +            continue; +        } + +        pte_start_addr = (pde & PLM4_ADDR_MASK) & a20_mask; +        walk_pte(list, as, pte_start_addr, a20_mask, line_addr); +    } +} + +/* 32-bit Paging */ +static void walk_pde2(MemoryMappingList *list, AddressSpace *as, +                      hwaddr pde_start_addr, int32_t a20_mask, +                      bool pse) +{ +    hwaddr pde_addr, pte_start_addr, start_paddr, high_paddr; +    uint32_t pde; +    target_ulong line_addr, start_vaddr; +    int i; + +    for (i = 0; i < 1024; i++) { +        pde_addr = (pde_start_addr + i * 4) & a20_mask; +        pde = address_space_ldl(as, pde_addr, MEMTXATTRS_UNSPECIFIED, NULL); +        if (!(pde & PG_PRESENT_MASK)) { +            /* not present */ +            continue; +        } + +        line_addr = (((unsigned int)i & 0x3ff) << 22); +        if ((pde & PG_PSE_MASK) && pse) { +            /* +             * 4 MB page: +             * bits 39:32 are bits 20:13 of the PDE +             * bit3 31:22 are bits 31:22 of the PDE +             */ +            high_paddr = ((hwaddr)(pde & 0x1fe000) << 19); +            start_paddr = (pde & ~0x3fffff) | high_paddr; +            if (cpu_physical_memory_is_io(start_paddr)) { +                /* I/O region */ +                continue; +            } +            start_vaddr = line_addr; +            memory_mapping_list_add_merge_sorted(list, start_paddr, +                                                 start_vaddr, 1 << 22); +            continue; +        } + +        pte_start_addr = (pde & ~0xfff) & a20_mask; +        walk_pte2(list, as, pte_start_addr, a20_mask, line_addr); +    } +} + +/* PAE Paging */ +static void walk_pdpe2(MemoryMappingList *list, AddressSpace *as, +                       hwaddr pdpe_start_addr, int32_t a20_mask) +{ +    hwaddr pdpe_addr, pde_start_addr; +    uint64_t pdpe; +    target_ulong line_addr; +    int i; + +    for (i = 0; i < 4; i++) { +        pdpe_addr = (pdpe_start_addr + i * 8) & a20_mask; +        pdpe = address_space_ldq(as, pdpe_addr, MEMTXATTRS_UNSPECIFIED, NULL); +        if (!(pdpe & PG_PRESENT_MASK)) { +            /* not present */ +            continue; +        } + +        line_addr = (((unsigned int)i & 0x3) << 30); +        pde_start_addr = (pdpe & ~0xfff) & a20_mask; +        walk_pde(list, as, pde_start_addr, a20_mask, line_addr); +    } +} + +#ifdef TARGET_X86_64 +/* IA-32e Paging */ +static void walk_pdpe(MemoryMappingList *list, AddressSpace *as, +                      hwaddr pdpe_start_addr, int32_t a20_mask, +                      target_ulong start_line_addr) +{ +    hwaddr pdpe_addr, pde_start_addr, start_paddr; +    uint64_t pdpe; +    target_ulong line_addr, start_vaddr; +    int i; + +    for (i = 0; i < 512; i++) { +        pdpe_addr = (pdpe_start_addr + i * 8) & a20_mask; +        pdpe = address_space_ldq(as, pdpe_addr, MEMTXATTRS_UNSPECIFIED, NULL); +        if (!(pdpe & PG_PRESENT_MASK)) { +            /* not present */ +            continue; +        } + +        line_addr = start_line_addr | ((i & 0x1ffULL) << 30); +        if (pdpe & PG_PSE_MASK) { +            /* 1 GB page */ +            start_paddr = (pdpe & ~0x3fffffff) & ~(0x1ULL << 63); +            if (cpu_physical_memory_is_io(start_paddr)) { +                /* I/O region */ +                continue; +            } +            start_vaddr = line_addr; +            memory_mapping_list_add_merge_sorted(list, start_paddr, +                                                 start_vaddr, 1 << 30); +            continue; +        } + +        pde_start_addr = (pdpe & PLM4_ADDR_MASK) & a20_mask; +        walk_pde(list, as, pde_start_addr, a20_mask, line_addr); +    } +} + +/* IA-32e Paging */ +static void walk_pml4e(MemoryMappingList *list, AddressSpace *as, +                       hwaddr pml4e_start_addr, int32_t a20_mask) +{ +    hwaddr pml4e_addr, pdpe_start_addr; +    uint64_t pml4e; +    target_ulong line_addr; +    int i; + +    for (i = 0; i < 512; i++) { +        pml4e_addr = (pml4e_start_addr + i * 8) & a20_mask; +        pml4e = address_space_ldq(as, pml4e_addr, MEMTXATTRS_UNSPECIFIED, +                                  NULL); +        if (!(pml4e & PG_PRESENT_MASK)) { +            /* not present */ +            continue; +        } + +        line_addr = ((i & 0x1ffULL) << 39) | (0xffffULL << 48); +        pdpe_start_addr = (pml4e & PLM4_ADDR_MASK) & a20_mask; +        walk_pdpe(list, as, pdpe_start_addr, a20_mask, line_addr); +    } +} +#endif + +void x86_cpu_get_memory_mapping(CPUState *cs, MemoryMappingList *list, +                                Error **errp) +{ +    X86CPU *cpu = X86_CPU(cs); +    CPUX86State *env = &cpu->env; + +    if (!cpu_paging_enabled(cs)) { +        /* paging is disabled */ +        return; +    } + +    if (env->cr[4] & CR4_PAE_MASK) { +#ifdef TARGET_X86_64 +        if (env->hflags & HF_LMA_MASK) { +            hwaddr pml4e_addr; + +            pml4e_addr = (env->cr[3] & PLM4_ADDR_MASK) & env->a20_mask; +            walk_pml4e(list, cs->as, pml4e_addr, env->a20_mask); +        } else +#endif +        { +            hwaddr pdpe_addr; + +            pdpe_addr = (env->cr[3] & ~0x1f) & env->a20_mask; +            walk_pdpe2(list, cs->as, pdpe_addr, env->a20_mask); +        } +    } else { +        hwaddr pde_addr; +        bool pse; + +        pde_addr = (env->cr[3] & ~0xfff) & env->a20_mask; +        pse = !!(env->cr[4] & CR4_PSE_MASK); +        walk_pde2(list, cs->as, pde_addr, env->a20_mask, pse); +    } +} + diff --git a/target-i386/cc_helper.c b/target-i386/cc_helper.c new file mode 100644 index 00000000..ecbf0ec0 --- /dev/null +++ b/target-i386/cc_helper.c @@ -0,0 +1,394 @@ +/* + *  x86 condition code helpers + * + *  Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "cpu.h" +#include "exec/helper-proto.h" + +const uint8_t parity_table[256] = { +    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, +    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, +    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, +    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, +    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, +    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, +    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, +    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, +    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, +    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, +    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, +    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, +    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, +    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, +    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, +    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, +    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, +    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, +    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, +    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, +    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, +    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, +    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, +    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, +    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, +    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, +    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, +    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, +    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, +    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, +    CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, +    0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, +}; + +#define SHIFT 0 +#include "cc_helper_template.h" +#undef SHIFT + +#define SHIFT 1 +#include "cc_helper_template.h" +#undef SHIFT + +#define SHIFT 2 +#include "cc_helper_template.h" +#undef SHIFT + +#ifdef TARGET_X86_64 + +#define SHIFT 3 +#include "cc_helper_template.h" +#undef SHIFT + +#endif + +static target_ulong compute_all_adcx(target_ulong dst, target_ulong src1, +                                     target_ulong src2) +{ +    return (src1 & ~CC_C) | (dst * CC_C); +} + +static target_ulong compute_all_adox(target_ulong dst, target_ulong src1, +                                     target_ulong src2) +{ +    return (src1 & ~CC_O) | (src2 * CC_O); +} + +static target_ulong compute_all_adcox(target_ulong dst, target_ulong src1, +                                      target_ulong src2) +{ +    return (src1 & ~(CC_C | CC_O)) | (dst * CC_C) | (src2 * CC_O); +} + +target_ulong helper_cc_compute_all(target_ulong dst, target_ulong src1, +                                   target_ulong src2, int op) +{ +    switch (op) { +    default: /* should never happen */ +        return 0; + +    case CC_OP_EFLAGS: +        return src1; +    case CC_OP_CLR: +        return CC_Z | CC_P; + +    case CC_OP_MULB: +        return compute_all_mulb(dst, src1); +    case CC_OP_MULW: +        return compute_all_mulw(dst, src1); +    case CC_OP_MULL: +        return compute_all_mull(dst, src1); + +    case CC_OP_ADDB: +        return compute_all_addb(dst, src1); +    case CC_OP_ADDW: +        return compute_all_addw(dst, src1); +    case CC_OP_ADDL: +        return compute_all_addl(dst, src1); + +    case CC_OP_ADCB: +        return compute_all_adcb(dst, src1, src2); +    case CC_OP_ADCW: +        return compute_all_adcw(dst, src1, src2); +    case CC_OP_ADCL: +        return compute_all_adcl(dst, src1, src2); + +    case CC_OP_SUBB: +        return compute_all_subb(dst, src1); +    case CC_OP_SUBW: +        return compute_all_subw(dst, src1); +    case CC_OP_SUBL: +        return compute_all_subl(dst, src1); + +    case CC_OP_SBBB: +        return compute_all_sbbb(dst, src1, src2); +    case CC_OP_SBBW: +        return compute_all_sbbw(dst, src1, src2); +    case CC_OP_SBBL: +        return compute_all_sbbl(dst, src1, src2); + +    case CC_OP_LOGICB: +        return compute_all_logicb(dst, src1); +    case CC_OP_LOGICW: +        return compute_all_logicw(dst, src1); +    case CC_OP_LOGICL: +        return compute_all_logicl(dst, src1); + +    case CC_OP_INCB: +        return compute_all_incb(dst, src1); +    case CC_OP_INCW: +        return compute_all_incw(dst, src1); +    case CC_OP_INCL: +        return compute_all_incl(dst, src1); + +    case CC_OP_DECB: +        return compute_all_decb(dst, src1); +    case CC_OP_DECW: +        return compute_all_decw(dst, src1); +    case CC_OP_DECL: +        return compute_all_decl(dst, src1); + +    case CC_OP_SHLB: +        return compute_all_shlb(dst, src1); +    case CC_OP_SHLW: +        return compute_all_shlw(dst, src1); +    case CC_OP_SHLL: +        return compute_all_shll(dst, src1); + +    case CC_OP_SARB: +        return compute_all_sarb(dst, src1); +    case CC_OP_SARW: +        return compute_all_sarw(dst, src1); +    case CC_OP_SARL: +        return compute_all_sarl(dst, src1); + +    case CC_OP_BMILGB: +        return compute_all_bmilgb(dst, src1); +    case CC_OP_BMILGW: +        return compute_all_bmilgw(dst, src1); +    case CC_OP_BMILGL: +        return compute_all_bmilgl(dst, src1); + +    case CC_OP_ADCX: +        return compute_all_adcx(dst, src1, src2); +    case CC_OP_ADOX: +        return compute_all_adox(dst, src1, src2); +    case CC_OP_ADCOX: +        return compute_all_adcox(dst, src1, src2); + +#ifdef TARGET_X86_64 +    case CC_OP_MULQ: +        return compute_all_mulq(dst, src1); +    case CC_OP_ADDQ: +        return compute_all_addq(dst, src1); +    case CC_OP_ADCQ: +        return compute_all_adcq(dst, src1, src2); +    case CC_OP_SUBQ: +        return compute_all_subq(dst, src1); +    case CC_OP_SBBQ: +        return compute_all_sbbq(dst, src1, src2); +    case CC_OP_LOGICQ: +        return compute_all_logicq(dst, src1); +    case CC_OP_INCQ: +        return compute_all_incq(dst, src1); +    case CC_OP_DECQ: +        return compute_all_decq(dst, src1); +    case CC_OP_SHLQ: +        return compute_all_shlq(dst, src1); +    case CC_OP_SARQ: +        return compute_all_sarq(dst, src1); +    case CC_OP_BMILGQ: +        return compute_all_bmilgq(dst, src1); +#endif +    } +} + +uint32_t cpu_cc_compute_all(CPUX86State *env, int op) +{ +    return helper_cc_compute_all(CC_DST, CC_SRC, CC_SRC2, op); +} + +target_ulong helper_cc_compute_c(target_ulong dst, target_ulong src1, +                                 target_ulong src2, int op) +{ +    switch (op) { +    default: /* should never happen */ +    case CC_OP_LOGICB: +    case CC_OP_LOGICW: +    case CC_OP_LOGICL: +    case CC_OP_LOGICQ: +    case CC_OP_CLR: +        return 0; + +    case CC_OP_EFLAGS: +    case CC_OP_SARB: +    case CC_OP_SARW: +    case CC_OP_SARL: +    case CC_OP_SARQ: +    case CC_OP_ADOX: +        return src1 & 1; + +    case CC_OP_INCB: +    case CC_OP_INCW: +    case CC_OP_INCL: +    case CC_OP_INCQ: +    case CC_OP_DECB: +    case CC_OP_DECW: +    case CC_OP_DECL: +    case CC_OP_DECQ: +        return src1; + +    case CC_OP_MULB: +    case CC_OP_MULW: +    case CC_OP_MULL: +    case CC_OP_MULQ: +        return src1 != 0; + +    case CC_OP_ADCX: +    case CC_OP_ADCOX: +        return dst; + +    case CC_OP_ADDB: +        return compute_c_addb(dst, src1); +    case CC_OP_ADDW: +        return compute_c_addw(dst, src1); +    case CC_OP_ADDL: +        return compute_c_addl(dst, src1); + +    case CC_OP_ADCB: +        return compute_c_adcb(dst, src1, src2); +    case CC_OP_ADCW: +        return compute_c_adcw(dst, src1, src2); +    case CC_OP_ADCL: +        return compute_c_adcl(dst, src1, src2); + +    case CC_OP_SUBB: +        return compute_c_subb(dst, src1); +    case CC_OP_SUBW: +        return compute_c_subw(dst, src1); +    case CC_OP_SUBL: +        return compute_c_subl(dst, src1); + +    case CC_OP_SBBB: +        return compute_c_sbbb(dst, src1, src2); +    case CC_OP_SBBW: +        return compute_c_sbbw(dst, src1, src2); +    case CC_OP_SBBL: +        return compute_c_sbbl(dst, src1, src2); + +    case CC_OP_SHLB: +        return compute_c_shlb(dst, src1); +    case CC_OP_SHLW: +        return compute_c_shlw(dst, src1); +    case CC_OP_SHLL: +        return compute_c_shll(dst, src1); + +    case CC_OP_BMILGB: +        return compute_c_bmilgb(dst, src1); +    case CC_OP_BMILGW: +        return compute_c_bmilgw(dst, src1); +    case CC_OP_BMILGL: +        return compute_c_bmilgl(dst, src1); + +#ifdef TARGET_X86_64 +    case CC_OP_ADDQ: +        return compute_c_addq(dst, src1); +    case CC_OP_ADCQ: +        return compute_c_adcq(dst, src1, src2); +    case CC_OP_SUBQ: +        return compute_c_subq(dst, src1); +    case CC_OP_SBBQ: +        return compute_c_sbbq(dst, src1, src2); +    case CC_OP_SHLQ: +        return compute_c_shlq(dst, src1); +    case CC_OP_BMILGQ: +        return compute_c_bmilgq(dst, src1); +#endif +    } +} + +void helper_write_eflags(CPUX86State *env, target_ulong t0, +                         uint32_t update_mask) +{ +    cpu_load_eflags(env, t0, update_mask); +} + +target_ulong helper_read_eflags(CPUX86State *env) +{ +    uint32_t eflags; + +    eflags = cpu_cc_compute_all(env, CC_OP); +    eflags |= (env->df & DF_MASK); +    eflags |= env->eflags & ~(VM_MASK | RF_MASK); +    return eflags; +} + +void helper_clts(CPUX86State *env) +{ +    env->cr[0] &= ~CR0_TS_MASK; +    env->hflags &= ~HF_TS_MASK; +} + +void helper_reset_rf(CPUX86State *env) +{ +    env->eflags &= ~RF_MASK; +} + +void helper_cli(CPUX86State *env) +{ +    env->eflags &= ~IF_MASK; +} + +void helper_sti(CPUX86State *env) +{ +    env->eflags |= IF_MASK; +} + +void helper_clac(CPUX86State *env) +{ +    env->eflags &= ~AC_MASK; +} + +void helper_stac(CPUX86State *env) +{ +    env->eflags |= AC_MASK; +} + +#if 0 +/* vm86plus instructions */ +void helper_cli_vm(CPUX86State *env) +{ +    env->eflags &= ~VIF_MASK; +} + +void helper_sti_vm(CPUX86State *env) +{ +    env->eflags |= VIF_MASK; +    if (env->eflags & VIP_MASK) { +        raise_exception(env, EXCP0D_GPF); +    } +} +#endif + +void helper_set_inhibit_irq(CPUX86State *env) +{ +    env->hflags |= HF_INHIBIT_IRQ_MASK; +} + +void helper_reset_inhibit_irq(CPUX86State *env) +{ +    env->hflags &= ~HF_INHIBIT_IRQ_MASK; +} diff --git a/target-i386/cc_helper_template.h b/target-i386/cc_helper_template.h new file mode 100644 index 00000000..607311f1 --- /dev/null +++ b/target-i386/cc_helper_template.h @@ -0,0 +1,242 @@ +/* + *  x86 condition code helpers + * + *  Copyright (c) 2008 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#define DATA_BITS (1 << (3 + SHIFT)) + +#if DATA_BITS == 8 +#define SUFFIX b +#define DATA_TYPE uint8_t +#elif DATA_BITS == 16 +#define SUFFIX w +#define DATA_TYPE uint16_t +#elif DATA_BITS == 32 +#define SUFFIX l +#define DATA_TYPE uint32_t +#elif DATA_BITS == 64 +#define SUFFIX q +#define DATA_TYPE uint64_t +#else +#error unhandled operand size +#endif + +#define SIGN_MASK (((DATA_TYPE)1) << (DATA_BITS - 1)) + +/* dynamic flags computation */ + +static int glue(compute_all_add, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ +    int cf, pf, af, zf, sf, of; +    DATA_TYPE src2 = dst - src1; + +    cf = dst < src1; +    pf = parity_table[(uint8_t)dst]; +    af = (dst ^ src1 ^ src2) & CC_A; +    zf = (dst == 0) * CC_Z; +    sf = lshift(dst, 8 - DATA_BITS) & CC_S; +    of = lshift((src1 ^ src2 ^ -1) & (src1 ^ dst), 12 - DATA_BITS) & CC_O; +    return cf | pf | af | zf | sf | of; +} + +static int glue(compute_c_add, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ +    return dst < src1; +} + +static int glue(compute_all_adc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1, +                                         DATA_TYPE src3) +{ +    int cf, pf, af, zf, sf, of; +    DATA_TYPE src2 = dst - src1 - src3; + +    cf = (src3 ? dst <= src1 : dst < src1); +    pf = parity_table[(uint8_t)dst]; +    af = (dst ^ src1 ^ src2) & 0x10; +    zf = (dst == 0) << 6; +    sf = lshift(dst, 8 - DATA_BITS) & 0x80; +    of = lshift((src1 ^ src2 ^ -1) & (src1 ^ dst), 12 - DATA_BITS) & CC_O; +    return cf | pf | af | zf | sf | of; +} + +static int glue(compute_c_adc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1, +                                       DATA_TYPE src3) +{ +    return src3 ? dst <= src1 : dst < src1; +} + +static int glue(compute_all_sub, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2) +{ +    int cf, pf, af, zf, sf, of; +    DATA_TYPE src1 = dst + src2; + +    cf = src1 < src2; +    pf = parity_table[(uint8_t)dst]; +    af = (dst ^ src1 ^ src2) & CC_A; +    zf = (dst == 0) * CC_Z; +    sf = lshift(dst, 8 - DATA_BITS) & CC_S; +    of = lshift((src1 ^ src2) & (src1 ^ dst), 12 - DATA_BITS) & CC_O; +    return cf | pf | af | zf | sf | of; +} + +static int glue(compute_c_sub, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2) +{ +    DATA_TYPE src1 = dst + src2; + +    return src1 < src2; +} + +static int glue(compute_all_sbb, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2, +                                         DATA_TYPE src3) +{ +    int cf, pf, af, zf, sf, of; +    DATA_TYPE src1 = dst + src2 + src3; + +    cf = (src3 ? src1 <= src2 : src1 < src2); +    pf = parity_table[(uint8_t)dst]; +    af = (dst ^ src1 ^ src2) & 0x10; +    zf = (dst == 0) << 6; +    sf = lshift(dst, 8 - DATA_BITS) & 0x80; +    of = lshift((src1 ^ src2) & (src1 ^ dst), 12 - DATA_BITS) & CC_O; +    return cf | pf | af | zf | sf | of; +} + +static int glue(compute_c_sbb, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2, +                                       DATA_TYPE src3) +{ +    DATA_TYPE src1 = dst + src2 + src3; + +    return (src3 ? src1 <= src2 : src1 < src2); +} + +static int glue(compute_all_logic, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ +    int cf, pf, af, zf, sf, of; + +    cf = 0; +    pf = parity_table[(uint8_t)dst]; +    af = 0; +    zf = (dst == 0) * CC_Z; +    sf = lshift(dst, 8 - DATA_BITS) & CC_S; +    of = 0; +    return cf | pf | af | zf | sf | of; +} + +static int glue(compute_all_inc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ +    int cf, pf, af, zf, sf, of; +    DATA_TYPE src2; + +    cf = src1; +    src1 = dst - 1; +    src2 = 1; +    pf = parity_table[(uint8_t)dst]; +    af = (dst ^ src1 ^ src2) & CC_A; +    zf = (dst == 0) * CC_Z; +    sf = lshift(dst, 8 - DATA_BITS) & CC_S; +    of = (dst == SIGN_MASK) * CC_O; +    return cf | pf | af | zf | sf | of; +} + +static int glue(compute_all_dec, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ +    int cf, pf, af, zf, sf, of; +    DATA_TYPE src2; + +    cf = src1; +    src1 = dst + 1; +    src2 = 1; +    pf = parity_table[(uint8_t)dst]; +    af = (dst ^ src1 ^ src2) & CC_A; +    zf = (dst == 0) * CC_Z; +    sf = lshift(dst, 8 - DATA_BITS) & CC_S; +    of = (dst == SIGN_MASK - 1) * CC_O; +    return cf | pf | af | zf | sf | of; +} + +static int glue(compute_all_shl, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ +    int cf, pf, af, zf, sf, of; + +    cf = (src1 >> (DATA_BITS - 1)) & CC_C; +    pf = parity_table[(uint8_t)dst]; +    af = 0; /* undefined */ +    zf = (dst == 0) * CC_Z; +    sf = lshift(dst, 8 - DATA_BITS) & CC_S; +    /* of is defined iff shift count == 1 */ +    of = lshift(src1 ^ dst, 12 - DATA_BITS) & CC_O; +    return cf | pf | af | zf | sf | of; +} + +static int glue(compute_c_shl, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ +    return (src1 >> (DATA_BITS - 1)) & CC_C; +} + +static int glue(compute_all_sar, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ +    int cf, pf, af, zf, sf, of; + +    cf = src1 & 1; +    pf = parity_table[(uint8_t)dst]; +    af = 0; /* undefined */ +    zf = (dst == 0) * CC_Z; +    sf = lshift(dst, 8 - DATA_BITS) & CC_S; +    /* of is defined iff shift count == 1 */ +    of = lshift(src1 ^ dst, 12 - DATA_BITS) & CC_O; +    return cf | pf | af | zf | sf | of; +} + +/* NOTE: we compute the flags like the P4. On olders CPUs, only OF and +   CF are modified and it is slower to do that.  Note as well that we +   don't truncate SRC1 for computing carry to DATA_TYPE.  */ +static int glue(compute_all_mul, SUFFIX)(DATA_TYPE dst, target_long src1) +{ +    int cf, pf, af, zf, sf, of; + +    cf = (src1 != 0); +    pf = parity_table[(uint8_t)dst]; +    af = 0; /* undefined */ +    zf = (dst == 0) * CC_Z; +    sf = lshift(dst, 8 - DATA_BITS) & CC_S; +    of = cf * CC_O; +    return cf | pf | af | zf | sf | of; +} + +static int glue(compute_all_bmilg, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ +    int cf, pf, af, zf, sf, of; + +    cf = (src1 == 0); +    pf = 0; /* undefined */ +    af = 0; /* undefined */ +    zf = (dst == 0) * CC_Z; +    sf = lshift(dst, 8 - DATA_BITS) & CC_S; +    of = 0; +    return cf | pf | af | zf | sf | of; +} + +static int glue(compute_c_bmilg, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ +    return src1 == 0; +} + +#undef DATA_BITS +#undef SIGN_MASK +#undef DATA_TYPE +#undef DATA_MASK +#undef SUFFIX diff --git a/target-i386/cpu-qom.h b/target-i386/cpu-qom.h new file mode 100644 index 00000000..7a4fddd8 --- /dev/null +++ b/target-i386/cpu-qom.h @@ -0,0 +1,162 @@ +/* + * QEMU x86 CPU + * + * Copyright (c) 2012 SUSE LINUX Products GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/lgpl-2.1.html> + */ +#ifndef QEMU_I386_CPU_QOM_H +#define QEMU_I386_CPU_QOM_H + +#include "qom/cpu.h" +#include "cpu.h" +#include "qapi/error.h" +#include "qemu/notify.h" + +#ifdef TARGET_X86_64 +#define TYPE_X86_CPU "x86_64-cpu" +#else +#define TYPE_X86_CPU "i386-cpu" +#endif + +#define X86_CPU_CLASS(klass) \ +    OBJECT_CLASS_CHECK(X86CPUClass, (klass), TYPE_X86_CPU) +#define X86_CPU(obj) \ +    OBJECT_CHECK(X86CPU, (obj), TYPE_X86_CPU) +#define X86_CPU_GET_CLASS(obj) \ +    OBJECT_GET_CLASS(X86CPUClass, (obj), TYPE_X86_CPU) + +/** + * X86CPUDefinition: + * + * CPU model definition data that was not converted to QOM per-subclass + * property defaults yet. + */ +typedef struct X86CPUDefinition X86CPUDefinition; + +/** + * X86CPUClass: + * @cpu_def: CPU model definition + * @kvm_required: Whether CPU model requires KVM to be enabled. + * @parent_realize: The parent class' realize handler. + * @parent_reset: The parent class' reset handler. + * + * An x86 CPU model or family. + */ +typedef struct X86CPUClass { +    /*< private >*/ +    CPUClass parent_class; +    /*< public >*/ + +    /* Should be eventually replaced by subclass-specific property defaults. */ +    X86CPUDefinition *cpu_def; + +    bool kvm_required; + +    DeviceRealize parent_realize; +    void (*parent_reset)(CPUState *cpu); +} X86CPUClass; + +/** + * X86CPU: + * @env: #CPUX86State + * @migratable: If set, only migratable flags will be accepted when "enforce" + * mode is used, and only migratable flags will be included in the "host" + * CPU model. + * + * An x86 CPU. + */ +typedef struct X86CPU { +    /*< private >*/ +    CPUState parent_obj; +    /*< public >*/ + +    CPUX86State env; + +    bool hyperv_vapic; +    bool hyperv_relaxed_timing; +    int hyperv_spinlock_attempts; +    bool hyperv_time; +    bool check_cpuid; +    bool enforce_cpuid; +    bool expose_kvm; +    bool migratable; +    bool host_features; +    int64_t apic_id; + +    /* if true the CPUID code directly forward host cache leaves to the guest */ +    bool cache_info_passthrough; + +    /* Features that were filtered out because of missing host capabilities */ +    uint32_t filtered_features[FEATURE_WORDS]; + +    /* Enable PMU CPUID bits. This can't be enabled by default yet because +     * it doesn't have ABI stability guarantees, as it passes all PMU CPUID +     * bits returned by GET_SUPPORTED_CPUID (that depend on host CPU and kernel +     * capabilities) directly to the guest. +     */ +    bool enable_pmu; + +    /* in order to simplify APIC support, we leave this pointer to the +       user */ +    struct DeviceState *apic_state; +    struct MemoryRegion *cpu_as_root, *cpu_as_mem, *smram; +    Notifier machine_done; +} X86CPU; + +static inline X86CPU *x86_env_get_cpu(CPUX86State *env) +{ +    return container_of(env, X86CPU, env); +} + +#define ENV_GET_CPU(e) CPU(x86_env_get_cpu(e)) + +#define ENV_OFFSET offsetof(X86CPU, env) + +#ifndef CONFIG_USER_ONLY +extern struct VMStateDescription vmstate_x86_cpu; +#endif + +/** + * x86_cpu_do_interrupt: + * @cpu: vCPU the interrupt is to be handled by. + */ +void x86_cpu_do_interrupt(CPUState *cpu); +bool x86_cpu_exec_interrupt(CPUState *cpu, int int_req); + +int x86_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cpu, +                             int cpuid, void *opaque); +int x86_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cpu, +                             int cpuid, void *opaque); +int x86_cpu_write_elf64_qemunote(WriteCoreDumpFunction f, CPUState *cpu, +                                 void *opaque); +int x86_cpu_write_elf32_qemunote(WriteCoreDumpFunction f, CPUState *cpu, +                                 void *opaque); + +void x86_cpu_get_memory_mapping(CPUState *cpu, MemoryMappingList *list, +                                Error **errp); + +void x86_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, +                        int flags); + +hwaddr x86_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); + +int x86_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int x86_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); + +void x86_cpu_exec_enter(CPUState *cpu); +void x86_cpu_exec_exit(CPUState *cpu); + +#endif diff --git a/target-i386/cpu.c b/target-i386/cpu.c new file mode 100644 index 00000000..e3120687 --- /dev/null +++ b/target-i386/cpu.c @@ -0,0 +1,3240 @@ +/* + *  i386 CPUID helper functions + * + *  Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> + +#include "cpu.h" +#include "sysemu/kvm.h" +#include "sysemu/cpus.h" +#include "kvm_i386.h" + +#include "qemu/error-report.h" +#include "qemu/option.h" +#include "qemu/config-file.h" +#include "qapi/qmp/qerror.h" + +#include "qapi-types.h" +#include "qapi-visit.h" +#include "qapi/visitor.h" +#include "sysemu/arch_init.h" + +#include "hw/hw.h" +#if defined(CONFIG_KVM) +#include <linux/kvm_para.h> +#endif + +#include "sysemu/sysemu.h" +#include "hw/qdev-properties.h" +#include "hw/cpu/icc_bus.h" +#ifndef CONFIG_USER_ONLY +#include "exec/address-spaces.h" +#include "hw/xen/xen.h" +#include "hw/i386/apic_internal.h" +#endif + + +/* Cache topology CPUID constants: */ + +/* CPUID Leaf 2 Descriptors */ + +#define CPUID_2_L1D_32KB_8WAY_64B 0x2c +#define CPUID_2_L1I_32KB_8WAY_64B 0x30 +#define CPUID_2_L2_2MB_8WAY_64B   0x7d + + +/* CPUID Leaf 4 constants: */ + +/* EAX: */ +#define CPUID_4_TYPE_DCACHE  1 +#define CPUID_4_TYPE_ICACHE  2 +#define CPUID_4_TYPE_UNIFIED 3 + +#define CPUID_4_LEVEL(l)          ((l) << 5) + +#define CPUID_4_SELF_INIT_LEVEL (1 << 8) +#define CPUID_4_FULLY_ASSOC     (1 << 9) + +/* EDX: */ +#define CPUID_4_NO_INVD_SHARING (1 << 0) +#define CPUID_4_INCLUSIVE       (1 << 1) +#define CPUID_4_COMPLEX_IDX     (1 << 2) + +#define ASSOC_FULL 0xFF + +/* AMD associativity encoding used on CPUID Leaf 0x80000006: */ +#define AMD_ENC_ASSOC(a) (a <=   1 ? a   : \ +                          a ==   2 ? 0x2 : \ +                          a ==   4 ? 0x4 : \ +                          a ==   8 ? 0x6 : \ +                          a ==  16 ? 0x8 : \ +                          a ==  32 ? 0xA : \ +                          a ==  48 ? 0xB : \ +                          a ==  64 ? 0xC : \ +                          a ==  96 ? 0xD : \ +                          a == 128 ? 0xE : \ +                          a == ASSOC_FULL ? 0xF : \ +                          0 /* invalid value */) + + +/* Definitions of the hardcoded cache entries we expose: */ + +/* L1 data cache: */ +#define L1D_LINE_SIZE         64 +#define L1D_ASSOCIATIVITY      8 +#define L1D_SETS              64 +#define L1D_PARTITIONS         1 +/* Size = LINE_SIZE*ASSOCIATIVITY*SETS*PARTITIONS = 32KiB */ +#define L1D_DESCRIPTOR CPUID_2_L1D_32KB_8WAY_64B +/*FIXME: CPUID leaf 0x80000005 is inconsistent with leaves 2 & 4 */ +#define L1D_LINES_PER_TAG      1 +#define L1D_SIZE_KB_AMD       64 +#define L1D_ASSOCIATIVITY_AMD  2 + +/* L1 instruction cache: */ +#define L1I_LINE_SIZE         64 +#define L1I_ASSOCIATIVITY      8 +#define L1I_SETS              64 +#define L1I_PARTITIONS         1 +/* Size = LINE_SIZE*ASSOCIATIVITY*SETS*PARTITIONS = 32KiB */ +#define L1I_DESCRIPTOR CPUID_2_L1I_32KB_8WAY_64B +/*FIXME: CPUID leaf 0x80000005 is inconsistent with leaves 2 & 4 */ +#define L1I_LINES_PER_TAG      1 +#define L1I_SIZE_KB_AMD       64 +#define L1I_ASSOCIATIVITY_AMD  2 + +/* Level 2 unified cache: */ +#define L2_LINE_SIZE          64 +#define L2_ASSOCIATIVITY      16 +#define L2_SETS             4096 +#define L2_PARTITIONS          1 +/* Size = LINE_SIZE*ASSOCIATIVITY*SETS*PARTITIONS = 4MiB */ +/*FIXME: CPUID leaf 2 descriptor is inconsistent with CPUID leaf 4 */ +#define L2_DESCRIPTOR CPUID_2_L2_2MB_8WAY_64B +/*FIXME: CPUID leaf 0x80000006 is inconsistent with leaves 2 & 4 */ +#define L2_LINES_PER_TAG       1 +#define L2_SIZE_KB_AMD       512 + +/* No L3 cache: */ +#define L3_SIZE_KB             0 /* disabled */ +#define L3_ASSOCIATIVITY       0 /* disabled */ +#define L3_LINES_PER_TAG       0 /* disabled */ +#define L3_LINE_SIZE           0 /* disabled */ + +/* TLB definitions: */ + +#define L1_DTLB_2M_ASSOC       1 +#define L1_DTLB_2M_ENTRIES   255 +#define L1_DTLB_4K_ASSOC       1 +#define L1_DTLB_4K_ENTRIES   255 + +#define L1_ITLB_2M_ASSOC       1 +#define L1_ITLB_2M_ENTRIES   255 +#define L1_ITLB_4K_ASSOC       1 +#define L1_ITLB_4K_ENTRIES   255 + +#define L2_DTLB_2M_ASSOC       0 /* disabled */ +#define L2_DTLB_2M_ENTRIES     0 /* disabled */ +#define L2_DTLB_4K_ASSOC       4 +#define L2_DTLB_4K_ENTRIES   512 + +#define L2_ITLB_2M_ASSOC       0 /* disabled */ +#define L2_ITLB_2M_ENTRIES     0 /* disabled */ +#define L2_ITLB_4K_ASSOC       4 +#define L2_ITLB_4K_ENTRIES   512 + + + +static void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, +                                     uint32_t vendor2, uint32_t vendor3) +{ +    int i; +    for (i = 0; i < 4; i++) { +        dst[i] = vendor1 >> (8 * i); +        dst[i + 4] = vendor2 >> (8 * i); +        dst[i + 8] = vendor3 >> (8 * i); +    } +    dst[CPUID_VENDOR_SZ] = '\0'; +} + +/* feature flags taken from "Intel Processor Identification and the CPUID + * Instruction" and AMD's "CPUID Specification".  In cases of disagreement + * between feature naming conventions, aliases may be added. + */ +static const char *feature_name[] = { +    "fpu", "vme", "de", "pse", +    "tsc", "msr", "pae", "mce", +    "cx8", "apic", NULL, "sep", +    "mtrr", "pge", "mca", "cmov", +    "pat", "pse36", "pn" /* Intel psn */, "clflush" /* Intel clfsh */, +    NULL, "ds" /* Intel dts */, "acpi", "mmx", +    "fxsr", "sse", "sse2", "ss", +    "ht" /* Intel htt */, "tm", "ia64", "pbe", +}; +static const char *ext_feature_name[] = { +    "pni|sse3" /* Intel,AMD sse3 */, "pclmulqdq|pclmuldq", "dtes64", "monitor", +    "ds_cpl", "vmx", "smx", "est", +    "tm2", "ssse3", "cid", NULL, +    "fma", "cx16", "xtpr", "pdcm", +    NULL, "pcid", "dca", "sse4.1|sse4_1", +    "sse4.2|sse4_2", "x2apic", "movbe", "popcnt", +    "tsc-deadline", "aes", "xsave", "osxsave", +    "avx", "f16c", "rdrand", "hypervisor", +}; +/* Feature names that are already defined on feature_name[] but are set on + * CPUID[8000_0001].EDX on AMD CPUs don't have their names on + * ext2_feature_name[]. They are copied automatically to cpuid_ext2_features + * if and only if CPU vendor is AMD. + */ +static const char *ext2_feature_name[] = { +    NULL /* fpu */, NULL /* vme */, NULL /* de */, NULL /* pse */, +    NULL /* tsc */, NULL /* msr */, NULL /* pae */, NULL /* mce */, +    NULL /* cx8 */ /* AMD CMPXCHG8B */, NULL /* apic */, NULL, "syscall", +    NULL /* mtrr */, NULL /* pge */, NULL /* mca */, NULL /* cmov */, +    NULL /* pat */, NULL /* pse36 */, NULL, NULL /* Linux mp */, +    "nx|xd", NULL, "mmxext", NULL /* mmx */, +    NULL /* fxsr */, "fxsr_opt|ffxsr", "pdpe1gb" /* AMD Page1GB */, "rdtscp", +    NULL, "lm|i64", "3dnowext", "3dnow", +}; +static const char *ext3_feature_name[] = { +    "lahf_lm" /* AMD LahfSahf */, "cmp_legacy", "svm", "extapic" /* AMD ExtApicSpace */, +    "cr8legacy" /* AMD AltMovCr8 */, "abm", "sse4a", "misalignsse", +    "3dnowprefetch", "osvw", "ibs", "xop", +    "skinit", "wdt", NULL, "lwp", +    "fma4", "tce", NULL, "nodeid_msr", +    NULL, "tbm", "topoext", "perfctr_core", +    "perfctr_nb", NULL, NULL, NULL, +    NULL, NULL, NULL, NULL, +}; + +static const char *ext4_feature_name[] = { +    NULL, NULL, "xstore", "xstore-en", +    NULL, NULL, "xcrypt", "xcrypt-en", +    "ace2", "ace2-en", "phe", "phe-en", +    "pmm", "pmm-en", NULL, NULL, +    NULL, NULL, NULL, NULL, +    NULL, NULL, NULL, NULL, +    NULL, NULL, NULL, NULL, +    NULL, NULL, NULL, NULL, +}; + +static const char *kvm_feature_name[] = { +    "kvmclock", "kvm_nopiodelay", "kvm_mmu", "kvmclock", +    "kvm_asyncpf", "kvm_steal_time", "kvm_pv_eoi", "kvm_pv_unhalt", +    NULL, NULL, NULL, NULL, +    NULL, NULL, NULL, NULL, +    NULL, NULL, NULL, NULL, +    NULL, NULL, NULL, NULL, +    "kvmclock-stable-bit", NULL, NULL, NULL, +    NULL, NULL, NULL, NULL, +}; + +static const char *svm_feature_name[] = { +    "npt", "lbrv", "svm_lock", "nrip_save", +    "tsc_scale", "vmcb_clean",  "flushbyasid", "decodeassists", +    NULL, NULL, "pause_filter", NULL, +    "pfthreshold", NULL, NULL, NULL, +    NULL, NULL, NULL, NULL, +    NULL, NULL, NULL, NULL, +    NULL, NULL, NULL, NULL, +    NULL, NULL, NULL, NULL, +}; + +static const char *cpuid_7_0_ebx_feature_name[] = { +    "fsgsbase", "tsc_adjust", NULL, "bmi1", "hle", "avx2", NULL, "smep", +    "bmi2", "erms", "invpcid", "rtm", NULL, NULL, "mpx", NULL, +    "avx512f", NULL, "rdseed", "adx", "smap", NULL, NULL, NULL, +    NULL, NULL, "avx512pf", "avx512er", "avx512cd", NULL, NULL, NULL, +}; + +static const char *cpuid_apm_edx_feature_name[] = { +    NULL, NULL, NULL, NULL, +    NULL, NULL, NULL, NULL, +    "invtsc", NULL, NULL, NULL, +    NULL, NULL, NULL, NULL, +    NULL, NULL, NULL, NULL, +    NULL, NULL, NULL, NULL, +    NULL, NULL, NULL, NULL, +    NULL, NULL, NULL, NULL, +}; + +static const char *cpuid_xsave_feature_name[] = { +    "xsaveopt", "xsavec", "xgetbv1", "xsaves", +    NULL, NULL, NULL, NULL, +    NULL, NULL, NULL, NULL, +    NULL, NULL, NULL, NULL, +    NULL, NULL, NULL, NULL, +    NULL, NULL, NULL, NULL, +    NULL, NULL, NULL, NULL, +    NULL, NULL, NULL, NULL, +}; + +static const char *cpuid_6_feature_name[] = { +    NULL, NULL, "arat", NULL, +    NULL, NULL, NULL, NULL, +    NULL, NULL, NULL, NULL, +    NULL, NULL, NULL, NULL, +    NULL, NULL, NULL, NULL, +    NULL, NULL, NULL, NULL, +    NULL, NULL, NULL, NULL, +    NULL, NULL, NULL, NULL, +}; + +#define I486_FEATURES (CPUID_FP87 | CPUID_VME | CPUID_PSE) +#define PENTIUM_FEATURES (I486_FEATURES | CPUID_DE | CPUID_TSC | \ +          CPUID_MSR | CPUID_MCE | CPUID_CX8 | CPUID_MMX | CPUID_APIC) +#define PENTIUM2_FEATURES (PENTIUM_FEATURES | CPUID_PAE | CPUID_SEP | \ +          CPUID_MTRR | CPUID_PGE | CPUID_MCA | CPUID_CMOV | CPUID_PAT | \ +          CPUID_PSE36 | CPUID_FXSR) +#define PENTIUM3_FEATURES (PENTIUM2_FEATURES | CPUID_SSE) +#define PPRO_FEATURES (CPUID_FP87 | CPUID_DE | CPUID_PSE | CPUID_TSC | \ +          CPUID_MSR | CPUID_MCE | CPUID_CX8 | CPUID_PGE | CPUID_CMOV | \ +          CPUID_PAT | CPUID_FXSR | CPUID_MMX | CPUID_SSE | CPUID_SSE2 | \ +          CPUID_PAE | CPUID_SEP | CPUID_APIC) + +#define TCG_FEATURES (CPUID_FP87 | CPUID_PSE | CPUID_TSC | CPUID_MSR | \ +          CPUID_PAE | CPUID_MCE | CPUID_CX8 | CPUID_APIC | CPUID_SEP | \ +          CPUID_MTRR | CPUID_PGE | CPUID_MCA | CPUID_CMOV | CPUID_PAT | \ +          CPUID_PSE36 | CPUID_CLFLUSH | CPUID_ACPI | CPUID_MMX | \ +          CPUID_FXSR | CPUID_SSE | CPUID_SSE2 | CPUID_SS) +          /* partly implemented: +          CPUID_MTRR, CPUID_MCA, CPUID_CLFLUSH (needed for Win64) */ +          /* missing: +          CPUID_VME, CPUID_DTS, CPUID_SS, CPUID_HT, CPUID_TM, CPUID_PBE */ +#define TCG_EXT_FEATURES (CPUID_EXT_SSE3 | CPUID_EXT_PCLMULQDQ | \ +          CPUID_EXT_MONITOR | CPUID_EXT_SSSE3 | CPUID_EXT_CX16 | \ +          CPUID_EXT_SSE41 | CPUID_EXT_SSE42 | CPUID_EXT_POPCNT | \ +          CPUID_EXT_MOVBE | CPUID_EXT_AES | CPUID_EXT_HYPERVISOR) +          /* missing: +          CPUID_EXT_DTES64, CPUID_EXT_DSCPL, CPUID_EXT_VMX, CPUID_EXT_SMX, +          CPUID_EXT_EST, CPUID_EXT_TM2, CPUID_EXT_CID, CPUID_EXT_FMA, +          CPUID_EXT_XTPR, CPUID_EXT_PDCM, CPUID_EXT_PCID, CPUID_EXT_DCA, +          CPUID_EXT_X2APIC, CPUID_EXT_TSC_DEADLINE_TIMER, CPUID_EXT_XSAVE, +          CPUID_EXT_OSXSAVE, CPUID_EXT_AVX, CPUID_EXT_F16C, +          CPUID_EXT_RDRAND */ + +#ifdef TARGET_X86_64 +#define TCG_EXT2_X86_64_FEATURES (CPUID_EXT2_SYSCALL | CPUID_EXT2_LM) +#else +#define TCG_EXT2_X86_64_FEATURES 0 +#endif + +#define TCG_EXT2_FEATURES ((TCG_FEATURES & CPUID_EXT2_AMD_ALIASES) | \ +          CPUID_EXT2_NX | CPUID_EXT2_MMXEXT | CPUID_EXT2_RDTSCP | \ +          CPUID_EXT2_3DNOW | CPUID_EXT2_3DNOWEXT | CPUID_EXT2_PDPE1GB | \ +          TCG_EXT2_X86_64_FEATURES) +#define TCG_EXT3_FEATURES (CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM | \ +          CPUID_EXT3_CR8LEG | CPUID_EXT3_ABM | CPUID_EXT3_SSE4A) +#define TCG_EXT4_FEATURES 0 +#define TCG_SVM_FEATURES 0 +#define TCG_KVM_FEATURES 0 +#define TCG_7_0_EBX_FEATURES (CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_SMAP | \ +          CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ADX) +          /* missing: +          CPUID_7_0_EBX_FSGSBASE, CPUID_7_0_EBX_HLE, CPUID_7_0_EBX_AVX2, +          CPUID_7_0_EBX_ERMS, CPUID_7_0_EBX_INVPCID, CPUID_7_0_EBX_RTM, +          CPUID_7_0_EBX_RDSEED */ +#define TCG_APM_FEATURES 0 +#define TCG_6_EAX_FEATURES CPUID_6_EAX_ARAT + + +typedef struct FeatureWordInfo { +    const char **feat_names; +    uint32_t cpuid_eax;   /* Input EAX for CPUID */ +    bool cpuid_needs_ecx; /* CPUID instruction uses ECX as input */ +    uint32_t cpuid_ecx;   /* Input ECX value for CPUID */ +    int cpuid_reg;        /* output register (R_* constant) */ +    uint32_t tcg_features; /* Feature flags supported by TCG */ +    uint32_t unmigratable_flags; /* Feature flags known to be unmigratable */ +} FeatureWordInfo; + +static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { +    [FEAT_1_EDX] = { +        .feat_names = feature_name, +        .cpuid_eax = 1, .cpuid_reg = R_EDX, +        .tcg_features = TCG_FEATURES, +    }, +    [FEAT_1_ECX] = { +        .feat_names = ext_feature_name, +        .cpuid_eax = 1, .cpuid_reg = R_ECX, +        .tcg_features = TCG_EXT_FEATURES, +    }, +    [FEAT_8000_0001_EDX] = { +        .feat_names = ext2_feature_name, +        .cpuid_eax = 0x80000001, .cpuid_reg = R_EDX, +        .tcg_features = TCG_EXT2_FEATURES, +    }, +    [FEAT_8000_0001_ECX] = { +        .feat_names = ext3_feature_name, +        .cpuid_eax = 0x80000001, .cpuid_reg = R_ECX, +        .tcg_features = TCG_EXT3_FEATURES, +    }, +    [FEAT_C000_0001_EDX] = { +        .feat_names = ext4_feature_name, +        .cpuid_eax = 0xC0000001, .cpuid_reg = R_EDX, +        .tcg_features = TCG_EXT4_FEATURES, +    }, +    [FEAT_KVM] = { +        .feat_names = kvm_feature_name, +        .cpuid_eax = KVM_CPUID_FEATURES, .cpuid_reg = R_EAX, +        .tcg_features = TCG_KVM_FEATURES, +    }, +    [FEAT_SVM] = { +        .feat_names = svm_feature_name, +        .cpuid_eax = 0x8000000A, .cpuid_reg = R_EDX, +        .tcg_features = TCG_SVM_FEATURES, +    }, +    [FEAT_7_0_EBX] = { +        .feat_names = cpuid_7_0_ebx_feature_name, +        .cpuid_eax = 7, +        .cpuid_needs_ecx = true, .cpuid_ecx = 0, +        .cpuid_reg = R_EBX, +        .tcg_features = TCG_7_0_EBX_FEATURES, +    }, +    [FEAT_8000_0007_EDX] = { +        .feat_names = cpuid_apm_edx_feature_name, +        .cpuid_eax = 0x80000007, +        .cpuid_reg = R_EDX, +        .tcg_features = TCG_APM_FEATURES, +        .unmigratable_flags = CPUID_APM_INVTSC, +    }, +    [FEAT_XSAVE] = { +        .feat_names = cpuid_xsave_feature_name, +        .cpuid_eax = 0xd, +        .cpuid_needs_ecx = true, .cpuid_ecx = 1, +        .cpuid_reg = R_EAX, +        .tcg_features = 0, +    }, +    [FEAT_6_EAX] = { +        .feat_names = cpuid_6_feature_name, +        .cpuid_eax = 6, .cpuid_reg = R_EAX, +        .tcg_features = TCG_6_EAX_FEATURES, +    }, +}; + +typedef struct X86RegisterInfo32 { +    /* Name of register */ +    const char *name; +    /* QAPI enum value register */ +    X86CPURegister32 qapi_enum; +} X86RegisterInfo32; + +#define REGISTER(reg) \ +    [R_##reg] = { .name = #reg, .qapi_enum = X86_CPU_REGISTER32_##reg } +static const X86RegisterInfo32 x86_reg_info_32[CPU_NB_REGS32] = { +    REGISTER(EAX), +    REGISTER(ECX), +    REGISTER(EDX), +    REGISTER(EBX), +    REGISTER(ESP), +    REGISTER(EBP), +    REGISTER(ESI), +    REGISTER(EDI), +}; +#undef REGISTER + +typedef struct ExtSaveArea { +    uint32_t feature, bits; +    uint32_t offset, size; +} ExtSaveArea; + +static const ExtSaveArea ext_save_areas[] = { +    [2] = { .feature = FEAT_1_ECX, .bits = CPUID_EXT_AVX, +            .offset = 0x240, .size = 0x100 }, +    [3] = { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_MPX, +            .offset = 0x3c0, .size = 0x40  }, +    [4] = { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_MPX, +            .offset = 0x400, .size = 0x40  }, +    [5] = { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_AVX512F, +            .offset = 0x440, .size = 0x40 }, +    [6] = { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_AVX512F, +            .offset = 0x480, .size = 0x200 }, +    [7] = { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_AVX512F, +            .offset = 0x680, .size = 0x400 }, +}; + +const char *get_register_name_32(unsigned int reg) +{ +    if (reg >= CPU_NB_REGS32) { +        return NULL; +    } +    return x86_reg_info_32[reg].name; +} + +/* KVM-specific features that are automatically added to all CPU models + * when KVM is enabled. + */ +static uint32_t kvm_default_features[FEATURE_WORDS] = { +    [FEAT_KVM] = (1 << KVM_FEATURE_CLOCKSOURCE) | +        (1 << KVM_FEATURE_NOP_IO_DELAY) | +        (1 << KVM_FEATURE_CLOCKSOURCE2) | +        (1 << KVM_FEATURE_ASYNC_PF) | +        (1 << KVM_FEATURE_STEAL_TIME) | +        (1 << KVM_FEATURE_PV_EOI) | +        (1 << KVM_FEATURE_CLOCKSOURCE_STABLE_BIT), +    [FEAT_1_ECX] = CPUID_EXT_X2APIC, +}; + +/* Features that are not added by default to any CPU model when KVM is enabled. + */ +static uint32_t kvm_default_unset_features[FEATURE_WORDS] = { +    [FEAT_1_EDX] = CPUID_ACPI, +    [FEAT_1_ECX] = CPUID_EXT_MONITOR, +    [FEAT_8000_0001_ECX] = CPUID_EXT3_SVM, +}; + +void x86_cpu_compat_kvm_no_autoenable(FeatureWord w, uint32_t features) +{ +    kvm_default_features[w] &= ~features; +} + +void x86_cpu_compat_kvm_no_autodisable(FeatureWord w, uint32_t features) +{ +    kvm_default_unset_features[w] &= ~features; +} + +/* + * Returns the set of feature flags that are supported and migratable by + * QEMU, for a given FeatureWord. + */ +static uint32_t x86_cpu_get_migratable_flags(FeatureWord w) +{ +    FeatureWordInfo *wi = &feature_word_info[w]; +    uint32_t r = 0; +    int i; + +    for (i = 0; i < 32; i++) { +        uint32_t f = 1U << i; +        /* If the feature name is unknown, it is not supported by QEMU yet */ +        if (!wi->feat_names[i]) { +            continue; +        } +        /* Skip features known to QEMU, but explicitly marked as unmigratable */ +        if (wi->unmigratable_flags & f) { +            continue; +        } +        r |= f; +    } +    return r; +} + +void host_cpuid(uint32_t function, uint32_t count, +                uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) +{ +    uint32_t vec[4]; + +#ifdef __x86_64__ +    asm volatile("cpuid" +                 : "=a"(vec[0]), "=b"(vec[1]), +                   "=c"(vec[2]), "=d"(vec[3]) +                 : "0"(function), "c"(count) : "cc"); +#elif defined(__i386__) +    asm volatile("pusha \n\t" +                 "cpuid \n\t" +                 "mov %%eax, 0(%2) \n\t" +                 "mov %%ebx, 4(%2) \n\t" +                 "mov %%ecx, 8(%2) \n\t" +                 "mov %%edx, 12(%2) \n\t" +                 "popa" +                 : : "a"(function), "c"(count), "S"(vec) +                 : "memory", "cc"); +#else +    abort(); +#endif + +    if (eax) +        *eax = vec[0]; +    if (ebx) +        *ebx = vec[1]; +    if (ecx) +        *ecx = vec[2]; +    if (edx) +        *edx = vec[3]; +} + +#define iswhite(c) ((c) && ((c) <= ' ' || '~' < (c))) + +/* general substring compare of *[s1..e1) and *[s2..e2).  sx is start of + * a substring.  ex if !NULL points to the first char after a substring, + * otherwise the string is assumed to sized by a terminating nul. + * Return lexical ordering of *s1:*s2. + */ +static int sstrcmp(const char *s1, const char *e1, +                   const char *s2, const char *e2) +{ +    for (;;) { +        if (!*s1 || !*s2 || *s1 != *s2) +            return (*s1 - *s2); +        ++s1, ++s2; +        if (s1 == e1 && s2 == e2) +            return (0); +        else if (s1 == e1) +            return (*s2); +        else if (s2 == e2) +            return (*s1); +    } +} + +/* compare *[s..e) to *altstr.  *altstr may be a simple string or multiple + * '|' delimited (possibly empty) strings in which case search for a match + * within the alternatives proceeds left to right.  Return 0 for success, + * non-zero otherwise. + */ +static int altcmp(const char *s, const char *e, const char *altstr) +{ +    const char *p, *q; + +    for (q = p = altstr; ; ) { +        while (*p && *p != '|') +            ++p; +        if ((q == p && !*s) || (q != p && !sstrcmp(s, e, q, p))) +            return (0); +        if (!*p) +            return (1); +        else +            q = ++p; +    } +} + +/* search featureset for flag *[s..e), if found set corresponding bit in + * *pval and return true, otherwise return false + */ +static bool lookup_feature(uint32_t *pval, const char *s, const char *e, +                           const char **featureset) +{ +    uint32_t mask; +    const char **ppc; +    bool found = false; + +    for (mask = 1, ppc = featureset; mask; mask <<= 1, ++ppc) { +        if (*ppc && !altcmp(s, e, *ppc)) { +            *pval |= mask; +            found = true; +        } +    } +    return found; +} + +static void add_flagname_to_bitmaps(const char *flagname, +                                    FeatureWordArray words, +                                    Error **errp) +{ +    FeatureWord w; +    for (w = 0; w < FEATURE_WORDS; w++) { +        FeatureWordInfo *wi = &feature_word_info[w]; +        if (wi->feat_names && +            lookup_feature(&words[w], flagname, NULL, wi->feat_names)) { +            break; +        } +    } +    if (w == FEATURE_WORDS) { +        error_setg(errp, "CPU feature %s not found", flagname); +    } +} + +/* CPU class name definitions: */ + +#define X86_CPU_TYPE_SUFFIX "-" TYPE_X86_CPU +#define X86_CPU_TYPE_NAME(name) (name X86_CPU_TYPE_SUFFIX) + +/* Return type name for a given CPU model name + * Caller is responsible for freeing the returned string. + */ +static char *x86_cpu_type_name(const char *model_name) +{ +    return g_strdup_printf(X86_CPU_TYPE_NAME("%s"), model_name); +} + +static ObjectClass *x86_cpu_class_by_name(const char *cpu_model) +{ +    ObjectClass *oc; +    char *typename; + +    if (cpu_model == NULL) { +        return NULL; +    } + +    typename = x86_cpu_type_name(cpu_model); +    oc = object_class_by_name(typename); +    g_free(typename); +    return oc; +} + +struct X86CPUDefinition { +    const char *name; +    uint32_t level; +    uint32_t xlevel; +    uint32_t xlevel2; +    /* vendor is zero-terminated, 12 character ASCII string */ +    char vendor[CPUID_VENDOR_SZ + 1]; +    int family; +    int model; +    int stepping; +    FeatureWordArray features; +    char model_id[48]; +    bool cache_info_passthrough; +}; + +static X86CPUDefinition builtin_x86_defs[] = { +    { +        .name = "qemu64", +        .level = 0xd, +        .vendor = CPUID_VENDOR_AMD, +        .family = 6, +        .model = 6, +        .stepping = 3, +        .features[FEAT_1_EDX] = +            PPRO_FEATURES | +            CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | +            CPUID_PSE36, +        .features[FEAT_1_ECX] = +            CPUID_EXT_SSE3 | CPUID_EXT_CX16 | CPUID_EXT_POPCNT, +        .features[FEAT_8000_0001_EDX] = +            CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX, +        .features[FEAT_8000_0001_ECX] = +            CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM | +            CPUID_EXT3_ABM | CPUID_EXT3_SSE4A, +        .xlevel = 0x8000000A, +    }, +    { +        .name = "phenom", +        .level = 5, +        .vendor = CPUID_VENDOR_AMD, +        .family = 16, +        .model = 2, +        .stepping = 3, +        /* Missing: CPUID_HT */ +        .features[FEAT_1_EDX] = +            PPRO_FEATURES | +            CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | +            CPUID_PSE36 | CPUID_VME, +        .features[FEAT_1_ECX] = +            CPUID_EXT_SSE3 | CPUID_EXT_MONITOR | CPUID_EXT_CX16 | +            CPUID_EXT_POPCNT, +        .features[FEAT_8000_0001_EDX] = +            CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX | +            CPUID_EXT2_3DNOW | CPUID_EXT2_3DNOWEXT | CPUID_EXT2_MMXEXT | +            CPUID_EXT2_FFXSR | CPUID_EXT2_PDPE1GB | CPUID_EXT2_RDTSCP, +        /* Missing: CPUID_EXT3_CMP_LEG, CPUID_EXT3_EXTAPIC, +                    CPUID_EXT3_CR8LEG, +                    CPUID_EXT3_MISALIGNSSE, CPUID_EXT3_3DNOWPREFETCH, +                    CPUID_EXT3_OSVW, CPUID_EXT3_IBS */ +        .features[FEAT_8000_0001_ECX] = +            CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM | +            CPUID_EXT3_ABM | CPUID_EXT3_SSE4A, +        /* Missing: CPUID_SVM_LBRV */ +        .features[FEAT_SVM] = +            CPUID_SVM_NPT, +        .xlevel = 0x8000001A, +        .model_id = "AMD Phenom(tm) 9550 Quad-Core Processor" +    }, +    { +        .name = "core2duo", +        .level = 10, +        .vendor = CPUID_VENDOR_INTEL, +        .family = 6, +        .model = 15, +        .stepping = 11, +        /* Missing: CPUID_DTS, CPUID_HT, CPUID_TM, CPUID_PBE */ +        .features[FEAT_1_EDX] = +            PPRO_FEATURES | +            CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | +            CPUID_PSE36 | CPUID_VME | CPUID_ACPI | CPUID_SS, +        /* Missing: CPUID_EXT_DTES64, CPUID_EXT_DSCPL, CPUID_EXT_EST, +         * CPUID_EXT_TM2, CPUID_EXT_XTPR, CPUID_EXT_PDCM, CPUID_EXT_VMX */ +        .features[FEAT_1_ECX] = +            CPUID_EXT_SSE3 | CPUID_EXT_MONITOR | CPUID_EXT_SSSE3 | +            CPUID_EXT_CX16, +        .features[FEAT_8000_0001_EDX] = +            CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX, +        .features[FEAT_8000_0001_ECX] = +            CPUID_EXT3_LAHF_LM, +        .xlevel = 0x80000008, +        .model_id = "Intel(R) Core(TM)2 Duo CPU     T7700  @ 2.40GHz", +    }, +    { +        .name = "kvm64", +        .level = 0xd, +        .vendor = CPUID_VENDOR_INTEL, +        .family = 15, +        .model = 6, +        .stepping = 1, +        /* Missing: CPUID_HT */ +        .features[FEAT_1_EDX] = +            PPRO_FEATURES | CPUID_VME | +            CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | +            CPUID_PSE36, +        /* Missing: CPUID_EXT_POPCNT, CPUID_EXT_MONITOR */ +        .features[FEAT_1_ECX] = +            CPUID_EXT_SSE3 | CPUID_EXT_CX16, +        /* Missing: CPUID_EXT2_PDPE1GB, CPUID_EXT2_RDTSCP */ +        .features[FEAT_8000_0001_EDX] = +            CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX, +        /* Missing: CPUID_EXT3_LAHF_LM, CPUID_EXT3_CMP_LEG, CPUID_EXT3_EXTAPIC, +                    CPUID_EXT3_CR8LEG, CPUID_EXT3_ABM, CPUID_EXT3_SSE4A, +                    CPUID_EXT3_MISALIGNSSE, CPUID_EXT3_3DNOWPREFETCH, +                    CPUID_EXT3_OSVW, CPUID_EXT3_IBS, CPUID_EXT3_SVM */ +        .features[FEAT_8000_0001_ECX] = +            0, +        .xlevel = 0x80000008, +        .model_id = "Common KVM processor" +    }, +    { +        .name = "qemu32", +        .level = 4, +        .vendor = CPUID_VENDOR_INTEL, +        .family = 6, +        .model = 6, +        .stepping = 3, +        .features[FEAT_1_EDX] = +            PPRO_FEATURES, +        .features[FEAT_1_ECX] = +            CPUID_EXT_SSE3 | CPUID_EXT_POPCNT, +        .xlevel = 0x80000004, +    }, +    { +        .name = "kvm32", +        .level = 5, +        .vendor = CPUID_VENDOR_INTEL, +        .family = 15, +        .model = 6, +        .stepping = 1, +        .features[FEAT_1_EDX] = +            PPRO_FEATURES | CPUID_VME | +            CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | CPUID_PSE36, +        .features[FEAT_1_ECX] = +            CPUID_EXT_SSE3, +        .features[FEAT_8000_0001_ECX] = +            0, +        .xlevel = 0x80000008, +        .model_id = "Common 32-bit KVM processor" +    }, +    { +        .name = "coreduo", +        .level = 10, +        .vendor = CPUID_VENDOR_INTEL, +        .family = 6, +        .model = 14, +        .stepping = 8, +        /* Missing: CPUID_DTS, CPUID_HT, CPUID_TM, CPUID_PBE */ +        .features[FEAT_1_EDX] = +            PPRO_FEATURES | CPUID_VME | +            CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | CPUID_ACPI | +            CPUID_SS, +        /* Missing: CPUID_EXT_EST, CPUID_EXT_TM2 , CPUID_EXT_XTPR, +         * CPUID_EXT_PDCM, CPUID_EXT_VMX */ +        .features[FEAT_1_ECX] = +            CPUID_EXT_SSE3 | CPUID_EXT_MONITOR, +        .features[FEAT_8000_0001_EDX] = +            CPUID_EXT2_NX, +        .xlevel = 0x80000008, +        .model_id = "Genuine Intel(R) CPU           T2600  @ 2.16GHz", +    }, +    { +        .name = "486", +        .level = 1, +        .vendor = CPUID_VENDOR_INTEL, +        .family = 4, +        .model = 8, +        .stepping = 0, +        .features[FEAT_1_EDX] = +            I486_FEATURES, +        .xlevel = 0, +    }, +    { +        .name = "pentium", +        .level = 1, +        .vendor = CPUID_VENDOR_INTEL, +        .family = 5, +        .model = 4, +        .stepping = 3, +        .features[FEAT_1_EDX] = +            PENTIUM_FEATURES, +        .xlevel = 0, +    }, +    { +        .name = "pentium2", +        .level = 2, +        .vendor = CPUID_VENDOR_INTEL, +        .family = 6, +        .model = 5, +        .stepping = 2, +        .features[FEAT_1_EDX] = +            PENTIUM2_FEATURES, +        .xlevel = 0, +    }, +    { +        .name = "pentium3", +        .level = 3, +        .vendor = CPUID_VENDOR_INTEL, +        .family = 6, +        .model = 7, +        .stepping = 3, +        .features[FEAT_1_EDX] = +            PENTIUM3_FEATURES, +        .xlevel = 0, +    }, +    { +        .name = "athlon", +        .level = 2, +        .vendor = CPUID_VENDOR_AMD, +        .family = 6, +        .model = 2, +        .stepping = 3, +        .features[FEAT_1_EDX] = +            PPRO_FEATURES | CPUID_PSE36 | CPUID_VME | CPUID_MTRR | +            CPUID_MCA, +        .features[FEAT_8000_0001_EDX] = +            CPUID_EXT2_MMXEXT | CPUID_EXT2_3DNOW | CPUID_EXT2_3DNOWEXT, +        .xlevel = 0x80000008, +    }, +    { +        .name = "n270", +        .level = 10, +        .vendor = CPUID_VENDOR_INTEL, +        .family = 6, +        .model = 28, +        .stepping = 2, +        /* Missing: CPUID_DTS, CPUID_HT, CPUID_TM, CPUID_PBE */ +        .features[FEAT_1_EDX] = +            PPRO_FEATURES | +            CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | CPUID_VME | +            CPUID_ACPI | CPUID_SS, +            /* Some CPUs got no CPUID_SEP */ +        /* Missing: CPUID_EXT_DSCPL, CPUID_EXT_EST, CPUID_EXT_TM2, +         * CPUID_EXT_XTPR */ +        .features[FEAT_1_ECX] = +            CPUID_EXT_SSE3 | CPUID_EXT_MONITOR | CPUID_EXT_SSSE3 | +            CPUID_EXT_MOVBE, +        .features[FEAT_8000_0001_EDX] = +            CPUID_EXT2_NX, +        .features[FEAT_8000_0001_ECX] = +            CPUID_EXT3_LAHF_LM, +        .xlevel = 0x80000008, +        .model_id = "Intel(R) Atom(TM) CPU N270   @ 1.60GHz", +    }, +    { +        .name = "Conroe", +        .level = 10, +        .vendor = CPUID_VENDOR_INTEL, +        .family = 6, +        .model = 15, +        .stepping = 3, +        .features[FEAT_1_EDX] = +            CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | +            CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | +            CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | +            CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | +            CPUID_DE | CPUID_FP87, +        .features[FEAT_1_ECX] = +            CPUID_EXT_SSSE3 | CPUID_EXT_SSE3, +        .features[FEAT_8000_0001_EDX] = +            CPUID_EXT2_LM | CPUID_EXT2_NX | CPUID_EXT2_SYSCALL, +        .features[FEAT_8000_0001_ECX] = +            CPUID_EXT3_LAHF_LM, +        .xlevel = 0x80000008, +        .model_id = "Intel Celeron_4x0 (Conroe/Merom Class Core 2)", +    }, +    { +        .name = "Penryn", +        .level = 10, +        .vendor = CPUID_VENDOR_INTEL, +        .family = 6, +        .model = 23, +        .stepping = 3, +        .features[FEAT_1_EDX] = +            CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | +            CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | +            CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | +            CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | +            CPUID_DE | CPUID_FP87, +        .features[FEAT_1_ECX] = +            CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | +            CPUID_EXT_SSE3, +        .features[FEAT_8000_0001_EDX] = +            CPUID_EXT2_LM | CPUID_EXT2_NX | CPUID_EXT2_SYSCALL, +        .features[FEAT_8000_0001_ECX] = +            CPUID_EXT3_LAHF_LM, +        .xlevel = 0x80000008, +        .model_id = "Intel Core 2 Duo P9xxx (Penryn Class Core 2)", +    }, +    { +        .name = "Nehalem", +        .level = 11, +        .vendor = CPUID_VENDOR_INTEL, +        .family = 6, +        .model = 26, +        .stepping = 3, +        .features[FEAT_1_EDX] = +            CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | +            CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | +            CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | +            CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | +            CPUID_DE | CPUID_FP87, +        .features[FEAT_1_ECX] = +            CPUID_EXT_POPCNT | CPUID_EXT_SSE42 | CPUID_EXT_SSE41 | +            CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | CPUID_EXT_SSE3, +        .features[FEAT_8000_0001_EDX] = +            CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX, +        .features[FEAT_8000_0001_ECX] = +            CPUID_EXT3_LAHF_LM, +        .xlevel = 0x80000008, +        .model_id = "Intel Core i7 9xx (Nehalem Class Core i7)", +    }, +    { +        .name = "Westmere", +        .level = 11, +        .vendor = CPUID_VENDOR_INTEL, +        .family = 6, +        .model = 44, +        .stepping = 1, +        .features[FEAT_1_EDX] = +            CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | +            CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | +            CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | +            CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | +            CPUID_DE | CPUID_FP87, +        .features[FEAT_1_ECX] = +            CPUID_EXT_AES | CPUID_EXT_POPCNT | CPUID_EXT_SSE42 | +            CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | +            CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3, +        .features[FEAT_8000_0001_EDX] = +            CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX, +        .features[FEAT_8000_0001_ECX] = +            CPUID_EXT3_LAHF_LM, +        .features[FEAT_6_EAX] = +            CPUID_6_EAX_ARAT, +        .xlevel = 0x80000008, +        .model_id = "Westmere E56xx/L56xx/X56xx (Nehalem-C)", +    }, +    { +        .name = "SandyBridge", +        .level = 0xd, +        .vendor = CPUID_VENDOR_INTEL, +        .family = 6, +        .model = 42, +        .stepping = 1, +        .features[FEAT_1_EDX] = +            CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | +            CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | +            CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | +            CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | +            CPUID_DE | CPUID_FP87, +        .features[FEAT_1_ECX] = +            CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES | +            CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_POPCNT | +            CPUID_EXT_X2APIC | CPUID_EXT_SSE42 | CPUID_EXT_SSE41 | +            CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | CPUID_EXT_PCLMULQDQ | +            CPUID_EXT_SSE3, +        .features[FEAT_8000_0001_EDX] = +            CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX | +            CPUID_EXT2_SYSCALL, +        .features[FEAT_8000_0001_ECX] = +            CPUID_EXT3_LAHF_LM, +        .features[FEAT_XSAVE] = +            CPUID_XSAVE_XSAVEOPT, +        .features[FEAT_6_EAX] = +            CPUID_6_EAX_ARAT, +        .xlevel = 0x80000008, +        .model_id = "Intel Xeon E312xx (Sandy Bridge)", +    }, +    { +        .name = "IvyBridge", +        .level = 0xd, +        .vendor = CPUID_VENDOR_INTEL, +        .family = 6, +        .model = 58, +        .stepping = 9, +        .features[FEAT_1_EDX] = +            CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | +            CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | +            CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | +            CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | +            CPUID_DE | CPUID_FP87, +        .features[FEAT_1_ECX] = +            CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES | +            CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_POPCNT | +            CPUID_EXT_X2APIC | CPUID_EXT_SSE42 | CPUID_EXT_SSE41 | +            CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | CPUID_EXT_PCLMULQDQ | +            CPUID_EXT_SSE3 | CPUID_EXT_F16C | CPUID_EXT_RDRAND, +        .features[FEAT_7_0_EBX] = +            CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_SMEP | +            CPUID_7_0_EBX_ERMS, +        .features[FEAT_8000_0001_EDX] = +            CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX | +            CPUID_EXT2_SYSCALL, +        .features[FEAT_8000_0001_ECX] = +            CPUID_EXT3_LAHF_LM, +        .features[FEAT_XSAVE] = +            CPUID_XSAVE_XSAVEOPT, +        .features[FEAT_6_EAX] = +            CPUID_6_EAX_ARAT, +        .xlevel = 0x80000008, +        .model_id = "Intel Xeon E3-12xx v2 (Ivy Bridge)", +    }, +    { +        .name = "Haswell-noTSX", +        .level = 0xd, +        .vendor = CPUID_VENDOR_INTEL, +        .family = 6, +        .model = 60, +        .stepping = 1, +        .features[FEAT_1_EDX] = +            CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | +            CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | +            CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | +            CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | +            CPUID_DE | CPUID_FP87, +        .features[FEAT_1_ECX] = +            CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES | +            CPUID_EXT_POPCNT | CPUID_EXT_X2APIC | CPUID_EXT_SSE42 | +            CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | +            CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3 | +            CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_FMA | CPUID_EXT_MOVBE | +            CPUID_EXT_PCID | CPUID_EXT_F16C | CPUID_EXT_RDRAND, +        .features[FEAT_8000_0001_EDX] = +            CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX | +            CPUID_EXT2_SYSCALL, +        .features[FEAT_8000_0001_ECX] = +            CPUID_EXT3_LAHF_LM, +        .features[FEAT_7_0_EBX] = +            CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | +            CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP | +            CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID, +        .features[FEAT_XSAVE] = +            CPUID_XSAVE_XSAVEOPT, +        .features[FEAT_6_EAX] = +            CPUID_6_EAX_ARAT, +        .xlevel = 0x80000008, +        .model_id = "Intel Core Processor (Haswell, no TSX)", +    },    { +        .name = "Haswell", +        .level = 0xd, +        .vendor = CPUID_VENDOR_INTEL, +        .family = 6, +        .model = 60, +        .stepping = 1, +        .features[FEAT_1_EDX] = +            CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | +            CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | +            CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | +            CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | +            CPUID_DE | CPUID_FP87, +        .features[FEAT_1_ECX] = +            CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES | +            CPUID_EXT_POPCNT | CPUID_EXT_X2APIC | CPUID_EXT_SSE42 | +            CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | +            CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3 | +            CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_FMA | CPUID_EXT_MOVBE | +            CPUID_EXT_PCID | CPUID_EXT_F16C | CPUID_EXT_RDRAND, +        .features[FEAT_8000_0001_EDX] = +            CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX | +            CPUID_EXT2_SYSCALL, +        .features[FEAT_8000_0001_ECX] = +            CPUID_EXT3_LAHF_LM, +        .features[FEAT_7_0_EBX] = +            CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | +            CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP | +            CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID | +            CPUID_7_0_EBX_RTM, +        .features[FEAT_XSAVE] = +            CPUID_XSAVE_XSAVEOPT, +        .features[FEAT_6_EAX] = +            CPUID_6_EAX_ARAT, +        .xlevel = 0x80000008, +        .model_id = "Intel Core Processor (Haswell)", +    }, +    { +        .name = "Broadwell-noTSX", +        .level = 0xd, +        .vendor = CPUID_VENDOR_INTEL, +        .family = 6, +        .model = 61, +        .stepping = 2, +        .features[FEAT_1_EDX] = +            CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | +            CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | +            CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | +            CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | +            CPUID_DE | CPUID_FP87, +        .features[FEAT_1_ECX] = +            CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES | +            CPUID_EXT_POPCNT | CPUID_EXT_X2APIC | CPUID_EXT_SSE42 | +            CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | +            CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3 | +            CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_FMA | CPUID_EXT_MOVBE | +            CPUID_EXT_PCID | CPUID_EXT_F16C | CPUID_EXT_RDRAND, +        .features[FEAT_8000_0001_EDX] = +            CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX | +            CPUID_EXT2_SYSCALL, +        .features[FEAT_8000_0001_ECX] = +            CPUID_EXT3_LAHF_LM | CPUID_EXT3_3DNOWPREFETCH, +        .features[FEAT_7_0_EBX] = +            CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | +            CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP | +            CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID | +            CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX | +            CPUID_7_0_EBX_SMAP, +        .features[FEAT_XSAVE] = +            CPUID_XSAVE_XSAVEOPT, +        .features[FEAT_6_EAX] = +            CPUID_6_EAX_ARAT, +        .xlevel = 0x80000008, +        .model_id = "Intel Core Processor (Broadwell, no TSX)", +    }, +    { +        .name = "Broadwell", +        .level = 0xd, +        .vendor = CPUID_VENDOR_INTEL, +        .family = 6, +        .model = 61, +        .stepping = 2, +        .features[FEAT_1_EDX] = +            CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | +            CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | +            CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | +            CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | +            CPUID_DE | CPUID_FP87, +        .features[FEAT_1_ECX] = +            CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES | +            CPUID_EXT_POPCNT | CPUID_EXT_X2APIC | CPUID_EXT_SSE42 | +            CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | +            CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3 | +            CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_FMA | CPUID_EXT_MOVBE | +            CPUID_EXT_PCID | CPUID_EXT_F16C | CPUID_EXT_RDRAND, +        .features[FEAT_8000_0001_EDX] = +            CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX | +            CPUID_EXT2_SYSCALL, +        .features[FEAT_8000_0001_ECX] = +            CPUID_EXT3_LAHF_LM | CPUID_EXT3_3DNOWPREFETCH, +        .features[FEAT_7_0_EBX] = +            CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | +            CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP | +            CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID | +            CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX | +            CPUID_7_0_EBX_SMAP, +        .features[FEAT_XSAVE] = +            CPUID_XSAVE_XSAVEOPT, +        .features[FEAT_6_EAX] = +            CPUID_6_EAX_ARAT, +        .xlevel = 0x80000008, +        .model_id = "Intel Core Processor (Broadwell)", +    }, +    { +        .name = "Opteron_G1", +        .level = 5, +        .vendor = CPUID_VENDOR_AMD, +        .family = 15, +        .model = 6, +        .stepping = 1, +        .features[FEAT_1_EDX] = +            CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | +            CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | +            CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | +            CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | +            CPUID_DE | CPUID_FP87, +        .features[FEAT_1_ECX] = +            CPUID_EXT_SSE3, +        .features[FEAT_8000_0001_EDX] = +            CPUID_EXT2_LM | CPUID_EXT2_FXSR | CPUID_EXT2_MMX | +            CPUID_EXT2_NX | CPUID_EXT2_PSE36 | CPUID_EXT2_PAT | +            CPUID_EXT2_CMOV | CPUID_EXT2_MCA | CPUID_EXT2_PGE | +            CPUID_EXT2_MTRR | CPUID_EXT2_SYSCALL | CPUID_EXT2_APIC | +            CPUID_EXT2_CX8 | CPUID_EXT2_MCE | CPUID_EXT2_PAE | CPUID_EXT2_MSR | +            CPUID_EXT2_TSC | CPUID_EXT2_PSE | CPUID_EXT2_DE | CPUID_EXT2_FPU, +        .xlevel = 0x80000008, +        .model_id = "AMD Opteron 240 (Gen 1 Class Opteron)", +    }, +    { +        .name = "Opteron_G2", +        .level = 5, +        .vendor = CPUID_VENDOR_AMD, +        .family = 15, +        .model = 6, +        .stepping = 1, +        .features[FEAT_1_EDX] = +            CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | +            CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | +            CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | +            CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | +            CPUID_DE | CPUID_FP87, +        .features[FEAT_1_ECX] = +            CPUID_EXT_CX16 | CPUID_EXT_SSE3, +        .features[FEAT_8000_0001_EDX] = +            CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_FXSR | +            CPUID_EXT2_MMX | CPUID_EXT2_NX | CPUID_EXT2_PSE36 | +            CPUID_EXT2_PAT | CPUID_EXT2_CMOV | CPUID_EXT2_MCA | +            CPUID_EXT2_PGE | CPUID_EXT2_MTRR | CPUID_EXT2_SYSCALL | +            CPUID_EXT2_APIC | CPUID_EXT2_CX8 | CPUID_EXT2_MCE | +            CPUID_EXT2_PAE | CPUID_EXT2_MSR | CPUID_EXT2_TSC | CPUID_EXT2_PSE | +            CPUID_EXT2_DE | CPUID_EXT2_FPU, +        .features[FEAT_8000_0001_ECX] = +            CPUID_EXT3_SVM | CPUID_EXT3_LAHF_LM, +        .xlevel = 0x80000008, +        .model_id = "AMD Opteron 22xx (Gen 2 Class Opteron)", +    }, +    { +        .name = "Opteron_G3", +        .level = 5, +        .vendor = CPUID_VENDOR_AMD, +        .family = 15, +        .model = 6, +        .stepping = 1, +        .features[FEAT_1_EDX] = +            CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | +            CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | +            CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | +            CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | +            CPUID_DE | CPUID_FP87, +        .features[FEAT_1_ECX] = +            CPUID_EXT_POPCNT | CPUID_EXT_CX16 | CPUID_EXT_MONITOR | +            CPUID_EXT_SSE3, +        .features[FEAT_8000_0001_EDX] = +            CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_FXSR | +            CPUID_EXT2_MMX | CPUID_EXT2_NX | CPUID_EXT2_PSE36 | +            CPUID_EXT2_PAT | CPUID_EXT2_CMOV | CPUID_EXT2_MCA | +            CPUID_EXT2_PGE | CPUID_EXT2_MTRR | CPUID_EXT2_SYSCALL | +            CPUID_EXT2_APIC | CPUID_EXT2_CX8 | CPUID_EXT2_MCE | +            CPUID_EXT2_PAE | CPUID_EXT2_MSR | CPUID_EXT2_TSC | CPUID_EXT2_PSE | +            CPUID_EXT2_DE | CPUID_EXT2_FPU, +        .features[FEAT_8000_0001_ECX] = +            CPUID_EXT3_MISALIGNSSE | CPUID_EXT3_SSE4A | +            CPUID_EXT3_ABM | CPUID_EXT3_SVM | CPUID_EXT3_LAHF_LM, +        .xlevel = 0x80000008, +        .model_id = "AMD Opteron 23xx (Gen 3 Class Opteron)", +    }, +    { +        .name = "Opteron_G4", +        .level = 0xd, +        .vendor = CPUID_VENDOR_AMD, +        .family = 21, +        .model = 1, +        .stepping = 2, +        .features[FEAT_1_EDX] = +            CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | +            CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | +            CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | +            CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | +            CPUID_DE | CPUID_FP87, +        .features[FEAT_1_ECX] = +            CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES | +            CPUID_EXT_POPCNT | CPUID_EXT_SSE42 | CPUID_EXT_SSE41 | +            CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | CPUID_EXT_PCLMULQDQ | +            CPUID_EXT_SSE3, +        .features[FEAT_8000_0001_EDX] = +            CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | +            CPUID_EXT2_PDPE1GB | CPUID_EXT2_FXSR | CPUID_EXT2_MMX | +            CPUID_EXT2_NX | CPUID_EXT2_PSE36 | CPUID_EXT2_PAT | +            CPUID_EXT2_CMOV | CPUID_EXT2_MCA | CPUID_EXT2_PGE | +            CPUID_EXT2_MTRR | CPUID_EXT2_SYSCALL | CPUID_EXT2_APIC | +            CPUID_EXT2_CX8 | CPUID_EXT2_MCE | CPUID_EXT2_PAE | CPUID_EXT2_MSR | +            CPUID_EXT2_TSC | CPUID_EXT2_PSE | CPUID_EXT2_DE | CPUID_EXT2_FPU, +        .features[FEAT_8000_0001_ECX] = +            CPUID_EXT3_FMA4 | CPUID_EXT3_XOP | +            CPUID_EXT3_3DNOWPREFETCH | CPUID_EXT3_MISALIGNSSE | +            CPUID_EXT3_SSE4A | CPUID_EXT3_ABM | CPUID_EXT3_SVM | +            CPUID_EXT3_LAHF_LM, +        /* no xsaveopt! */ +        .xlevel = 0x8000001A, +        .model_id = "AMD Opteron 62xx class CPU", +    }, +    { +        .name = "Opteron_G5", +        .level = 0xd, +        .vendor = CPUID_VENDOR_AMD, +        .family = 21, +        .model = 2, +        .stepping = 0, +        .features[FEAT_1_EDX] = +            CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | +            CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | +            CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | +            CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | +            CPUID_DE | CPUID_FP87, +        .features[FEAT_1_ECX] = +            CPUID_EXT_F16C | CPUID_EXT_AVX | CPUID_EXT_XSAVE | +            CPUID_EXT_AES | CPUID_EXT_POPCNT | CPUID_EXT_SSE42 | +            CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_FMA | +            CPUID_EXT_SSSE3 | CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3, +        .features[FEAT_8000_0001_EDX] = +            CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | +            CPUID_EXT2_PDPE1GB | CPUID_EXT2_FXSR | CPUID_EXT2_MMX | +            CPUID_EXT2_NX | CPUID_EXT2_PSE36 | CPUID_EXT2_PAT | +            CPUID_EXT2_CMOV | CPUID_EXT2_MCA | CPUID_EXT2_PGE | +            CPUID_EXT2_MTRR | CPUID_EXT2_SYSCALL | CPUID_EXT2_APIC | +            CPUID_EXT2_CX8 | CPUID_EXT2_MCE | CPUID_EXT2_PAE | CPUID_EXT2_MSR | +            CPUID_EXT2_TSC | CPUID_EXT2_PSE | CPUID_EXT2_DE | CPUID_EXT2_FPU, +        .features[FEAT_8000_0001_ECX] = +            CPUID_EXT3_TBM | CPUID_EXT3_FMA4 | CPUID_EXT3_XOP | +            CPUID_EXT3_3DNOWPREFETCH | CPUID_EXT3_MISALIGNSSE | +            CPUID_EXT3_SSE4A | CPUID_EXT3_ABM | CPUID_EXT3_SVM | +            CPUID_EXT3_LAHF_LM, +        /* no xsaveopt! */ +        .xlevel = 0x8000001A, +        .model_id = "AMD Opteron 63xx class CPU", +    }, +}; + +/** + * x86_cpu_compat_set_features: + * @cpu_model: CPU model name to be changed. If NULL, all CPU models are changed + * @w: Identifies the feature word to be changed. + * @feat_add: Feature bits to be added to feature word + * @feat_remove: Feature bits to be removed from feature word + * + * Change CPU model feature bits for compatibility. + * + * This function may be used by machine-type compatibility functions + * to enable or disable feature bits on specific CPU models. + */ +void x86_cpu_compat_set_features(const char *cpu_model, FeatureWord w, +                                 uint32_t feat_add, uint32_t feat_remove) +{ +    X86CPUDefinition *def; +    int i; +    for (i = 0; i < ARRAY_SIZE(builtin_x86_defs); i++) { +        def = &builtin_x86_defs[i]; +        if (!cpu_model || !strcmp(cpu_model, def->name)) { +            def->features[w] |= feat_add; +            def->features[w] &= ~feat_remove; +        } +    } +} + +static uint32_t x86_cpu_get_supported_feature_word(FeatureWord w, +                                                   bool migratable_only); + +#ifdef CONFIG_KVM + +static int cpu_x86_fill_model_id(char *str) +{ +    uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0; +    int i; + +    for (i = 0; i < 3; i++) { +        host_cpuid(0x80000002 + i, 0, &eax, &ebx, &ecx, &edx); +        memcpy(str + i * 16 +  0, &eax, 4); +        memcpy(str + i * 16 +  4, &ebx, 4); +        memcpy(str + i * 16 +  8, &ecx, 4); +        memcpy(str + i * 16 + 12, &edx, 4); +    } +    return 0; +} + +static X86CPUDefinition host_cpudef; + +static Property host_x86_cpu_properties[] = { +    DEFINE_PROP_BOOL("migratable", X86CPU, migratable, true), +    DEFINE_PROP_END_OF_LIST() +}; + +/* class_init for the "host" CPU model + * + * This function may be called before KVM is initialized. + */ +static void host_x86_cpu_class_init(ObjectClass *oc, void *data) +{ +    DeviceClass *dc = DEVICE_CLASS(oc); +    X86CPUClass *xcc = X86_CPU_CLASS(oc); +    uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0; + +    xcc->kvm_required = true; + +    host_cpuid(0x0, 0, &eax, &ebx, &ecx, &edx); +    x86_cpu_vendor_words2str(host_cpudef.vendor, ebx, edx, ecx); + +    host_cpuid(0x1, 0, &eax, &ebx, &ecx, &edx); +    host_cpudef.family = ((eax >> 8) & 0x0F) + ((eax >> 20) & 0xFF); +    host_cpudef.model = ((eax >> 4) & 0x0F) | ((eax & 0xF0000) >> 12); +    host_cpudef.stepping = eax & 0x0F; + +    cpu_x86_fill_model_id(host_cpudef.model_id); + +    xcc->cpu_def = &host_cpudef; +    host_cpudef.cache_info_passthrough = true; + +    /* level, xlevel, xlevel2, and the feature words are initialized on +     * instance_init, because they require KVM to be initialized. +     */ + +    dc->props = host_x86_cpu_properties; +    /* Reason: host_x86_cpu_initfn() dies when !kvm_enabled() */ +    dc->cannot_destroy_with_object_finalize_yet = true; +} + +static void host_x86_cpu_initfn(Object *obj) +{ +    X86CPU *cpu = X86_CPU(obj); +    CPUX86State *env = &cpu->env; +    KVMState *s = kvm_state; + +    assert(kvm_enabled()); + +    /* We can't fill the features array here because we don't know yet if +     * "migratable" is true or false. +     */ +    cpu->host_features = true; + +    env->cpuid_level = kvm_arch_get_supported_cpuid(s, 0x0, 0, R_EAX); +    env->cpuid_xlevel = kvm_arch_get_supported_cpuid(s, 0x80000000, 0, R_EAX); +    env->cpuid_xlevel2 = kvm_arch_get_supported_cpuid(s, 0xC0000000, 0, R_EAX); + +    object_property_set_bool(OBJECT(cpu), true, "pmu", &error_abort); +} + +static const TypeInfo host_x86_cpu_type_info = { +    .name = X86_CPU_TYPE_NAME("host"), +    .parent = TYPE_X86_CPU, +    .instance_init = host_x86_cpu_initfn, +    .class_init = host_x86_cpu_class_init, +}; + +#endif + +static void report_unavailable_features(FeatureWord w, uint32_t mask) +{ +    FeatureWordInfo *f = &feature_word_info[w]; +    int i; + +    for (i = 0; i < 32; ++i) { +        if (1 << i & mask) { +            const char *reg = get_register_name_32(f->cpuid_reg); +            assert(reg); +            fprintf(stderr, "warning: %s doesn't support requested feature: " +                "CPUID.%02XH:%s%s%s [bit %d]\n", +                kvm_enabled() ? "host" : "TCG", +                f->cpuid_eax, reg, +                f->feat_names[i] ? "." : "", +                f->feat_names[i] ? f->feat_names[i] : "", i); +        } +    } +} + +static void x86_cpuid_version_get_family(Object *obj, Visitor *v, void *opaque, +                                         const char *name, Error **errp) +{ +    X86CPU *cpu = X86_CPU(obj); +    CPUX86State *env = &cpu->env; +    int64_t value; + +    value = (env->cpuid_version >> 8) & 0xf; +    if (value == 0xf) { +        value += (env->cpuid_version >> 20) & 0xff; +    } +    visit_type_int(v, &value, name, errp); +} + +static void x86_cpuid_version_set_family(Object *obj, Visitor *v, void *opaque, +                                         const char *name, Error **errp) +{ +    X86CPU *cpu = X86_CPU(obj); +    CPUX86State *env = &cpu->env; +    const int64_t min = 0; +    const int64_t max = 0xff + 0xf; +    Error *local_err = NULL; +    int64_t value; + +    visit_type_int(v, &value, name, &local_err); +    if (local_err) { +        error_propagate(errp, local_err); +        return; +    } +    if (value < min || value > max) { +        error_setg(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE, "", +                   name ? name : "null", value, min, max); +        return; +    } + +    env->cpuid_version &= ~0xff00f00; +    if (value > 0x0f) { +        env->cpuid_version |= 0xf00 | ((value - 0x0f) << 20); +    } else { +        env->cpuid_version |= value << 8; +    } +} + +static void x86_cpuid_version_get_model(Object *obj, Visitor *v, void *opaque, +                                        const char *name, Error **errp) +{ +    X86CPU *cpu = X86_CPU(obj); +    CPUX86State *env = &cpu->env; +    int64_t value; + +    value = (env->cpuid_version >> 4) & 0xf; +    value |= ((env->cpuid_version >> 16) & 0xf) << 4; +    visit_type_int(v, &value, name, errp); +} + +static void x86_cpuid_version_set_model(Object *obj, Visitor *v, void *opaque, +                                        const char *name, Error **errp) +{ +    X86CPU *cpu = X86_CPU(obj); +    CPUX86State *env = &cpu->env; +    const int64_t min = 0; +    const int64_t max = 0xff; +    Error *local_err = NULL; +    int64_t value; + +    visit_type_int(v, &value, name, &local_err); +    if (local_err) { +        error_propagate(errp, local_err); +        return; +    } +    if (value < min || value > max) { +        error_setg(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE, "", +                   name ? name : "null", value, min, max); +        return; +    } + +    env->cpuid_version &= ~0xf00f0; +    env->cpuid_version |= ((value & 0xf) << 4) | ((value >> 4) << 16); +} + +static void x86_cpuid_version_get_stepping(Object *obj, Visitor *v, +                                           void *opaque, const char *name, +                                           Error **errp) +{ +    X86CPU *cpu = X86_CPU(obj); +    CPUX86State *env = &cpu->env; +    int64_t value; + +    value = env->cpuid_version & 0xf; +    visit_type_int(v, &value, name, errp); +} + +static void x86_cpuid_version_set_stepping(Object *obj, Visitor *v, +                                           void *opaque, const char *name, +                                           Error **errp) +{ +    X86CPU *cpu = X86_CPU(obj); +    CPUX86State *env = &cpu->env; +    const int64_t min = 0; +    const int64_t max = 0xf; +    Error *local_err = NULL; +    int64_t value; + +    visit_type_int(v, &value, name, &local_err); +    if (local_err) { +        error_propagate(errp, local_err); +        return; +    } +    if (value < min || value > max) { +        error_setg(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE, "", +                   name ? name : "null", value, min, max); +        return; +    } + +    env->cpuid_version &= ~0xf; +    env->cpuid_version |= value & 0xf; +} + +static char *x86_cpuid_get_vendor(Object *obj, Error **errp) +{ +    X86CPU *cpu = X86_CPU(obj); +    CPUX86State *env = &cpu->env; +    char *value; + +    value = g_malloc(CPUID_VENDOR_SZ + 1); +    x86_cpu_vendor_words2str(value, env->cpuid_vendor1, env->cpuid_vendor2, +                             env->cpuid_vendor3); +    return value; +} + +static void x86_cpuid_set_vendor(Object *obj, const char *value, +                                 Error **errp) +{ +    X86CPU *cpu = X86_CPU(obj); +    CPUX86State *env = &cpu->env; +    int i; + +    if (strlen(value) != CPUID_VENDOR_SZ) { +        error_setg(errp, QERR_PROPERTY_VALUE_BAD, "", "vendor", value); +        return; +    } + +    env->cpuid_vendor1 = 0; +    env->cpuid_vendor2 = 0; +    env->cpuid_vendor3 = 0; +    for (i = 0; i < 4; i++) { +        env->cpuid_vendor1 |= ((uint8_t)value[i    ]) << (8 * i); +        env->cpuid_vendor2 |= ((uint8_t)value[i + 4]) << (8 * i); +        env->cpuid_vendor3 |= ((uint8_t)value[i + 8]) << (8 * i); +    } +} + +static char *x86_cpuid_get_model_id(Object *obj, Error **errp) +{ +    X86CPU *cpu = X86_CPU(obj); +    CPUX86State *env = &cpu->env; +    char *value; +    int i; + +    value = g_malloc(48 + 1); +    for (i = 0; i < 48; i++) { +        value[i] = env->cpuid_model[i >> 2] >> (8 * (i & 3)); +    } +    value[48] = '\0'; +    return value; +} + +static void x86_cpuid_set_model_id(Object *obj, const char *model_id, +                                   Error **errp) +{ +    X86CPU *cpu = X86_CPU(obj); +    CPUX86State *env = &cpu->env; +    int c, len, i; + +    if (model_id == NULL) { +        model_id = ""; +    } +    len = strlen(model_id); +    memset(env->cpuid_model, 0, 48); +    for (i = 0; i < 48; i++) { +        if (i >= len) { +            c = '\0'; +        } else { +            c = (uint8_t)model_id[i]; +        } +        env->cpuid_model[i >> 2] |= c << (8 * (i & 3)); +    } +} + +static void x86_cpuid_get_tsc_freq(Object *obj, Visitor *v, void *opaque, +                                   const char *name, Error **errp) +{ +    X86CPU *cpu = X86_CPU(obj); +    int64_t value; + +    value = cpu->env.tsc_khz * 1000; +    visit_type_int(v, &value, name, errp); +} + +static void x86_cpuid_set_tsc_freq(Object *obj, Visitor *v, void *opaque, +                                   const char *name, Error **errp) +{ +    X86CPU *cpu = X86_CPU(obj); +    const int64_t min = 0; +    const int64_t max = INT64_MAX; +    Error *local_err = NULL; +    int64_t value; + +    visit_type_int(v, &value, name, &local_err); +    if (local_err) { +        error_propagate(errp, local_err); +        return; +    } +    if (value < min || value > max) { +        error_setg(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE, "", +                   name ? name : "null", value, min, max); +        return; +    } + +    cpu->env.tsc_khz = value / 1000; +} + +static void x86_cpuid_get_apic_id(Object *obj, Visitor *v, void *opaque, +                                  const char *name, Error **errp) +{ +    X86CPU *cpu = X86_CPU(obj); +    int64_t value = cpu->apic_id; + +    visit_type_int(v, &value, name, errp); +} + +static void x86_cpuid_set_apic_id(Object *obj, Visitor *v, void *opaque, +                                  const char *name, Error **errp) +{ +    X86CPU *cpu = X86_CPU(obj); +    DeviceState *dev = DEVICE(obj); +    const int64_t min = 0; +    const int64_t max = UINT32_MAX; +    Error *error = NULL; +    int64_t value; + +    if (dev->realized) { +        error_setg(errp, "Attempt to set property '%s' on '%s' after " +                   "it was realized", name, object_get_typename(obj)); +        return; +    } + +    visit_type_int(v, &value, name, &error); +    if (error) { +        error_propagate(errp, error); +        return; +    } +    if (value < min || value > max) { +        error_setg(errp, "Property %s.%s doesn't take value %" PRId64 +                   " (minimum: %" PRId64 ", maximum: %" PRId64 ")" , +                   object_get_typename(obj), name, value, min, max); +        return; +    } + +    if ((value != cpu->apic_id) && cpu_exists(value)) { +        error_setg(errp, "CPU with APIC ID %" PRIi64 " exists", value); +        return; +    } +    cpu->apic_id = value; +} + +/* Generic getter for "feature-words" and "filtered-features" properties */ +static void x86_cpu_get_feature_words(Object *obj, Visitor *v, void *opaque, +                                      const char *name, Error **errp) +{ +    uint32_t *array = (uint32_t *)opaque; +    FeatureWord w; +    Error *err = NULL; +    X86CPUFeatureWordInfo word_infos[FEATURE_WORDS] = { }; +    X86CPUFeatureWordInfoList list_entries[FEATURE_WORDS] = { }; +    X86CPUFeatureWordInfoList *list = NULL; + +    for (w = 0; w < FEATURE_WORDS; w++) { +        FeatureWordInfo *wi = &feature_word_info[w]; +        X86CPUFeatureWordInfo *qwi = &word_infos[w]; +        qwi->cpuid_input_eax = wi->cpuid_eax; +        qwi->has_cpuid_input_ecx = wi->cpuid_needs_ecx; +        qwi->cpuid_input_ecx = wi->cpuid_ecx; +        qwi->cpuid_register = x86_reg_info_32[wi->cpuid_reg].qapi_enum; +        qwi->features = array[w]; + +        /* List will be in reverse order, but order shouldn't matter */ +        list_entries[w].next = list; +        list_entries[w].value = &word_infos[w]; +        list = &list_entries[w]; +    } + +    visit_type_X86CPUFeatureWordInfoList(v, &list, "feature-words", &err); +    error_propagate(errp, err); +} + +static void x86_get_hv_spinlocks(Object *obj, Visitor *v, void *opaque, +                                 const char *name, Error **errp) +{ +    X86CPU *cpu = X86_CPU(obj); +    int64_t value = cpu->hyperv_spinlock_attempts; + +    visit_type_int(v, &value, name, errp); +} + +static void x86_set_hv_spinlocks(Object *obj, Visitor *v, void *opaque, +                                 const char *name, Error **errp) +{ +    const int64_t min = 0xFFF; +    const int64_t max = UINT_MAX; +    X86CPU *cpu = X86_CPU(obj); +    Error *err = NULL; +    int64_t value; + +    visit_type_int(v, &value, name, &err); +    if (err) { +        error_propagate(errp, err); +        return; +    } + +    if (value < min || value > max) { +        error_setg(errp, "Property %s.%s doesn't take value %" PRId64 +                   " (minimum: %" PRId64 ", maximum: %" PRId64 ")", +                   object_get_typename(obj), name ? name : "null", +                   value, min, max); +        return; +    } +    cpu->hyperv_spinlock_attempts = value; +} + +static PropertyInfo qdev_prop_spinlocks = { +    .name  = "int", +    .get   = x86_get_hv_spinlocks, +    .set   = x86_set_hv_spinlocks, +}; + +/* Convert all '_' in a feature string option name to '-', to make feature + * name conform to QOM property naming rule, which uses '-' instead of '_'. + */ +static inline void feat2prop(char *s) +{ +    while ((s = strchr(s, '_'))) { +        *s = '-'; +    } +} + +/* Parse "+feature,-feature,feature=foo" CPU feature string + */ +static void x86_cpu_parse_featurestr(CPUState *cs, char *features, +                                     Error **errp) +{ +    X86CPU *cpu = X86_CPU(cs); +    char *featurestr; /* Single 'key=value" string being parsed */ +    FeatureWord w; +    /* Features to be added */ +    FeatureWordArray plus_features = { 0 }; +    /* Features to be removed */ +    FeatureWordArray minus_features = { 0 }; +    uint32_t numvalue; +    CPUX86State *env = &cpu->env; +    Error *local_err = NULL; + +    featurestr = features ? strtok(features, ",") : NULL; + +    while (featurestr) { +        char *val; +        if (featurestr[0] == '+') { +            add_flagname_to_bitmaps(featurestr + 1, plus_features, &local_err); +        } else if (featurestr[0] == '-') { +            add_flagname_to_bitmaps(featurestr + 1, minus_features, &local_err); +        } else if ((val = strchr(featurestr, '='))) { +            *val = 0; val++; +            feat2prop(featurestr); +            if (!strcmp(featurestr, "xlevel")) { +                char *err; +                char num[32]; + +                numvalue = strtoul(val, &err, 0); +                if (!*val || *err) { +                    error_setg(errp, "bad numerical value %s", val); +                    return; +                } +                if (numvalue < 0x80000000) { +                    error_report("xlevel value shall always be >= 0x80000000" +                                 ", fixup will be removed in future versions"); +                    numvalue += 0x80000000; +                } +                snprintf(num, sizeof(num), "%" PRIu32, numvalue); +                object_property_parse(OBJECT(cpu), num, featurestr, &local_err); +            } else if (!strcmp(featurestr, "tsc-freq")) { +                int64_t tsc_freq; +                char *err; +                char num[32]; + +                tsc_freq = strtosz_suffix_unit(val, &err, +                                               STRTOSZ_DEFSUFFIX_B, 1000); +                if (tsc_freq < 0 || *err) { +                    error_setg(errp, "bad numerical value %s", val); +                    return; +                } +                snprintf(num, sizeof(num), "%" PRId64, tsc_freq); +                object_property_parse(OBJECT(cpu), num, "tsc-frequency", +                                      &local_err); +            } else if (!strcmp(featurestr, "hv-spinlocks")) { +                char *err; +                const int min = 0xFFF; +                char num[32]; +                numvalue = strtoul(val, &err, 0); +                if (!*val || *err) { +                    error_setg(errp, "bad numerical value %s", val); +                    return; +                } +                if (numvalue < min) { +                    error_report("hv-spinlocks value shall always be >= 0x%x" +                                 ", fixup will be removed in future versions", +                                 min); +                    numvalue = min; +                } +                snprintf(num, sizeof(num), "%" PRId32, numvalue); +                object_property_parse(OBJECT(cpu), num, featurestr, &local_err); +            } else { +                object_property_parse(OBJECT(cpu), val, featurestr, &local_err); +            } +        } else { +            feat2prop(featurestr); +            object_property_parse(OBJECT(cpu), "on", featurestr, &local_err); +        } +        if (local_err) { +            error_propagate(errp, local_err); +            return; +        } +        featurestr = strtok(NULL, ","); +    } + +    if (cpu->host_features) { +        for (w = 0; w < FEATURE_WORDS; w++) { +            env->features[w] = +                x86_cpu_get_supported_feature_word(w, cpu->migratable); +        } +    } + +    for (w = 0; w < FEATURE_WORDS; w++) { +        env->features[w] |= plus_features[w]; +        env->features[w] &= ~minus_features[w]; +    } +} + +/* Print all cpuid feature names in featureset + */ +static void listflags(FILE *f, fprintf_function print, const char **featureset) +{ +    int bit; +    bool first = true; + +    for (bit = 0; bit < 32; bit++) { +        if (featureset[bit]) { +            print(f, "%s%s", first ? "" : " ", featureset[bit]); +            first = false; +        } +    } +} + +/* generate CPU information. */ +void x86_cpu_list(FILE *f, fprintf_function cpu_fprintf) +{ +    X86CPUDefinition *def; +    char buf[256]; +    int i; + +    for (i = 0; i < ARRAY_SIZE(builtin_x86_defs); i++) { +        def = &builtin_x86_defs[i]; +        snprintf(buf, sizeof(buf), "%s", def->name); +        (*cpu_fprintf)(f, "x86 %16s  %-48s\n", buf, def->model_id); +    } +#ifdef CONFIG_KVM +    (*cpu_fprintf)(f, "x86 %16s  %-48s\n", "host", +                   "KVM processor with all supported host features " +                   "(only available in KVM mode)"); +#endif + +    (*cpu_fprintf)(f, "\nRecognized CPUID flags:\n"); +    for (i = 0; i < ARRAY_SIZE(feature_word_info); i++) { +        FeatureWordInfo *fw = &feature_word_info[i]; + +        (*cpu_fprintf)(f, "  "); +        listflags(f, cpu_fprintf, fw->feat_names); +        (*cpu_fprintf)(f, "\n"); +    } +} + +CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp) +{ +    CpuDefinitionInfoList *cpu_list = NULL; +    X86CPUDefinition *def; +    int i; + +    for (i = 0; i < ARRAY_SIZE(builtin_x86_defs); i++) { +        CpuDefinitionInfoList *entry; +        CpuDefinitionInfo *info; + +        def = &builtin_x86_defs[i]; +        info = g_malloc0(sizeof(*info)); +        info->name = g_strdup(def->name); + +        entry = g_malloc0(sizeof(*entry)); +        entry->value = info; +        entry->next = cpu_list; +        cpu_list = entry; +    } + +    return cpu_list; +} + +static uint32_t x86_cpu_get_supported_feature_word(FeatureWord w, +                                                   bool migratable_only) +{ +    FeatureWordInfo *wi = &feature_word_info[w]; +    uint32_t r; + +    if (kvm_enabled()) { +        r = kvm_arch_get_supported_cpuid(kvm_state, wi->cpuid_eax, +                                                    wi->cpuid_ecx, +                                                    wi->cpuid_reg); +    } else if (tcg_enabled()) { +        r = wi->tcg_features; +    } else { +        return ~0; +    } +    if (migratable_only) { +        r &= x86_cpu_get_migratable_flags(w); +    } +    return r; +} + +/* + * Filters CPU feature words based on host availability of each feature. + * + * Returns: 0 if all flags are supported by the host, non-zero otherwise. + */ +static int x86_cpu_filter_features(X86CPU *cpu) +{ +    CPUX86State *env = &cpu->env; +    FeatureWord w; +    int rv = 0; + +    for (w = 0; w < FEATURE_WORDS; w++) { +        uint32_t host_feat = +            x86_cpu_get_supported_feature_word(w, cpu->migratable); +        uint32_t requested_features = env->features[w]; +        env->features[w] &= host_feat; +        cpu->filtered_features[w] = requested_features & ~env->features[w]; +        if (cpu->filtered_features[w]) { +            if (cpu->check_cpuid || cpu->enforce_cpuid) { +                report_unavailable_features(w, cpu->filtered_features[w]); +            } +            rv = 1; +        } +    } + +    return rv; +} + +/* Load data from X86CPUDefinition + */ +static void x86_cpu_load_def(X86CPU *cpu, X86CPUDefinition *def, Error **errp) +{ +    CPUX86State *env = &cpu->env; +    const char *vendor; +    char host_vendor[CPUID_VENDOR_SZ + 1]; +    FeatureWord w; + +    object_property_set_int(OBJECT(cpu), def->level, "level", errp); +    object_property_set_int(OBJECT(cpu), def->family, "family", errp); +    object_property_set_int(OBJECT(cpu), def->model, "model", errp); +    object_property_set_int(OBJECT(cpu), def->stepping, "stepping", errp); +    object_property_set_int(OBJECT(cpu), def->xlevel, "xlevel", errp); +    object_property_set_int(OBJECT(cpu), def->xlevel2, "xlevel2", errp); +    cpu->cache_info_passthrough = def->cache_info_passthrough; +    object_property_set_str(OBJECT(cpu), def->model_id, "model-id", errp); +    for (w = 0; w < FEATURE_WORDS; w++) { +        env->features[w] = def->features[w]; +    } + +    /* Special cases not set in the X86CPUDefinition structs: */ +    if (kvm_enabled()) { +        FeatureWord w; +        for (w = 0; w < FEATURE_WORDS; w++) { +            env->features[w] |= kvm_default_features[w]; +            env->features[w] &= ~kvm_default_unset_features[w]; +        } +    } + +    env->features[FEAT_1_ECX] |= CPUID_EXT_HYPERVISOR; + +    /* sysenter isn't supported in compatibility mode on AMD, +     * syscall isn't supported in compatibility mode on Intel. +     * Normally we advertise the actual CPU vendor, but you can +     * override this using the 'vendor' property if you want to use +     * KVM's sysenter/syscall emulation in compatibility mode and +     * when doing cross vendor migration +     */ +    vendor = def->vendor; +    if (kvm_enabled()) { +        uint32_t  ebx = 0, ecx = 0, edx = 0; +        host_cpuid(0, 0, NULL, &ebx, &ecx, &edx); +        x86_cpu_vendor_words2str(host_vendor, ebx, edx, ecx); +        vendor = host_vendor; +    } + +    object_property_set_str(OBJECT(cpu), vendor, "vendor", errp); + +} + +X86CPU *cpu_x86_create(const char *cpu_model, Error **errp) +{ +    X86CPU *cpu = NULL; +    X86CPUClass *xcc; +    ObjectClass *oc; +    gchar **model_pieces; +    char *name, *features; +    Error *error = NULL; + +    model_pieces = g_strsplit(cpu_model, ",", 2); +    if (!model_pieces[0]) { +        error_setg(&error, "Invalid/empty CPU model name"); +        goto out; +    } +    name = model_pieces[0]; +    features = model_pieces[1]; + +    oc = x86_cpu_class_by_name(name); +    if (oc == NULL) { +        error_setg(&error, "Unable to find CPU definition: %s", name); +        goto out; +    } +    xcc = X86_CPU_CLASS(oc); + +    if (xcc->kvm_required && !kvm_enabled()) { +        error_setg(&error, "CPU model '%s' requires KVM", name); +        goto out; +    } + +    cpu = X86_CPU(object_new(object_class_get_name(oc))); + +    x86_cpu_parse_featurestr(CPU(cpu), features, &error); +    if (error) { +        goto out; +    } + +out: +    if (error != NULL) { +        error_propagate(errp, error); +        if (cpu) { +            object_unref(OBJECT(cpu)); +            cpu = NULL; +        } +    } +    g_strfreev(model_pieces); +    return cpu; +} + +X86CPU *cpu_x86_init(const char *cpu_model) +{ +    Error *error = NULL; +    X86CPU *cpu; + +    cpu = cpu_x86_create(cpu_model, &error); +    if (error) { +        goto out; +    } + +    object_property_set_bool(OBJECT(cpu), true, "realized", &error); + +out: +    if (error) { +        error_report_err(error); +        if (cpu != NULL) { +            object_unref(OBJECT(cpu)); +            cpu = NULL; +        } +    } +    return cpu; +} + +static void x86_cpu_cpudef_class_init(ObjectClass *oc, void *data) +{ +    X86CPUDefinition *cpudef = data; +    X86CPUClass *xcc = X86_CPU_CLASS(oc); + +    xcc->cpu_def = cpudef; +} + +static void x86_register_cpudef_type(X86CPUDefinition *def) +{ +    char *typename = x86_cpu_type_name(def->name); +    TypeInfo ti = { +        .name = typename, +        .parent = TYPE_X86_CPU, +        .class_init = x86_cpu_cpudef_class_init, +        .class_data = def, +    }; + +    type_register(&ti); +    g_free(typename); +} + +#if !defined(CONFIG_USER_ONLY) + +void cpu_clear_apic_feature(CPUX86State *env) +{ +    env->features[FEAT_1_EDX] &= ~CPUID_APIC; +} + +#endif /* !CONFIG_USER_ONLY */ + +/* Initialize list of CPU models, filling some non-static fields if necessary + */ +void x86_cpudef_setup(void) +{ +    int i, j; +    static const char *model_with_versions[] = { "qemu32", "qemu64", "athlon" }; + +    for (i = 0; i < ARRAY_SIZE(builtin_x86_defs); ++i) { +        X86CPUDefinition *def = &builtin_x86_defs[i]; + +        /* Look for specific "cpudef" models that */ +        /* have the QEMU version in .model_id */ +        for (j = 0; j < ARRAY_SIZE(model_with_versions); j++) { +            if (strcmp(model_with_versions[j], def->name) == 0) { +                pstrcpy(def->model_id, sizeof(def->model_id), +                        "QEMU Virtual CPU version "); +                pstrcat(def->model_id, sizeof(def->model_id), +                        qemu_get_version()); +                break; +            } +        } +    } +} + +void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, +                   uint32_t *eax, uint32_t *ebx, +                   uint32_t *ecx, uint32_t *edx) +{ +    X86CPU *cpu = x86_env_get_cpu(env); +    CPUState *cs = CPU(cpu); + +    /* test if maximum index reached */ +    if (index & 0x80000000) { +        if (index > env->cpuid_xlevel) { +            if (env->cpuid_xlevel2 > 0) { +                /* Handle the Centaur's CPUID instruction. */ +                if (index > env->cpuid_xlevel2) { +                    index = env->cpuid_xlevel2; +                } else if (index < 0xC0000000) { +                    index = env->cpuid_xlevel; +                } +            } else { +                /* Intel documentation states that invalid EAX input will +                 * return the same information as EAX=cpuid_level +                 * (Intel SDM Vol. 2A - Instruction Set Reference - CPUID) +                 */ +                index =  env->cpuid_level; +            } +        } +    } else { +        if (index > env->cpuid_level) +            index = env->cpuid_level; +    } + +    switch(index) { +    case 0: +        *eax = env->cpuid_level; +        *ebx = env->cpuid_vendor1; +        *edx = env->cpuid_vendor2; +        *ecx = env->cpuid_vendor3; +        break; +    case 1: +        *eax = env->cpuid_version; +        *ebx = (cpu->apic_id << 24) | +               8 << 8; /* CLFLUSH size in quad words, Linux wants it. */ +        *ecx = env->features[FEAT_1_ECX]; +        *edx = env->features[FEAT_1_EDX]; +        if (cs->nr_cores * cs->nr_threads > 1) { +            *ebx |= (cs->nr_cores * cs->nr_threads) << 16; +            *edx |= 1 << 28;    /* HTT bit */ +        } +        break; +    case 2: +        /* cache info: needed for Pentium Pro compatibility */ +        if (cpu->cache_info_passthrough) { +            host_cpuid(index, 0, eax, ebx, ecx, edx); +            break; +        } +        *eax = 1; /* Number of CPUID[EAX=2] calls required */ +        *ebx = 0; +        *ecx = 0; +        *edx = (L1D_DESCRIPTOR << 16) | \ +               (L1I_DESCRIPTOR <<  8) | \ +               (L2_DESCRIPTOR); +        break; +    case 4: +        /* cache info: needed for Core compatibility */ +        if (cpu->cache_info_passthrough) { +            host_cpuid(index, count, eax, ebx, ecx, edx); +            *eax &= ~0xFC000000; +        } else { +            *eax = 0; +            switch (count) { +            case 0: /* L1 dcache info */ +                *eax |= CPUID_4_TYPE_DCACHE | \ +                        CPUID_4_LEVEL(1) | \ +                        CPUID_4_SELF_INIT_LEVEL; +                *ebx = (L1D_LINE_SIZE - 1) | \ +                       ((L1D_PARTITIONS - 1) << 12) | \ +                       ((L1D_ASSOCIATIVITY - 1) << 22); +                *ecx = L1D_SETS - 1; +                *edx = CPUID_4_NO_INVD_SHARING; +                break; +            case 1: /* L1 icache info */ +                *eax |= CPUID_4_TYPE_ICACHE | \ +                        CPUID_4_LEVEL(1) | \ +                        CPUID_4_SELF_INIT_LEVEL; +                *ebx = (L1I_LINE_SIZE - 1) | \ +                       ((L1I_PARTITIONS - 1) << 12) | \ +                       ((L1I_ASSOCIATIVITY - 1) << 22); +                *ecx = L1I_SETS - 1; +                *edx = CPUID_4_NO_INVD_SHARING; +                break; +            case 2: /* L2 cache info */ +                *eax |= CPUID_4_TYPE_UNIFIED | \ +                        CPUID_4_LEVEL(2) | \ +                        CPUID_4_SELF_INIT_LEVEL; +                if (cs->nr_threads > 1) { +                    *eax |= (cs->nr_threads - 1) << 14; +                } +                *ebx = (L2_LINE_SIZE - 1) | \ +                       ((L2_PARTITIONS - 1) << 12) | \ +                       ((L2_ASSOCIATIVITY - 1) << 22); +                *ecx = L2_SETS - 1; +                *edx = CPUID_4_NO_INVD_SHARING; +                break; +            default: /* end of info */ +                *eax = 0; +                *ebx = 0; +                *ecx = 0; +                *edx = 0; +                break; +            } +        } + +        /* QEMU gives out its own APIC IDs, never pass down bits 31..26.  */ +        if ((*eax & 31) && cs->nr_cores > 1) { +            *eax |= (cs->nr_cores - 1) << 26; +        } +        break; +    case 5: +        /* mwait info: needed for Core compatibility */ +        *eax = 0; /* Smallest monitor-line size in bytes */ +        *ebx = 0; /* Largest monitor-line size in bytes */ +        *ecx = CPUID_MWAIT_EMX | CPUID_MWAIT_IBE; +        *edx = 0; +        break; +    case 6: +        /* Thermal and Power Leaf */ +        *eax = env->features[FEAT_6_EAX]; +        *ebx = 0; +        *ecx = 0; +        *edx = 0; +        break; +    case 7: +        /* Structured Extended Feature Flags Enumeration Leaf */ +        if (count == 0) { +            *eax = 0; /* Maximum ECX value for sub-leaves */ +            *ebx = env->features[FEAT_7_0_EBX]; /* Feature flags */ +            *ecx = 0; /* Reserved */ +            *edx = 0; /* Reserved */ +        } else { +            *eax = 0; +            *ebx = 0; +            *ecx = 0; +            *edx = 0; +        } +        break; +    case 9: +        /* Direct Cache Access Information Leaf */ +        *eax = 0; /* Bits 0-31 in DCA_CAP MSR */ +        *ebx = 0; +        *ecx = 0; +        *edx = 0; +        break; +    case 0xA: +        /* Architectural Performance Monitoring Leaf */ +        if (kvm_enabled() && cpu->enable_pmu) { +            KVMState *s = cs->kvm_state; + +            *eax = kvm_arch_get_supported_cpuid(s, 0xA, count, R_EAX); +            *ebx = kvm_arch_get_supported_cpuid(s, 0xA, count, R_EBX); +            *ecx = kvm_arch_get_supported_cpuid(s, 0xA, count, R_ECX); +            *edx = kvm_arch_get_supported_cpuid(s, 0xA, count, R_EDX); +        } else { +            *eax = 0; +            *ebx = 0; +            *ecx = 0; +            *edx = 0; +        } +        break; +    case 0xD: { +        KVMState *s = cs->kvm_state; +        uint64_t kvm_mask; +        int i; + +        /* Processor Extended State */ +        *eax = 0; +        *ebx = 0; +        *ecx = 0; +        *edx = 0; +        if (!(env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE) || !kvm_enabled()) { +            break; +        } +        kvm_mask = +            kvm_arch_get_supported_cpuid(s, 0xd, 0, R_EAX) | +            ((uint64_t)kvm_arch_get_supported_cpuid(s, 0xd, 0, R_EDX) << 32); + +        if (count == 0) { +            *ecx = 0x240; +            for (i = 2; i < ARRAY_SIZE(ext_save_areas); i++) { +                const ExtSaveArea *esa = &ext_save_areas[i]; +                if ((env->features[esa->feature] & esa->bits) == esa->bits && +                    (kvm_mask & (1 << i)) != 0) { +                    if (i < 32) { +                        *eax |= 1 << i; +                    } else { +                        *edx |= 1 << (i - 32); +                    } +                    *ecx = MAX(*ecx, esa->offset + esa->size); +                } +            } +            *eax |= kvm_mask & (XSTATE_FP | XSTATE_SSE); +            *ebx = *ecx; +        } else if (count == 1) { +            *eax = env->features[FEAT_XSAVE]; +        } else if (count < ARRAY_SIZE(ext_save_areas)) { +            const ExtSaveArea *esa = &ext_save_areas[count]; +            if ((env->features[esa->feature] & esa->bits) == esa->bits && +                (kvm_mask & (1 << count)) != 0) { +                *eax = esa->size; +                *ebx = esa->offset; +            } +        } +        break; +    } +    case 0x80000000: +        *eax = env->cpuid_xlevel; +        *ebx = env->cpuid_vendor1; +        *edx = env->cpuid_vendor2; +        *ecx = env->cpuid_vendor3; +        break; +    case 0x80000001: +        *eax = env->cpuid_version; +        *ebx = 0; +        *ecx = env->features[FEAT_8000_0001_ECX]; +        *edx = env->features[FEAT_8000_0001_EDX]; + +        /* The Linux kernel checks for the CMPLegacy bit and +         * discards multiple thread information if it is set. +         * So dont set it here for Intel to make Linux guests happy. +         */ +        if (cs->nr_cores * cs->nr_threads > 1) { +            if (env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1 || +                env->cpuid_vendor2 != CPUID_VENDOR_INTEL_2 || +                env->cpuid_vendor3 != CPUID_VENDOR_INTEL_3) { +                *ecx |= 1 << 1;    /* CmpLegacy bit */ +            } +        } +        break; +    case 0x80000002: +    case 0x80000003: +    case 0x80000004: +        *eax = env->cpuid_model[(index - 0x80000002) * 4 + 0]; +        *ebx = env->cpuid_model[(index - 0x80000002) * 4 + 1]; +        *ecx = env->cpuid_model[(index - 0x80000002) * 4 + 2]; +        *edx = env->cpuid_model[(index - 0x80000002) * 4 + 3]; +        break; +    case 0x80000005: +        /* cache info (L1 cache) */ +        if (cpu->cache_info_passthrough) { +            host_cpuid(index, 0, eax, ebx, ecx, edx); +            break; +        } +        *eax = (L1_DTLB_2M_ASSOC << 24) | (L1_DTLB_2M_ENTRIES << 16) | \ +               (L1_ITLB_2M_ASSOC <<  8) | (L1_ITLB_2M_ENTRIES); +        *ebx = (L1_DTLB_4K_ASSOC << 24) | (L1_DTLB_4K_ENTRIES << 16) | \ +               (L1_ITLB_4K_ASSOC <<  8) | (L1_ITLB_4K_ENTRIES); +        *ecx = (L1D_SIZE_KB_AMD << 24) | (L1D_ASSOCIATIVITY_AMD << 16) | \ +               (L1D_LINES_PER_TAG << 8) | (L1D_LINE_SIZE); +        *edx = (L1I_SIZE_KB_AMD << 24) | (L1I_ASSOCIATIVITY_AMD << 16) | \ +               (L1I_LINES_PER_TAG << 8) | (L1I_LINE_SIZE); +        break; +    case 0x80000006: +        /* cache info (L2 cache) */ +        if (cpu->cache_info_passthrough) { +            host_cpuid(index, 0, eax, ebx, ecx, edx); +            break; +        } +        *eax = (AMD_ENC_ASSOC(L2_DTLB_2M_ASSOC) << 28) | \ +               (L2_DTLB_2M_ENTRIES << 16) | \ +               (AMD_ENC_ASSOC(L2_ITLB_2M_ASSOC) << 12) | \ +               (L2_ITLB_2M_ENTRIES); +        *ebx = (AMD_ENC_ASSOC(L2_DTLB_4K_ASSOC) << 28) | \ +               (L2_DTLB_4K_ENTRIES << 16) | \ +               (AMD_ENC_ASSOC(L2_ITLB_4K_ASSOC) << 12) | \ +               (L2_ITLB_4K_ENTRIES); +        *ecx = (L2_SIZE_KB_AMD << 16) | \ +               (AMD_ENC_ASSOC(L2_ASSOCIATIVITY) << 12) | \ +               (L2_LINES_PER_TAG << 8) | (L2_LINE_SIZE); +        *edx = ((L3_SIZE_KB/512) << 18) | \ +               (AMD_ENC_ASSOC(L3_ASSOCIATIVITY) << 12) | \ +               (L3_LINES_PER_TAG << 8) | (L3_LINE_SIZE); +        break; +    case 0x80000007: +        *eax = 0; +        *ebx = 0; +        *ecx = 0; +        *edx = env->features[FEAT_8000_0007_EDX]; +        break; +    case 0x80000008: +        /* virtual & phys address size in low 2 bytes. */ +/* XXX: This value must match the one used in the MMU code. */ +        if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) { +            /* 64 bit processor */ +/* XXX: The physical address space is limited to 42 bits in exec.c. */ +            *eax = 0x00003028; /* 48 bits virtual, 40 bits physical */ +        } else { +            if (env->features[FEAT_1_EDX] & CPUID_PSE36) { +                *eax = 0x00000024; /* 36 bits physical */ +            } else { +                *eax = 0x00000020; /* 32 bits physical */ +            } +        } +        *ebx = 0; +        *ecx = 0; +        *edx = 0; +        if (cs->nr_cores * cs->nr_threads > 1) { +            *ecx |= (cs->nr_cores * cs->nr_threads) - 1; +        } +        break; +    case 0x8000000A: +        if (env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_SVM) { +            *eax = 0x00000001; /* SVM Revision */ +            *ebx = 0x00000010; /* nr of ASIDs */ +            *ecx = 0; +            *edx = env->features[FEAT_SVM]; /* optional features */ +        } else { +            *eax = 0; +            *ebx = 0; +            *ecx = 0; +            *edx = 0; +        } +        break; +    case 0xC0000000: +        *eax = env->cpuid_xlevel2; +        *ebx = 0; +        *ecx = 0; +        *edx = 0; +        break; +    case 0xC0000001: +        /* Support for VIA CPU's CPUID instruction */ +        *eax = env->cpuid_version; +        *ebx = 0; +        *ecx = 0; +        *edx = env->features[FEAT_C000_0001_EDX]; +        break; +    case 0xC0000002: +    case 0xC0000003: +    case 0xC0000004: +        /* Reserved for the future, and now filled with zero */ +        *eax = 0; +        *ebx = 0; +        *ecx = 0; +        *edx = 0; +        break; +    default: +        /* reserved values: zero */ +        *eax = 0; +        *ebx = 0; +        *ecx = 0; +        *edx = 0; +        break; +    } +} + +/* CPUClass::reset() */ +static void x86_cpu_reset(CPUState *s) +{ +    X86CPU *cpu = X86_CPU(s); +    X86CPUClass *xcc = X86_CPU_GET_CLASS(cpu); +    CPUX86State *env = &cpu->env; +    int i; + +    xcc->parent_reset(s); + +    memset(env, 0, offsetof(CPUX86State, cpuid_level)); + +    tlb_flush(s, 1); + +    env->old_exception = -1; + +    /* init to reset state */ + +#ifdef CONFIG_SOFTMMU +    env->hflags |= HF_SOFTMMU_MASK; +#endif +    env->hflags2 |= HF2_GIF_MASK; + +    cpu_x86_update_cr0(env, 0x60000010); +    env->a20_mask = ~0x0; +    env->smbase = 0x30000; + +    env->idt.limit = 0xffff; +    env->gdt.limit = 0xffff; +    env->ldt.limit = 0xffff; +    env->ldt.flags = DESC_P_MASK | (2 << DESC_TYPE_SHIFT); +    env->tr.limit = 0xffff; +    env->tr.flags = DESC_P_MASK | (11 << DESC_TYPE_SHIFT); + +    cpu_x86_load_seg_cache(env, R_CS, 0xf000, 0xffff0000, 0xffff, +                           DESC_P_MASK | DESC_S_MASK | DESC_CS_MASK | +                           DESC_R_MASK | DESC_A_MASK); +    cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0xffff, +                           DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | +                           DESC_A_MASK); +    cpu_x86_load_seg_cache(env, R_ES, 0, 0, 0xffff, +                           DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | +                           DESC_A_MASK); +    cpu_x86_load_seg_cache(env, R_SS, 0, 0, 0xffff, +                           DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | +                           DESC_A_MASK); +    cpu_x86_load_seg_cache(env, R_FS, 0, 0, 0xffff, +                           DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | +                           DESC_A_MASK); +    cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0xffff, +                           DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | +                           DESC_A_MASK); + +    env->eip = 0xfff0; +    env->regs[R_EDX] = env->cpuid_version; + +    env->eflags = 0x2; + +    /* FPU init */ +    for (i = 0; i < 8; i++) { +        env->fptags[i] = 1; +    } +    cpu_set_fpuc(env, 0x37f); + +    env->mxcsr = 0x1f80; +    env->xstate_bv = XSTATE_FP | XSTATE_SSE; + +    env->pat = 0x0007040600070406ULL; +    env->msr_ia32_misc_enable = MSR_IA32_MISC_ENABLE_DEFAULT; + +    //JMM - EDK doesn't do this +    if (cpu->env.features[FEAT_1_ECX] & CPUID_EXT_VMX) +    	env->msr_ia32_feature_control = 5;  + +    memset(env->dr, 0, sizeof(env->dr)); +    env->dr[6] = DR6_FIXED_1; +    env->dr[7] = DR7_FIXED_1; +    cpu_breakpoint_remove_all(s, BP_CPU); +    cpu_watchpoint_remove_all(s, BP_CPU); + +    env->xcr0 = 1; + +    /* +     * SDM 11.11.5 requires: +     *  - IA32_MTRR_DEF_TYPE MSR.E = 0 +     *  - IA32_MTRR_PHYSMASKn.V = 0 +     * All other bits are undefined.  For simplification, zero it all. +     */ +    env->mtrr_deftype = 0; +    memset(env->mtrr_var, 0, sizeof(env->mtrr_var)); +    memset(env->mtrr_fixed, 0, sizeof(env->mtrr_fixed)); + +#if !defined(CONFIG_USER_ONLY) +    /* We hard-wire the BSP to the first CPU. */ +    apic_designate_bsp(cpu->apic_state, s->cpu_index == 0); + +    s->halted = !cpu_is_bsp(cpu); + +    if (kvm_enabled()) { +        kvm_arch_reset_vcpu(cpu); +    } +#endif +} + +#ifndef CONFIG_USER_ONLY +bool cpu_is_bsp(X86CPU *cpu) +{ +    return cpu_get_apic_base(cpu->apic_state) & MSR_IA32_APICBASE_BSP; +} + +/* TODO: remove me, when reset over QOM tree is implemented */ +static void x86_cpu_machine_reset_cb(void *opaque) +{ +    X86CPU *cpu = opaque; +    cpu_reset(CPU(cpu)); +} +#endif + +static void mce_init(X86CPU *cpu) +{ +    CPUX86State *cenv = &cpu->env; +    unsigned int bank; + +    if (((cenv->cpuid_version >> 8) & 0xf) >= 6 +        && (cenv->features[FEAT_1_EDX] & (CPUID_MCE | CPUID_MCA)) == +            (CPUID_MCE | CPUID_MCA)) { +        cenv->mcg_cap = MCE_CAP_DEF | MCE_BANKS_DEF; +        cenv->mcg_ctl = ~(uint64_t)0; +        for (bank = 0; bank < MCE_BANKS_DEF; bank++) { +            cenv->mce_banks[bank * 4] = ~(uint64_t)0; +        } +    } +} + +#ifndef CONFIG_USER_ONLY +static void x86_cpu_apic_create(X86CPU *cpu, Error **errp) +{ +    DeviceState *dev = DEVICE(cpu); +    APICCommonState *apic; +    const char *apic_type = "apic"; + +    if (kvm_irqchip_in_kernel()) { +        apic_type = "kvm-apic"; +    } else if (xen_enabled()) { +        apic_type = "xen-apic"; +    } + +    cpu->apic_state = qdev_try_create(qdev_get_parent_bus(dev), apic_type); +    if (cpu->apic_state == NULL) { +        error_setg(errp, "APIC device '%s' could not be created", apic_type); +        return; +    } + +    object_property_add_child(OBJECT(cpu), "apic", +                              OBJECT(cpu->apic_state), NULL); +    qdev_prop_set_uint8(cpu->apic_state, "id", cpu->apic_id); +    /* TODO: convert to link<> */ +    apic = APIC_COMMON(cpu->apic_state); +    apic->cpu = cpu; +} + +static void x86_cpu_apic_realize(X86CPU *cpu, Error **errp) +{ +    if (cpu->apic_state == NULL) { +        return; +    } +    object_property_set_bool(OBJECT(cpu->apic_state), true, "realized", +                             errp); +} + +static void x86_cpu_machine_done(Notifier *n, void *unused) +{ +    X86CPU *cpu = container_of(n, X86CPU, machine_done); +    MemoryRegion *smram = +        (MemoryRegion *) object_resolve_path("/machine/smram", NULL); + +    if (smram) { +        cpu->smram = g_new(MemoryRegion, 1); +        memory_region_init_alias(cpu->smram, OBJECT(cpu), "smram", +                                 smram, 0, 1ull << 32); +        memory_region_set_enabled(cpu->smram, false); +        memory_region_add_subregion_overlap(cpu->cpu_as_root, 0, cpu->smram, 1); +    } +} +#else +static void x86_cpu_apic_realize(X86CPU *cpu, Error **errp) +{ +} +#endif + + +#define IS_INTEL_CPU(env) ((env)->cpuid_vendor1 == CPUID_VENDOR_INTEL_1 && \ +                           (env)->cpuid_vendor2 == CPUID_VENDOR_INTEL_2 && \ +                           (env)->cpuid_vendor3 == CPUID_VENDOR_INTEL_3) +#define IS_AMD_CPU(env) ((env)->cpuid_vendor1 == CPUID_VENDOR_AMD_1 && \ +                         (env)->cpuid_vendor2 == CPUID_VENDOR_AMD_2 && \ +                         (env)->cpuid_vendor3 == CPUID_VENDOR_AMD_3) +static void x86_cpu_realizefn(DeviceState *dev, Error **errp) +{ +    CPUState *cs = CPU(dev); +    X86CPU *cpu = X86_CPU(dev); +    X86CPUClass *xcc = X86_CPU_GET_CLASS(dev); +    CPUX86State *env = &cpu->env; +    Error *local_err = NULL; +    static bool ht_warned; + +    if (cpu->apic_id < 0) { +        error_setg(errp, "apic-id property was not initialized properly"); +        return; +    } + +    if (env->features[FEAT_7_0_EBX] && env->cpuid_level < 7) { +        env->cpuid_level = 7; +    } + +    /* On AMD CPUs, some CPUID[8000_0001].EDX bits must match the bits on +     * CPUID[1].EDX. +     */ +    if (IS_AMD_CPU(env)) { +        env->features[FEAT_8000_0001_EDX] &= ~CPUID_EXT2_AMD_ALIASES; +        env->features[FEAT_8000_0001_EDX] |= (env->features[FEAT_1_EDX] +           & CPUID_EXT2_AMD_ALIASES); +    } + + +    if (x86_cpu_filter_features(cpu) && cpu->enforce_cpuid) { +        error_setg(&local_err, +                   kvm_enabled() ? +                       "Host doesn't support requested features" : +                       "TCG doesn't support requested features"); +        goto out; +    } + +#ifndef CONFIG_USER_ONLY +    qemu_register_reset(x86_cpu_machine_reset_cb, cpu); + +    if (cpu->env.features[FEAT_1_EDX] & CPUID_APIC || smp_cpus > 1) { +        x86_cpu_apic_create(cpu, &local_err); +        if (local_err != NULL) { +            goto out; +        } +    } +#endif + +    mce_init(cpu); + +#ifndef CONFIG_USER_ONLY +    if (tcg_enabled()) { +        cpu->cpu_as_mem = g_new(MemoryRegion, 1); +        cpu->cpu_as_root = g_new(MemoryRegion, 1); +        cs->as = g_new(AddressSpace, 1); + +        /* Outer container... */ +        memory_region_init(cpu->cpu_as_root, OBJECT(cpu), "memory", ~0ull); +        memory_region_set_enabled(cpu->cpu_as_root, true); + +        /* ... with two regions inside: normal system memory with low +         * priority, and... +         */ +        memory_region_init_alias(cpu->cpu_as_mem, OBJECT(cpu), "memory", +                                 get_system_memory(), 0, ~0ull); +        memory_region_add_subregion_overlap(cpu->cpu_as_root, 0, cpu->cpu_as_mem, 0); +        memory_region_set_enabled(cpu->cpu_as_mem, true); +        address_space_init(cs->as, cpu->cpu_as_root, "CPU"); + +        /* ... SMRAM with higher priority, linked from /machine/smram.  */ +        cpu->machine_done.notify = x86_cpu_machine_done; +        qemu_add_machine_init_done_notifier(&cpu->machine_done); +    } +#endif + +    qemu_init_vcpu(cs); + +    /* Only Intel CPUs support hyperthreading. Even though QEMU fixes this +     * issue by adjusting CPUID_0000_0001_EBX and CPUID_8000_0008_ECX +     * based on inputs (sockets,cores,threads), it is still better to gives +     * users a warning. +     * +     * NOTE: the following code has to follow qemu_init_vcpu(). Otherwise +     * cs->nr_threads hasn't be populated yet and the checking is incorrect. +     */ +    if (!IS_INTEL_CPU(env) && cs->nr_threads > 1 && !ht_warned) { +        error_report("AMD CPU doesn't support hyperthreading. Please configure" +                     " -smp options properly."); +        ht_warned = true; +    } + +    x86_cpu_apic_realize(cpu, &local_err); +    if (local_err != NULL) { +        goto out; +    } +    cpu_reset(cs); + +    xcc->parent_realize(dev, &local_err); + +out: +    if (local_err != NULL) { +        error_propagate(errp, local_err); +        return; +    } +} + +typedef struct BitProperty { +    uint32_t *ptr; +    uint32_t mask; +} BitProperty; + +static void x86_cpu_get_bit_prop(Object *obj, +                                 struct Visitor *v, +                                 void *opaque, +                                 const char *name, +                                 Error **errp) +{ +    BitProperty *fp = opaque; +    bool value = (*fp->ptr & fp->mask) == fp->mask; +    visit_type_bool(v, &value, name, errp); +} + +static void x86_cpu_set_bit_prop(Object *obj, +                                 struct Visitor *v, +                                 void *opaque, +                                 const char *name, +                                 Error **errp) +{ +    DeviceState *dev = DEVICE(obj); +    BitProperty *fp = opaque; +    Error *local_err = NULL; +    bool value; + +    if (dev->realized) { +        qdev_prop_set_after_realize(dev, name, errp); +        return; +    } + +    visit_type_bool(v, &value, name, &local_err); +    if (local_err) { +        error_propagate(errp, local_err); +        return; +    } + +    if (value) { +        *fp->ptr |= fp->mask; +    } else { +        *fp->ptr &= ~fp->mask; +    } +} + +static void x86_cpu_release_bit_prop(Object *obj, const char *name, +                                     void *opaque) +{ +    BitProperty *prop = opaque; +    g_free(prop); +} + +/* Register a boolean property to get/set a single bit in a uint32_t field. + * + * The same property name can be registered multiple times to make it affect + * multiple bits in the same FeatureWord. In that case, the getter will return + * true only if all bits are set. + */ +static void x86_cpu_register_bit_prop(X86CPU *cpu, +                                      const char *prop_name, +                                      uint32_t *field, +                                      int bitnr) +{ +    BitProperty *fp; +    ObjectProperty *op; +    uint32_t mask = (1UL << bitnr); + +    op = object_property_find(OBJECT(cpu), prop_name, NULL); +    if (op) { +        fp = op->opaque; +        assert(fp->ptr == field); +        fp->mask |= mask; +    } else { +        fp = g_new0(BitProperty, 1); +        fp->ptr = field; +        fp->mask = mask; +        object_property_add(OBJECT(cpu), prop_name, "bool", +                            x86_cpu_get_bit_prop, +                            x86_cpu_set_bit_prop, +                            x86_cpu_release_bit_prop, fp, &error_abort); +    } +} + +static void x86_cpu_register_feature_bit_props(X86CPU *cpu, +                                               FeatureWord w, +                                               int bitnr) +{ +    Object *obj = OBJECT(cpu); +    int i; +    char **names; +    FeatureWordInfo *fi = &feature_word_info[w]; + +    if (!fi->feat_names) { +        return; +    } +    if (!fi->feat_names[bitnr]) { +        return; +    } + +    names = g_strsplit(fi->feat_names[bitnr], "|", 0); + +    feat2prop(names[0]); +    x86_cpu_register_bit_prop(cpu, names[0], &cpu->env.features[w], bitnr); + +    for (i = 1; names[i]; i++) { +        feat2prop(names[i]); +        object_property_add_alias(obj, names[i], obj, names[0], +                                  &error_abort); +    } + +    g_strfreev(names); +} + +static void x86_cpu_initfn(Object *obj) +{ +    CPUState *cs = CPU(obj); +    X86CPU *cpu = X86_CPU(obj); +    X86CPUClass *xcc = X86_CPU_GET_CLASS(obj); +    CPUX86State *env = &cpu->env; +    FeatureWord w; +    static int inited; + +    cs->env_ptr = env; +    cpu_exec_init(cs, &error_abort); + +    object_property_add(obj, "family", "int", +                        x86_cpuid_version_get_family, +                        x86_cpuid_version_set_family, NULL, NULL, NULL); +    object_property_add(obj, "model", "int", +                        x86_cpuid_version_get_model, +                        x86_cpuid_version_set_model, NULL, NULL, NULL); +    object_property_add(obj, "stepping", "int", +                        x86_cpuid_version_get_stepping, +                        x86_cpuid_version_set_stepping, NULL, NULL, NULL); +    object_property_add_str(obj, "vendor", +                            x86_cpuid_get_vendor, +                            x86_cpuid_set_vendor, NULL); +    object_property_add_str(obj, "model-id", +                            x86_cpuid_get_model_id, +                            x86_cpuid_set_model_id, NULL); +    object_property_add(obj, "tsc-frequency", "int", +                        x86_cpuid_get_tsc_freq, +                        x86_cpuid_set_tsc_freq, NULL, NULL, NULL); +    object_property_add(obj, "apic-id", "int", +                        x86_cpuid_get_apic_id, +                        x86_cpuid_set_apic_id, NULL, NULL, NULL); +    object_property_add(obj, "feature-words", "X86CPUFeatureWordInfo", +                        x86_cpu_get_feature_words, +                        NULL, NULL, (void *)env->features, NULL); +    object_property_add(obj, "filtered-features", "X86CPUFeatureWordInfo", +                        x86_cpu_get_feature_words, +                        NULL, NULL, (void *)cpu->filtered_features, NULL); + +    cpu->hyperv_spinlock_attempts = HYPERV_SPINLOCK_NEVER_RETRY; + +#ifndef CONFIG_USER_ONLY +    /* Any code creating new X86CPU objects have to set apic-id explicitly */ +    cpu->apic_id = -1; +#endif + +    for (w = 0; w < FEATURE_WORDS; w++) { +        int bitnr; + +        for (bitnr = 0; bitnr < 32; bitnr++) { +            x86_cpu_register_feature_bit_props(cpu, w, bitnr); +        } +    } + +    x86_cpu_load_def(cpu, xcc->cpu_def, &error_abort); + +    /* init various static tables used in TCG mode */ +    if (tcg_enabled() && !inited) { +        inited = 1; +        optimize_flags_init(); +    } +} + +static int64_t x86_cpu_get_arch_id(CPUState *cs) +{ +    X86CPU *cpu = X86_CPU(cs); + +    return cpu->apic_id; +} + +static bool x86_cpu_get_paging_enabled(const CPUState *cs) +{ +    X86CPU *cpu = X86_CPU(cs); + +    return cpu->env.cr[0] & CR0_PG_MASK; +} + +static void x86_cpu_set_pc(CPUState *cs, vaddr value) +{ +    X86CPU *cpu = X86_CPU(cs); + +    cpu->env.eip = value; +} + +static void x86_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb) +{ +    X86CPU *cpu = X86_CPU(cs); + +    cpu->env.eip = tb->pc - tb->cs_base; +} + +static bool x86_cpu_has_work(CPUState *cs) +{ +    X86CPU *cpu = X86_CPU(cs); +    CPUX86State *env = &cpu->env; + +#if !defined(CONFIG_USER_ONLY) +    if (cs->interrupt_request & CPU_INTERRUPT_POLL) { +        apic_poll_irq(cpu->apic_state); +        cpu_reset_interrupt(cs, CPU_INTERRUPT_POLL); +    } +#endif + +    return ((cs->interrupt_request & CPU_INTERRUPT_HARD) && +            (env->eflags & IF_MASK)) || +           (cs->interrupt_request & (CPU_INTERRUPT_NMI | +                                     CPU_INTERRUPT_INIT | +                                     CPU_INTERRUPT_SIPI | +                                     CPU_INTERRUPT_MCE)) || +           ((cs->interrupt_request & CPU_INTERRUPT_SMI) && +            !(env->hflags & HF_SMM_MASK)); +} + +static Property x86_cpu_properties[] = { +    DEFINE_PROP_BOOL("pmu", X86CPU, enable_pmu, false), +    { .name  = "hv-spinlocks", .info  = &qdev_prop_spinlocks }, +    DEFINE_PROP_BOOL("hv-relaxed", X86CPU, hyperv_relaxed_timing, false), +    DEFINE_PROP_BOOL("hv-vapic", X86CPU, hyperv_vapic, false), +    DEFINE_PROP_BOOL("hv-time", X86CPU, hyperv_time, false), +    DEFINE_PROP_BOOL("check", X86CPU, check_cpuid, false), +    DEFINE_PROP_BOOL("enforce", X86CPU, enforce_cpuid, false), +    DEFINE_PROP_BOOL("kvm", X86CPU, expose_kvm, true), +    DEFINE_PROP_UINT32("level", X86CPU, env.cpuid_level, 0), +    DEFINE_PROP_UINT32("xlevel", X86CPU, env.cpuid_xlevel, 0), +    DEFINE_PROP_UINT32("xlevel2", X86CPU, env.cpuid_xlevel2, 0), +    DEFINE_PROP_END_OF_LIST() +}; + +static void x86_cpu_common_class_init(ObjectClass *oc, void *data) +{ +    X86CPUClass *xcc = X86_CPU_CLASS(oc); +    CPUClass *cc = CPU_CLASS(oc); +    DeviceClass *dc = DEVICE_CLASS(oc); + +    xcc->parent_realize = dc->realize; +    dc->realize = x86_cpu_realizefn; +    dc->bus_type = TYPE_ICC_BUS; +    dc->props = x86_cpu_properties; + +    xcc->parent_reset = cc->reset; +    cc->reset = x86_cpu_reset; +    cc->reset_dump_flags = CPU_DUMP_FPU | CPU_DUMP_CCOP; + +    cc->class_by_name = x86_cpu_class_by_name; +    cc->parse_features = x86_cpu_parse_featurestr; +    cc->has_work = x86_cpu_has_work; +    cc->do_interrupt = x86_cpu_do_interrupt; +    cc->cpu_exec_interrupt = x86_cpu_exec_interrupt; +    cc->dump_state = x86_cpu_dump_state; +    cc->set_pc = x86_cpu_set_pc; +    cc->synchronize_from_tb = x86_cpu_synchronize_from_tb; +    cc->gdb_read_register = x86_cpu_gdb_read_register; +    cc->gdb_write_register = x86_cpu_gdb_write_register; +    cc->get_arch_id = x86_cpu_get_arch_id; +    cc->get_paging_enabled = x86_cpu_get_paging_enabled; +#ifdef CONFIG_USER_ONLY +    cc->handle_mmu_fault = x86_cpu_handle_mmu_fault; +#else +    cc->get_memory_mapping = x86_cpu_get_memory_mapping; +    cc->get_phys_page_debug = x86_cpu_get_phys_page_debug; +    cc->write_elf64_note = x86_cpu_write_elf64_note; +    cc->write_elf64_qemunote = x86_cpu_write_elf64_qemunote; +    cc->write_elf32_note = x86_cpu_write_elf32_note; +    cc->write_elf32_qemunote = x86_cpu_write_elf32_qemunote; +    cc->vmsd = &vmstate_x86_cpu; +#endif +    cc->gdb_num_core_regs = CPU_NB_REGS * 2 + 25; +#ifndef CONFIG_USER_ONLY +    cc->debug_excp_handler = breakpoint_handler; +#endif +    cc->cpu_exec_enter = x86_cpu_exec_enter; +    cc->cpu_exec_exit = x86_cpu_exec_exit; + +    /* +     * Reason: x86_cpu_initfn() calls cpu_exec_init(), which saves the +     * object in cpus -> dangling pointer after final object_unref(). +     */ +    dc->cannot_destroy_with_object_finalize_yet = true; +} + +static const TypeInfo x86_cpu_type_info = { +    .name = TYPE_X86_CPU, +    .parent = TYPE_CPU, +    .instance_size = sizeof(X86CPU), +    .instance_init = x86_cpu_initfn, +    .abstract = true, +    .class_size = sizeof(X86CPUClass), +    .class_init = x86_cpu_common_class_init, +}; + +static void x86_cpu_register_types(void) +{ +    int i; + +    type_register_static(&x86_cpu_type_info); +    for (i = 0; i < ARRAY_SIZE(builtin_x86_defs); i++) { +        x86_register_cpudef_type(&builtin_x86_defs[i]); +    } +#ifdef CONFIG_KVM +    type_register_static(&host_x86_cpu_type_info); +#endif +} + +type_init(x86_cpu_register_types) diff --git a/target-i386/cpu.h b/target-i386/cpu.h new file mode 100644 index 00000000..ead28325 --- /dev/null +++ b/target-i386/cpu.h @@ -0,0 +1,1350 @@ +/* + * i386 virtual CPU header + * + *  Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef CPU_I386_H +#define CPU_I386_H + +#include "config.h" +#include "qemu-common.h" + +#ifdef TARGET_X86_64 +#define TARGET_LONG_BITS 64 +#else +#define TARGET_LONG_BITS 32 +#endif + +/* Maximum instruction code size */ +#define TARGET_MAX_INSN_SIZE 16 + +/* support for self modifying code even if the modified instruction is +   close to the modifying instruction */ +#define TARGET_HAS_PRECISE_SMC + +#ifdef TARGET_X86_64 +#define ELF_MACHINE     EM_X86_64 +#define ELF_MACHINE_UNAME "x86_64" +#else +#define ELF_MACHINE     EM_386 +#define ELF_MACHINE_UNAME "i686" +#endif + +#define CPUArchState struct CPUX86State + +#include "exec/cpu-defs.h" + +#include "fpu/softfloat.h" + +#define R_EAX 0 +#define R_ECX 1 +#define R_EDX 2 +#define R_EBX 3 +#define R_ESP 4 +#define R_EBP 5 +#define R_ESI 6 +#define R_EDI 7 + +#define R_AL 0 +#define R_CL 1 +#define R_DL 2 +#define R_BL 3 +#define R_AH 4 +#define R_CH 5 +#define R_DH 6 +#define R_BH 7 + +#define R_ES 0 +#define R_CS 1 +#define R_SS 2 +#define R_DS 3 +#define R_FS 4 +#define R_GS 5 + +/* segment descriptor fields */ +#define DESC_G_MASK     (1 << 23) +#define DESC_B_SHIFT    22 +#define DESC_B_MASK     (1 << DESC_B_SHIFT) +#define DESC_L_SHIFT    21 /* x86_64 only : 64 bit code segment */ +#define DESC_L_MASK     (1 << DESC_L_SHIFT) +#define DESC_AVL_MASK   (1 << 20) +#define DESC_P_MASK     (1 << 15) +#define DESC_DPL_SHIFT  13 +#define DESC_DPL_MASK   (3 << DESC_DPL_SHIFT) +#define DESC_S_MASK     (1 << 12) +#define DESC_TYPE_SHIFT 8 +#define DESC_TYPE_MASK  (15 << DESC_TYPE_SHIFT) +#define DESC_A_MASK     (1 << 8) + +#define DESC_CS_MASK    (1 << 11) /* 1=code segment 0=data segment */ +#define DESC_C_MASK     (1 << 10) /* code: conforming */ +#define DESC_R_MASK     (1 << 9)  /* code: readable */ + +#define DESC_E_MASK     (1 << 10) /* data: expansion direction */ +#define DESC_W_MASK     (1 << 9)  /* data: writable */ + +#define DESC_TSS_BUSY_MASK (1 << 9) + +/* eflags masks */ +#define CC_C    0x0001 +#define CC_P    0x0004 +#define CC_A    0x0010 +#define CC_Z    0x0040 +#define CC_S    0x0080 +#define CC_O    0x0800 + +#define TF_SHIFT   8 +#define IOPL_SHIFT 12 +#define VM_SHIFT   17 + +#define TF_MASK                 0x00000100 +#define IF_MASK                 0x00000200 +#define DF_MASK                 0x00000400 +#define IOPL_MASK               0x00003000 +#define NT_MASK                 0x00004000 +#define RF_MASK                 0x00010000 +#define VM_MASK                 0x00020000 +#define AC_MASK                 0x00040000 +#define VIF_MASK                0x00080000 +#define VIP_MASK                0x00100000 +#define ID_MASK                 0x00200000 + +/* hidden flags - used internally by qemu to represent additional cpu +   states. Only the INHIBIT_IRQ, SMM and SVMI are not redundant. We +   avoid using the IOPL_MASK, TF_MASK, VM_MASK and AC_MASK bit +   positions to ease oring with eflags. */ +/* current cpl */ +#define HF_CPL_SHIFT         0 +/* true if soft mmu is being used */ +#define HF_SOFTMMU_SHIFT     2 +/* true if hardware interrupts must be disabled for next instruction */ +#define HF_INHIBIT_IRQ_SHIFT 3 +/* 16 or 32 segments */ +#define HF_CS32_SHIFT        4 +#define HF_SS32_SHIFT        5 +/* zero base for DS, ES and SS : can be '0' only in 32 bit CS segment */ +#define HF_ADDSEG_SHIFT      6 +/* copy of CR0.PE (protected mode) */ +#define HF_PE_SHIFT          7 +#define HF_TF_SHIFT          8 /* must be same as eflags */ +#define HF_MP_SHIFT          9 /* the order must be MP, EM, TS */ +#define HF_EM_SHIFT         10 +#define HF_TS_SHIFT         11 +#define HF_IOPL_SHIFT       12 /* must be same as eflags */ +#define HF_LMA_SHIFT        14 /* only used on x86_64: long mode active */ +#define HF_CS64_SHIFT       15 /* only used on x86_64: 64 bit code segment  */ +#define HF_RF_SHIFT         16 /* must be same as eflags */ +#define HF_VM_SHIFT         17 /* must be same as eflags */ +#define HF_AC_SHIFT         18 /* must be same as eflags */ +#define HF_SMM_SHIFT        19 /* CPU in SMM mode */ +#define HF_SVME_SHIFT       20 /* SVME enabled (copy of EFER.SVME) */ +#define HF_SVMI_SHIFT       21 /* SVM intercepts are active */ +#define HF_OSFXSR_SHIFT     22 /* CR4.OSFXSR */ +#define HF_SMAP_SHIFT       23 /* CR4.SMAP */ + +#define HF_CPL_MASK          (3 << HF_CPL_SHIFT) +#define HF_SOFTMMU_MASK      (1 << HF_SOFTMMU_SHIFT) +#define HF_INHIBIT_IRQ_MASK  (1 << HF_INHIBIT_IRQ_SHIFT) +#define HF_CS32_MASK         (1 << HF_CS32_SHIFT) +#define HF_SS32_MASK         (1 << HF_SS32_SHIFT) +#define HF_ADDSEG_MASK       (1 << HF_ADDSEG_SHIFT) +#define HF_PE_MASK           (1 << HF_PE_SHIFT) +#define HF_TF_MASK           (1 << HF_TF_SHIFT) +#define HF_MP_MASK           (1 << HF_MP_SHIFT) +#define HF_EM_MASK           (1 << HF_EM_SHIFT) +#define HF_TS_MASK           (1 << HF_TS_SHIFT) +#define HF_IOPL_MASK         (3 << HF_IOPL_SHIFT) +#define HF_LMA_MASK          (1 << HF_LMA_SHIFT) +#define HF_CS64_MASK         (1 << HF_CS64_SHIFT) +#define HF_RF_MASK           (1 << HF_RF_SHIFT) +#define HF_VM_MASK           (1 << HF_VM_SHIFT) +#define HF_AC_MASK           (1 << HF_AC_SHIFT) +#define HF_SMM_MASK          (1 << HF_SMM_SHIFT) +#define HF_SVME_MASK         (1 << HF_SVME_SHIFT) +#define HF_SVMI_MASK         (1 << HF_SVMI_SHIFT) +#define HF_OSFXSR_MASK       (1 << HF_OSFXSR_SHIFT) +#define HF_SMAP_MASK         (1 << HF_SMAP_SHIFT) + +/* hflags2 */ + +#define HF2_GIF_SHIFT            0 /* if set CPU takes interrupts */ +#define HF2_HIF_SHIFT            1 /* value of IF_MASK when entering SVM */ +#define HF2_NMI_SHIFT            2 /* CPU serving NMI */ +#define HF2_VINTR_SHIFT          3 /* value of V_INTR_MASKING bit */ +#define HF2_SMM_INSIDE_NMI_SHIFT 4 /* CPU serving SMI nested inside NMI */ + +#define HF2_GIF_MASK            (1 << HF2_GIF_SHIFT) +#define HF2_HIF_MASK            (1 << HF2_HIF_SHIFT) +#define HF2_NMI_MASK            (1 << HF2_NMI_SHIFT) +#define HF2_VINTR_MASK          (1 << HF2_VINTR_SHIFT) +#define HF2_SMM_INSIDE_NMI_MASK (1 << HF2_SMM_INSIDE_NMI_SHIFT) + +#define CR0_PE_SHIFT 0 +#define CR0_MP_SHIFT 1 + +#define CR0_PE_MASK  (1U << 0) +#define CR0_MP_MASK  (1U << 1) +#define CR0_EM_MASK  (1U << 2) +#define CR0_TS_MASK  (1U << 3) +#define CR0_ET_MASK  (1U << 4) +#define CR0_NE_MASK  (1U << 5) +#define CR0_WP_MASK  (1U << 16) +#define CR0_AM_MASK  (1U << 18) +#define CR0_PG_MASK  (1U << 31) + +#define CR4_VME_MASK  (1U << 0) +#define CR4_PVI_MASK  (1U << 1) +#define CR4_TSD_MASK  (1U << 2) +#define CR4_DE_MASK   (1U << 3) +#define CR4_PSE_MASK  (1U << 4) +#define CR4_PAE_MASK  (1U << 5) +#define CR4_MCE_MASK  (1U << 6) +#define CR4_PGE_MASK  (1U << 7) +#define CR4_PCE_MASK  (1U << 8) +#define CR4_OSFXSR_SHIFT 9 +#define CR4_OSFXSR_MASK (1U << CR4_OSFXSR_SHIFT) +#define CR4_OSXMMEXCPT_MASK  (1U << 10) +#define CR4_VMXE_MASK   (1U << 13) +#define CR4_SMXE_MASK   (1U << 14) +#define CR4_FSGSBASE_MASK (1U << 16) +#define CR4_PCIDE_MASK  (1U << 17) +#define CR4_OSXSAVE_MASK (1U << 18) +#define CR4_SMEP_MASK   (1U << 20) +#define CR4_SMAP_MASK   (1U << 21) + +#define DR6_BD          (1 << 13) +#define DR6_BS          (1 << 14) +#define DR6_BT          (1 << 15) +#define DR6_FIXED_1     0xffff0ff0 + +#define DR7_GD          (1 << 13) +#define DR7_TYPE_SHIFT  16 +#define DR7_LEN_SHIFT   18 +#define DR7_FIXED_1     0x00000400 +#define DR7_LOCAL_BP_MASK    0x55 +#define DR7_MAX_BP           4 +#define DR7_TYPE_BP_INST     0x0 +#define DR7_TYPE_DATA_WR     0x1 +#define DR7_TYPE_IO_RW       0x2 +#define DR7_TYPE_DATA_RW     0x3 + +#define PG_PRESENT_BIT  0 +#define PG_RW_BIT       1 +#define PG_USER_BIT     2 +#define PG_PWT_BIT      3 +#define PG_PCD_BIT      4 +#define PG_ACCESSED_BIT 5 +#define PG_DIRTY_BIT    6 +#define PG_PSE_BIT      7 +#define PG_GLOBAL_BIT   8 +#define PG_PSE_PAT_BIT  12 +#define PG_NX_BIT       63 + +#define PG_PRESENT_MASK  (1 << PG_PRESENT_BIT) +#define PG_RW_MASK       (1 << PG_RW_BIT) +#define PG_USER_MASK     (1 << PG_USER_BIT) +#define PG_PWT_MASK      (1 << PG_PWT_BIT) +#define PG_PCD_MASK      (1 << PG_PCD_BIT) +#define PG_ACCESSED_MASK (1 << PG_ACCESSED_BIT) +#define PG_DIRTY_MASK    (1 << PG_DIRTY_BIT) +#define PG_PSE_MASK      (1 << PG_PSE_BIT) +#define PG_GLOBAL_MASK   (1 << PG_GLOBAL_BIT) +#define PG_PSE_PAT_MASK  (1 << PG_PSE_PAT_BIT) +#define PG_ADDRESS_MASK  0x000ffffffffff000LL +#define PG_HI_RSVD_MASK  (PG_ADDRESS_MASK & ~PHYS_ADDR_MASK) +#define PG_HI_USER_MASK  0x7ff0000000000000LL +#define PG_NX_MASK       (1LL << PG_NX_BIT) + +#define PG_ERROR_W_BIT     1 + +#define PG_ERROR_P_MASK    0x01 +#define PG_ERROR_W_MASK    (1 << PG_ERROR_W_BIT) +#define PG_ERROR_U_MASK    0x04 +#define PG_ERROR_RSVD_MASK 0x08 +#define PG_ERROR_I_D_MASK  0x10 + +#define MCG_CTL_P       (1ULL<<8)   /* MCG_CAP register available */ +#define MCG_SER_P       (1ULL<<24) /* MCA recovery/new status bits */ + +#define MCE_CAP_DEF     (MCG_CTL_P|MCG_SER_P) +#define MCE_BANKS_DEF   10 + +#define MCG_STATUS_RIPV (1ULL<<0)   /* restart ip valid */ +#define MCG_STATUS_EIPV (1ULL<<1)   /* ip points to correct instruction */ +#define MCG_STATUS_MCIP (1ULL<<2)   /* machine check in progress */ + +#define MCI_STATUS_VAL   (1ULL<<63)  /* valid error */ +#define MCI_STATUS_OVER  (1ULL<<62)  /* previous errors lost */ +#define MCI_STATUS_UC    (1ULL<<61)  /* uncorrected error */ +#define MCI_STATUS_EN    (1ULL<<60)  /* error enabled */ +#define MCI_STATUS_MISCV (1ULL<<59)  /* misc error reg. valid */ +#define MCI_STATUS_ADDRV (1ULL<<58)  /* addr reg. valid */ +#define MCI_STATUS_PCC   (1ULL<<57)  /* processor context corrupt */ +#define MCI_STATUS_S     (1ULL<<56)  /* Signaled machine check */ +#define MCI_STATUS_AR    (1ULL<<55)  /* Action required */ + +/* MISC register defines */ +#define MCM_ADDR_SEGOFF  0      /* segment offset */ +#define MCM_ADDR_LINEAR  1      /* linear address */ +#define MCM_ADDR_PHYS    2      /* physical address */ +#define MCM_ADDR_MEM     3      /* memory address */ +#define MCM_ADDR_GENERIC 7      /* generic */ + +#define MSR_IA32_TSC                    0x10 +#define MSR_IA32_APICBASE               0x1b +#define MSR_IA32_APICBASE_BSP           (1<<8) +#define MSR_IA32_APICBASE_ENABLE        (1<<11) +#define MSR_IA32_APICBASE_BASE          (0xfffffU<<12) +#define MSR_IA32_FEATURE_CONTROL        0x0000003a +#define MSR_TSC_ADJUST                  0x0000003b +#define MSR_IA32_TSCDEADLINE            0x6e0 + +#define MSR_P6_PERFCTR0                 0xc1 + +#define MSR_IA32_SMBASE                 0x9e +#define MSR_MTRRcap                     0xfe +#define MSR_MTRRcap_VCNT                8 +#define MSR_MTRRcap_FIXRANGE_SUPPORT    (1 << 8) +#define MSR_MTRRcap_WC_SUPPORTED        (1 << 10) + +#define MSR_IA32_SYSENTER_CS            0x174 +#define MSR_IA32_SYSENTER_ESP           0x175 +#define MSR_IA32_SYSENTER_EIP           0x176 + +#define MSR_MCG_CAP                     0x179 +#define MSR_MCG_STATUS                  0x17a +#define MSR_MCG_CTL                     0x17b + +#define MSR_P6_EVNTSEL0                 0x186 + +#define MSR_IA32_PERF_STATUS            0x198 + +#define MSR_IA32_MISC_ENABLE            0x1a0 +/* Indicates good rep/movs microcode on some processors: */ +#define MSR_IA32_MISC_ENABLE_DEFAULT    1 + +#define MSR_MTRRphysBase(reg)           (0x200 + 2 * (reg)) +#define MSR_MTRRphysMask(reg)           (0x200 + 2 * (reg) + 1) + +#define MSR_MTRRphysIndex(addr)         ((((addr) & ~1u) - 0x200) / 2) + +#define MSR_MTRRfix64K_00000            0x250 +#define MSR_MTRRfix16K_80000            0x258 +#define MSR_MTRRfix16K_A0000            0x259 +#define MSR_MTRRfix4K_C0000             0x268 +#define MSR_MTRRfix4K_C8000             0x269 +#define MSR_MTRRfix4K_D0000             0x26a +#define MSR_MTRRfix4K_D8000             0x26b +#define MSR_MTRRfix4K_E0000             0x26c +#define MSR_MTRRfix4K_E8000             0x26d +#define MSR_MTRRfix4K_F0000             0x26e +#define MSR_MTRRfix4K_F8000             0x26f + +#define MSR_PAT                         0x277 + +#define MSR_MTRRdefType                 0x2ff + +#define MSR_CORE_PERF_FIXED_CTR0        0x309 +#define MSR_CORE_PERF_FIXED_CTR1        0x30a +#define MSR_CORE_PERF_FIXED_CTR2        0x30b +#define MSR_CORE_PERF_FIXED_CTR_CTRL    0x38d +#define MSR_CORE_PERF_GLOBAL_STATUS     0x38e +#define MSR_CORE_PERF_GLOBAL_CTRL       0x38f +#define MSR_CORE_PERF_GLOBAL_OVF_CTRL   0x390 + +#define MSR_MC0_CTL                     0x400 +#define MSR_MC0_STATUS                  0x401 +#define MSR_MC0_ADDR                    0x402 +#define MSR_MC0_MISC                    0x403 + +#define MSR_EFER                        0xc0000080 + +#define MSR_EFER_SCE   (1 << 0) +#define MSR_EFER_LME   (1 << 8) +#define MSR_EFER_LMA   (1 << 10) +#define MSR_EFER_NXE   (1 << 11) +#define MSR_EFER_SVME  (1 << 12) +#define MSR_EFER_FFXSR (1 << 14) + +#define MSR_STAR                        0xc0000081 +#define MSR_LSTAR                       0xc0000082 +#define MSR_CSTAR                       0xc0000083 +#define MSR_FMASK                       0xc0000084 +#define MSR_FSBASE                      0xc0000100 +#define MSR_GSBASE                      0xc0000101 +#define MSR_KERNELGSBASE                0xc0000102 +#define MSR_TSC_AUX                     0xc0000103 + +#define MSR_VM_HSAVE_PA                 0xc0010117 + +#define MSR_IA32_BNDCFGS                0x00000d90 +#define MSR_IA32_XSS                    0x00000da0 + +#define XSTATE_FP                       (1ULL << 0) +#define XSTATE_SSE                      (1ULL << 1) +#define XSTATE_YMM                      (1ULL << 2) +#define XSTATE_BNDREGS                  (1ULL << 3) +#define XSTATE_BNDCSR                   (1ULL << 4) +#define XSTATE_OPMASK                   (1ULL << 5) +#define XSTATE_ZMM_Hi256                (1ULL << 6) +#define XSTATE_Hi16_ZMM                 (1ULL << 7) + + +/* CPUID feature words */ +typedef enum FeatureWord { +    FEAT_1_EDX,         /* CPUID[1].EDX */ +    FEAT_1_ECX,         /* CPUID[1].ECX */ +    FEAT_7_0_EBX,       /* CPUID[EAX=7,ECX=0].EBX */ +    FEAT_8000_0001_EDX, /* CPUID[8000_0001].EDX */ +    FEAT_8000_0001_ECX, /* CPUID[8000_0001].ECX */ +    FEAT_8000_0007_EDX, /* CPUID[8000_0007].EDX */ +    FEAT_C000_0001_EDX, /* CPUID[C000_0001].EDX */ +    FEAT_KVM,           /* CPUID[4000_0001].EAX (KVM_CPUID_FEATURES) */ +    FEAT_SVM,           /* CPUID[8000_000A].EDX */ +    FEAT_XSAVE,         /* CPUID[EAX=0xd,ECX=1].EAX */ +    FEAT_6_EAX,         /* CPUID[6].EAX */ +    FEATURE_WORDS, +} FeatureWord; + +typedef uint32_t FeatureWordArray[FEATURE_WORDS]; + +/* cpuid_features bits */ +#define CPUID_FP87 (1U << 0) +#define CPUID_VME  (1U << 1) +#define CPUID_DE   (1U << 2) +#define CPUID_PSE  (1U << 3) +#define CPUID_TSC  (1U << 4) +#define CPUID_MSR  (1U << 5) +#define CPUID_PAE  (1U << 6) +#define CPUID_MCE  (1U << 7) +#define CPUID_CX8  (1U << 8) +#define CPUID_APIC (1U << 9) +#define CPUID_SEP  (1U << 11) /* sysenter/sysexit */ +#define CPUID_MTRR (1U << 12) +#define CPUID_PGE  (1U << 13) +#define CPUID_MCA  (1U << 14) +#define CPUID_CMOV (1U << 15) +#define CPUID_PAT  (1U << 16) +#define CPUID_PSE36   (1U << 17) +#define CPUID_PN   (1U << 18) +#define CPUID_CLFLUSH (1U << 19) +#define CPUID_DTS (1U << 21) +#define CPUID_ACPI (1U << 22) +#define CPUID_MMX  (1U << 23) +#define CPUID_FXSR (1U << 24) +#define CPUID_SSE  (1U << 25) +#define CPUID_SSE2 (1U << 26) +#define CPUID_SS (1U << 27) +#define CPUID_HT (1U << 28) +#define CPUID_TM (1U << 29) +#define CPUID_IA64 (1U << 30) +#define CPUID_PBE (1U << 31) + +#define CPUID_EXT_SSE3     (1U << 0) +#define CPUID_EXT_PCLMULQDQ (1U << 1) +#define CPUID_EXT_DTES64   (1U << 2) +#define CPUID_EXT_MONITOR  (1U << 3) +#define CPUID_EXT_DSCPL    (1U << 4) +#define CPUID_EXT_VMX      (1U << 5) +#define CPUID_EXT_SMX      (1U << 6) +#define CPUID_EXT_EST      (1U << 7) +#define CPUID_EXT_TM2      (1U << 8) +#define CPUID_EXT_SSSE3    (1U << 9) +#define CPUID_EXT_CID      (1U << 10) +#define CPUID_EXT_FMA      (1U << 12) +#define CPUID_EXT_CX16     (1U << 13) +#define CPUID_EXT_XTPR     (1U << 14) +#define CPUID_EXT_PDCM     (1U << 15) +#define CPUID_EXT_PCID     (1U << 17) +#define CPUID_EXT_DCA      (1U << 18) +#define CPUID_EXT_SSE41    (1U << 19) +#define CPUID_EXT_SSE42    (1U << 20) +#define CPUID_EXT_X2APIC   (1U << 21) +#define CPUID_EXT_MOVBE    (1U << 22) +#define CPUID_EXT_POPCNT   (1U << 23) +#define CPUID_EXT_TSC_DEADLINE_TIMER (1U << 24) +#define CPUID_EXT_AES      (1U << 25) +#define CPUID_EXT_XSAVE    (1U << 26) +#define CPUID_EXT_OSXSAVE  (1U << 27) +#define CPUID_EXT_AVX      (1U << 28) +#define CPUID_EXT_F16C     (1U << 29) +#define CPUID_EXT_RDRAND   (1U << 30) +#define CPUID_EXT_HYPERVISOR  (1U << 31) + +#define CPUID_EXT2_FPU     (1U << 0) +#define CPUID_EXT2_VME     (1U << 1) +#define CPUID_EXT2_DE      (1U << 2) +#define CPUID_EXT2_PSE     (1U << 3) +#define CPUID_EXT2_TSC     (1U << 4) +#define CPUID_EXT2_MSR     (1U << 5) +#define CPUID_EXT2_PAE     (1U << 6) +#define CPUID_EXT2_MCE     (1U << 7) +#define CPUID_EXT2_CX8     (1U << 8) +#define CPUID_EXT2_APIC    (1U << 9) +#define CPUID_EXT2_SYSCALL (1U << 11) +#define CPUID_EXT2_MTRR    (1U << 12) +#define CPUID_EXT2_PGE     (1U << 13) +#define CPUID_EXT2_MCA     (1U << 14) +#define CPUID_EXT2_CMOV    (1U << 15) +#define CPUID_EXT2_PAT     (1U << 16) +#define CPUID_EXT2_PSE36   (1U << 17) +#define CPUID_EXT2_MP      (1U << 19) +#define CPUID_EXT2_NX      (1U << 20) +#define CPUID_EXT2_MMXEXT  (1U << 22) +#define CPUID_EXT2_MMX     (1U << 23) +#define CPUID_EXT2_FXSR    (1U << 24) +#define CPUID_EXT2_FFXSR   (1U << 25) +#define CPUID_EXT2_PDPE1GB (1U << 26) +#define CPUID_EXT2_RDTSCP  (1U << 27) +#define CPUID_EXT2_LM      (1U << 29) +#define CPUID_EXT2_3DNOWEXT (1U << 30) +#define CPUID_EXT2_3DNOW   (1U << 31) + +/* CPUID[8000_0001].EDX bits that are aliase of CPUID[1].EDX bits on AMD CPUs */ +#define CPUID_EXT2_AMD_ALIASES (CPUID_EXT2_FPU | CPUID_EXT2_VME | \ +                                CPUID_EXT2_DE | CPUID_EXT2_PSE | \ +                                CPUID_EXT2_TSC | CPUID_EXT2_MSR | \ +                                CPUID_EXT2_PAE | CPUID_EXT2_MCE | \ +                                CPUID_EXT2_CX8 | CPUID_EXT2_APIC | \ +                                CPUID_EXT2_MTRR | CPUID_EXT2_PGE | \ +                                CPUID_EXT2_MCA | CPUID_EXT2_CMOV | \ +                                CPUID_EXT2_PAT | CPUID_EXT2_PSE36 | \ +                                CPUID_EXT2_MMX | CPUID_EXT2_FXSR) + +#define CPUID_EXT3_LAHF_LM (1U << 0) +#define CPUID_EXT3_CMP_LEG (1U << 1) +#define CPUID_EXT3_SVM     (1U << 2) +#define CPUID_EXT3_EXTAPIC (1U << 3) +#define CPUID_EXT3_CR8LEG  (1U << 4) +#define CPUID_EXT3_ABM     (1U << 5) +#define CPUID_EXT3_SSE4A   (1U << 6) +#define CPUID_EXT3_MISALIGNSSE (1U << 7) +#define CPUID_EXT3_3DNOWPREFETCH (1U << 8) +#define CPUID_EXT3_OSVW    (1U << 9) +#define CPUID_EXT3_IBS     (1U << 10) +#define CPUID_EXT3_XOP     (1U << 11) +#define CPUID_EXT3_SKINIT  (1U << 12) +#define CPUID_EXT3_WDT     (1U << 13) +#define CPUID_EXT3_LWP     (1U << 15) +#define CPUID_EXT3_FMA4    (1U << 16) +#define CPUID_EXT3_TCE     (1U << 17) +#define CPUID_EXT3_NODEID  (1U << 19) +#define CPUID_EXT3_TBM     (1U << 21) +#define CPUID_EXT3_TOPOEXT (1U << 22) +#define CPUID_EXT3_PERFCORE (1U << 23) +#define CPUID_EXT3_PERFNB  (1U << 24) + +#define CPUID_SVM_NPT          (1U << 0) +#define CPUID_SVM_LBRV         (1U << 1) +#define CPUID_SVM_SVMLOCK      (1U << 2) +#define CPUID_SVM_NRIPSAVE     (1U << 3) +#define CPUID_SVM_TSCSCALE     (1U << 4) +#define CPUID_SVM_VMCBCLEAN    (1U << 5) +#define CPUID_SVM_FLUSHASID    (1U << 6) +#define CPUID_SVM_DECODEASSIST (1U << 7) +#define CPUID_SVM_PAUSEFILTER  (1U << 10) +#define CPUID_SVM_PFTHRESHOLD  (1U << 12) + +#define CPUID_7_0_EBX_FSGSBASE (1U << 0) +#define CPUID_7_0_EBX_BMI1     (1U << 3) +#define CPUID_7_0_EBX_HLE      (1U << 4) +#define CPUID_7_0_EBX_AVX2     (1U << 5) +#define CPUID_7_0_EBX_SMEP     (1U << 7) +#define CPUID_7_0_EBX_BMI2     (1U << 8) +#define CPUID_7_0_EBX_ERMS     (1U << 9) +#define CPUID_7_0_EBX_INVPCID  (1U << 10) +#define CPUID_7_0_EBX_RTM      (1U << 11) +#define CPUID_7_0_EBX_MPX      (1U << 14) +#define CPUID_7_0_EBX_AVX512F  (1U << 16) /* AVX-512 Foundation */ +#define CPUID_7_0_EBX_RDSEED   (1U << 18) +#define CPUID_7_0_EBX_ADX      (1U << 19) +#define CPUID_7_0_EBX_SMAP     (1U << 20) +#define CPUID_7_0_EBX_AVX512PF (1U << 26) /* AVX-512 Prefetch */ +#define CPUID_7_0_EBX_AVX512ER (1U << 27) /* AVX-512 Exponential and Reciprocal */ +#define CPUID_7_0_EBX_AVX512CD (1U << 28) /* AVX-512 Conflict Detection */ + +#define CPUID_XSAVE_XSAVEOPT   (1U << 0) +#define CPUID_XSAVE_XSAVEC     (1U << 1) +#define CPUID_XSAVE_XGETBV1    (1U << 2) +#define CPUID_XSAVE_XSAVES     (1U << 3) + +#define CPUID_6_EAX_ARAT       (1U << 2) + +/* CPUID[0x80000007].EDX flags: */ +#define CPUID_APM_INVTSC       (1U << 8) + +#define CPUID_VENDOR_SZ      12 + +#define CPUID_VENDOR_INTEL_1 0x756e6547 /* "Genu" */ +#define CPUID_VENDOR_INTEL_2 0x49656e69 /* "ineI" */ +#define CPUID_VENDOR_INTEL_3 0x6c65746e /* "ntel" */ +#define CPUID_VENDOR_INTEL "GenuineIntel" + +#define CPUID_VENDOR_AMD_1   0x68747541 /* "Auth" */ +#define CPUID_VENDOR_AMD_2   0x69746e65 /* "enti" */ +#define CPUID_VENDOR_AMD_3   0x444d4163 /* "cAMD" */ +#define CPUID_VENDOR_AMD   "AuthenticAMD" + +#define CPUID_VENDOR_VIA   "CentaurHauls" + +#define CPUID_MWAIT_IBE     (1U << 1) /* Interrupts can exit capability */ +#define CPUID_MWAIT_EMX     (1U << 0) /* enumeration supported */ + +#ifndef HYPERV_SPINLOCK_NEVER_RETRY +#define HYPERV_SPINLOCK_NEVER_RETRY             0xFFFFFFFF +#endif + +#define EXCP00_DIVZ	0 +#define EXCP01_DB	1 +#define EXCP02_NMI	2 +#define EXCP03_INT3	3 +#define EXCP04_INTO	4 +#define EXCP05_BOUND	5 +#define EXCP06_ILLOP	6 +#define EXCP07_PREX	7 +#define EXCP08_DBLE	8 +#define EXCP09_XERR	9 +#define EXCP0A_TSS	10 +#define EXCP0B_NOSEG	11 +#define EXCP0C_STACK	12 +#define EXCP0D_GPF	13 +#define EXCP0E_PAGE	14 +#define EXCP10_COPR	16 +#define EXCP11_ALGN	17 +#define EXCP12_MCHK	18 + +#define EXCP_SYSCALL    0x100 /* only happens in user only emulation +                                 for syscall instruction */ + +/* i386-specific interrupt pending bits.  */ +#define CPU_INTERRUPT_POLL      CPU_INTERRUPT_TGT_EXT_1 +#define CPU_INTERRUPT_SMI       CPU_INTERRUPT_TGT_EXT_2 +#define CPU_INTERRUPT_NMI       CPU_INTERRUPT_TGT_EXT_3 +#define CPU_INTERRUPT_MCE       CPU_INTERRUPT_TGT_EXT_4 +#define CPU_INTERRUPT_VIRQ      CPU_INTERRUPT_TGT_INT_0 +#define CPU_INTERRUPT_SIPI      CPU_INTERRUPT_TGT_INT_1 +#define CPU_INTERRUPT_TPR       CPU_INTERRUPT_TGT_INT_2 + +/* Use a clearer name for this.  */ +#define CPU_INTERRUPT_INIT      CPU_INTERRUPT_RESET + +typedef enum { +    CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */ +    CC_OP_EFLAGS,  /* all cc are explicitly computed, CC_SRC = flags */ + +    CC_OP_MULB, /* modify all flags, C, O = (CC_SRC != 0) */ +    CC_OP_MULW, +    CC_OP_MULL, +    CC_OP_MULQ, + +    CC_OP_ADDB, /* modify all flags, CC_DST = res, CC_SRC = src1 */ +    CC_OP_ADDW, +    CC_OP_ADDL, +    CC_OP_ADDQ, + +    CC_OP_ADCB, /* modify all flags, CC_DST = res, CC_SRC = src1 */ +    CC_OP_ADCW, +    CC_OP_ADCL, +    CC_OP_ADCQ, + +    CC_OP_SUBB, /* modify all flags, CC_DST = res, CC_SRC = src1 */ +    CC_OP_SUBW, +    CC_OP_SUBL, +    CC_OP_SUBQ, + +    CC_OP_SBBB, /* modify all flags, CC_DST = res, CC_SRC = src1 */ +    CC_OP_SBBW, +    CC_OP_SBBL, +    CC_OP_SBBQ, + +    CC_OP_LOGICB, /* modify all flags, CC_DST = res */ +    CC_OP_LOGICW, +    CC_OP_LOGICL, +    CC_OP_LOGICQ, + +    CC_OP_INCB, /* modify all flags except, CC_DST = res, CC_SRC = C */ +    CC_OP_INCW, +    CC_OP_INCL, +    CC_OP_INCQ, + +    CC_OP_DECB, /* modify all flags except, CC_DST = res, CC_SRC = C  */ +    CC_OP_DECW, +    CC_OP_DECL, +    CC_OP_DECQ, + +    CC_OP_SHLB, /* modify all flags, CC_DST = res, CC_SRC.msb = C */ +    CC_OP_SHLW, +    CC_OP_SHLL, +    CC_OP_SHLQ, + +    CC_OP_SARB, /* modify all flags, CC_DST = res, CC_SRC.lsb = C */ +    CC_OP_SARW, +    CC_OP_SARL, +    CC_OP_SARQ, + +    CC_OP_BMILGB, /* Z,S via CC_DST, C = SRC==0; O=0; P,A undefined */ +    CC_OP_BMILGW, +    CC_OP_BMILGL, +    CC_OP_BMILGQ, + +    CC_OP_ADCX, /* CC_DST = C, CC_SRC = rest.  */ +    CC_OP_ADOX, /* CC_DST = O, CC_SRC = rest.  */ +    CC_OP_ADCOX, /* CC_DST = C, CC_SRC2 = O, CC_SRC = rest.  */ + +    CC_OP_CLR, /* Z set, all other flags clear.  */ + +    CC_OP_NB, +} CCOp; + +typedef struct SegmentCache { +    uint32_t selector; +    target_ulong base; +    uint32_t limit; +    uint32_t flags; +} SegmentCache; + +typedef union { +    uint8_t _b[64]; +    uint16_t _w[32]; +    uint32_t _l[16]; +    uint64_t _q[8]; +    float32 _s[16]; +    float64 _d[8]; +} XMMReg; /* really zmm */ + +typedef union { +    uint8_t _b[8]; +    uint16_t _w[4]; +    uint32_t _l[2]; +    float32 _s[2]; +    uint64_t q; +} MMXReg; + +typedef struct BNDReg { +    uint64_t lb; +    uint64_t ub; +} BNDReg; + +typedef struct BNDCSReg { +    uint64_t cfgu; +    uint64_t sts; +} BNDCSReg; + +#ifdef HOST_WORDS_BIGENDIAN +#define XMM_B(n) _b[63 - (n)] +#define XMM_W(n) _w[31 - (n)] +#define XMM_L(n) _l[15 - (n)] +#define XMM_S(n) _s[15 - (n)] +#define XMM_Q(n) _q[7 - (n)] +#define XMM_D(n) _d[7 - (n)] + +#define MMX_B(n) _b[7 - (n)] +#define MMX_W(n) _w[3 - (n)] +#define MMX_L(n) _l[1 - (n)] +#define MMX_S(n) _s[1 - (n)] +#else +#define XMM_B(n) _b[n] +#define XMM_W(n) _w[n] +#define XMM_L(n) _l[n] +#define XMM_S(n) _s[n] +#define XMM_Q(n) _q[n] +#define XMM_D(n) _d[n] + +#define MMX_B(n) _b[n] +#define MMX_W(n) _w[n] +#define MMX_L(n) _l[n] +#define MMX_S(n) _s[n] +#endif +#define MMX_Q(n) q + +typedef union { +    floatx80 d __attribute__((aligned(16))); +    MMXReg mmx; +} FPReg; + +typedef struct { +    uint64_t base; +    uint64_t mask; +} MTRRVar; + +#define CPU_NB_REGS64 16 +#define CPU_NB_REGS32 8 + +#ifdef TARGET_X86_64 +#define CPU_NB_REGS CPU_NB_REGS64 +#else +#define CPU_NB_REGS CPU_NB_REGS32 +#endif + +#define MAX_FIXED_COUNTERS 3 +#define MAX_GP_COUNTERS    (MSR_IA32_PERF_STATUS - MSR_P6_EVNTSEL0) + +#define NB_MMU_MODES 3 + +#define NB_OPMASK_REGS 8 + +typedef enum TPRAccess { +    TPR_ACCESS_READ, +    TPR_ACCESS_WRITE, +} TPRAccess; + +typedef struct CPUX86State { +    /* standard registers */ +    target_ulong regs[CPU_NB_REGS]; +    target_ulong eip; +    target_ulong eflags; /* eflags register. During CPU emulation, CC +                        flags and DF are set to zero because they are +                        stored elsewhere */ + +    /* emulator internal eflags handling */ +    target_ulong cc_dst; +    target_ulong cc_src; +    target_ulong cc_src2; +    uint32_t cc_op; +    int32_t df; /* D flag : 1 if D = 0, -1 if D = 1 */ +    uint32_t hflags; /* TB flags, see HF_xxx constants. These flags +                        are known at translation time. */ +    uint32_t hflags2; /* various other flags, see HF2_xxx constants. */ + +    /* segments */ +    SegmentCache segs[6]; /* selector values */ +    SegmentCache ldt; +    SegmentCache tr; +    SegmentCache gdt; /* only base and limit are used */ +    SegmentCache idt; /* only base and limit are used */ + +    target_ulong cr[5]; /* NOTE: cr1 is unused */ +    int32_t a20_mask; + +    BNDReg bnd_regs[4]; +    BNDCSReg bndcs_regs; +    uint64_t msr_bndcfgs; + +    /* Beginning of state preserved by INIT (dummy marker).  */ +    struct {} start_init_save; + +    /* FPU state */ +    unsigned int fpstt; /* top of stack index */ +    uint16_t fpus; +    uint16_t fpuc; +    uint8_t fptags[8];   /* 0 = valid, 1 = empty */ +    FPReg fpregs[8]; +    /* KVM-only so far */ +    uint16_t fpop; +    uint64_t fpip; +    uint64_t fpdp; + +    /* emulator internal variables */ +    float_status fp_status; +    floatx80 ft0; + +    float_status mmx_status; /* for 3DNow! float ops */ +    float_status sse_status; +    uint32_t mxcsr; +    XMMReg xmm_regs[CPU_NB_REGS == 8 ? 8 : 32]; +    XMMReg xmm_t0; +    MMXReg mmx_t0; + +    uint64_t opmask_regs[NB_OPMASK_REGS]; + +    /* sysenter registers */ +    uint32_t sysenter_cs; +    target_ulong sysenter_esp; +    target_ulong sysenter_eip; +    uint64_t efer; +    uint64_t star; + +    uint64_t vm_hsave; + +#ifdef TARGET_X86_64 +    target_ulong lstar; +    target_ulong cstar; +    target_ulong fmask; +    target_ulong kernelgsbase; +#endif + +    uint64_t tsc; +    uint64_t tsc_adjust; +    uint64_t tsc_deadline; + +    uint64_t mcg_status; +    uint64_t msr_ia32_misc_enable; +    uint64_t msr_ia32_feature_control; + +    uint64_t msr_fixed_ctr_ctrl; +    uint64_t msr_global_ctrl; +    uint64_t msr_global_status; +    uint64_t msr_global_ovf_ctrl; +    uint64_t msr_fixed_counters[MAX_FIXED_COUNTERS]; +    uint64_t msr_gp_counters[MAX_GP_COUNTERS]; +    uint64_t msr_gp_evtsel[MAX_GP_COUNTERS]; + +    uint64_t pat; +    uint32_t smbase; + +    /* End of state preserved by INIT (dummy marker).  */ +    struct {} end_init_save; + +    uint64_t system_time_msr; +    uint64_t wall_clock_msr; +    uint64_t steal_time_msr; +    uint64_t async_pf_en_msr; +    uint64_t pv_eoi_en_msr; + +    uint64_t msr_hv_hypercall; +    uint64_t msr_hv_guest_os_id; +    uint64_t msr_hv_vapic; +    uint64_t msr_hv_tsc; + +    /* exception/interrupt handling */ +    int error_code; +    int exception_is_int; +    target_ulong exception_next_eip; +    target_ulong dr[8]; /* debug registers */ +    union { +        struct CPUBreakpoint *cpu_breakpoint[4]; +        struct CPUWatchpoint *cpu_watchpoint[4]; +    }; /* break/watchpoints for dr[0..3] */ +    int old_exception;  /* exception in flight */ + +    uint64_t vm_vmcb; +    uint64_t tsc_offset; +    uint64_t intercept; +    uint16_t intercept_cr_read; +    uint16_t intercept_cr_write; +    uint16_t intercept_dr_read; +    uint16_t intercept_dr_write; +    uint32_t intercept_exceptions; +    uint8_t v_tpr; + +    /* KVM states, automatically cleared on reset */ +    uint8_t nmi_injected; +    uint8_t nmi_pending; + +    CPU_COMMON + +    /* Fields from here on are preserved across CPU reset. */ + +    /* processor features (e.g. for CPUID insn) */ +    uint32_t cpuid_level; +    uint32_t cpuid_xlevel; +    uint32_t cpuid_xlevel2; +    uint32_t cpuid_vendor1; +    uint32_t cpuid_vendor2; +    uint32_t cpuid_vendor3; +    uint32_t cpuid_version; +    FeatureWordArray features; +    uint32_t cpuid_model[12]; + +    /* MTRRs */ +    uint64_t mtrr_fixed[11]; +    uint64_t mtrr_deftype; +    MTRRVar mtrr_var[MSR_MTRRcap_VCNT]; + +    /* For KVM */ +    uint32_t mp_state; +    int32_t exception_injected; +    int32_t interrupt_injected; +    uint8_t soft_interrupt; +    uint8_t has_error_code; +    uint32_t sipi_vector; +    bool tsc_valid; +    int64_t tsc_khz; +    void *kvm_xsave_buf; + +    uint64_t mcg_cap; +    uint64_t mcg_ctl; +    uint64_t mce_banks[MCE_BANKS_DEF*4]; + +    uint64_t tsc_aux; + +    /* vmstate */ +    uint16_t fpus_vmstate; +    uint16_t fptag_vmstate; +    uint16_t fpregs_format_vmstate; +    uint64_t xstate_bv; + +    uint64_t xcr0; +    uint64_t xss; + +    TPRAccess tpr_access_type; +} CPUX86State; + +#include "cpu-qom.h" + +X86CPU *cpu_x86_init(const char *cpu_model); +X86CPU *cpu_x86_create(const char *cpu_model, Error **errp); +int cpu_x86_exec(CPUState *cpu); +void x86_cpu_list(FILE *f, fprintf_function cpu_fprintf); +void x86_cpudef_setup(void); +int cpu_x86_support_mca_broadcast(CPUX86State *env); + +int cpu_get_pic_interrupt(CPUX86State *s); +/* MSDOS compatibility mode FPU exception support */ +void cpu_set_ferr(CPUX86State *s); + +/* this function must always be used to load data in the segment +   cache: it synchronizes the hflags with the segment cache values */ +static inline void cpu_x86_load_seg_cache(CPUX86State *env, +                                          int seg_reg, unsigned int selector, +                                          target_ulong base, +                                          unsigned int limit, +                                          unsigned int flags) +{ +    SegmentCache *sc; +    unsigned int new_hflags; + +    sc = &env->segs[seg_reg]; +    sc->selector = selector; +    sc->base = base; +    sc->limit = limit; +    sc->flags = flags; + +    /* update the hidden flags */ +    { +        if (seg_reg == R_CS) { +#ifdef TARGET_X86_64 +            if ((env->hflags & HF_LMA_MASK) && (flags & DESC_L_MASK)) { +                /* long mode */ +                env->hflags |= HF_CS32_MASK | HF_SS32_MASK | HF_CS64_MASK; +                env->hflags &= ~(HF_ADDSEG_MASK); +            } else +#endif +            { +                /* legacy / compatibility case */ +                new_hflags = (env->segs[R_CS].flags & DESC_B_MASK) +                    >> (DESC_B_SHIFT - HF_CS32_SHIFT); +                env->hflags = (env->hflags & ~(HF_CS32_MASK | HF_CS64_MASK)) | +                    new_hflags; +            } +        } +        if (seg_reg == R_SS) { +            int cpl = (flags >> DESC_DPL_SHIFT) & 3; +#if HF_CPL_MASK != 3 +#error HF_CPL_MASK is hardcoded +#endif +            env->hflags = (env->hflags & ~HF_CPL_MASK) | cpl; +        } +        new_hflags = (env->segs[R_SS].flags & DESC_B_MASK) +            >> (DESC_B_SHIFT - HF_SS32_SHIFT); +        if (env->hflags & HF_CS64_MASK) { +            /* zero base assumed for DS, ES and SS in long mode */ +        } else if (!(env->cr[0] & CR0_PE_MASK) || +                   (env->eflags & VM_MASK) || +                   !(env->hflags & HF_CS32_MASK)) { +            /* XXX: try to avoid this test. The problem comes from the +               fact that is real mode or vm86 mode we only modify the +               'base' and 'selector' fields of the segment cache to go +               faster. A solution may be to force addseg to one in +               translate-i386.c. */ +            new_hflags |= HF_ADDSEG_MASK; +        } else { +            new_hflags |= ((env->segs[R_DS].base | +                            env->segs[R_ES].base | +                            env->segs[R_SS].base) != 0) << +                HF_ADDSEG_SHIFT; +        } +        env->hflags = (env->hflags & +                       ~(HF_SS32_MASK | HF_ADDSEG_MASK)) | new_hflags; +    } +} + +static inline void cpu_x86_load_seg_cache_sipi(X86CPU *cpu, +                                               uint8_t sipi_vector) +{ +    CPUState *cs = CPU(cpu); +    CPUX86State *env = &cpu->env; + +    env->eip = 0; +    cpu_x86_load_seg_cache(env, R_CS, sipi_vector << 8, +                           sipi_vector << 12, +                           env->segs[R_CS].limit, +                           env->segs[R_CS].flags); +    cs->halted = 0; +} + +int cpu_x86_get_descr_debug(CPUX86State *env, unsigned int selector, +                            target_ulong *base, unsigned int *limit, +                            unsigned int *flags); + +/* op_helper.c */ +/* used for debug or cpu save/restore */ +void cpu_get_fp80(uint64_t *pmant, uint16_t *pexp, floatx80 f); +floatx80 cpu_set_fp80(uint64_t mant, uint16_t upper); + +/* cpu-exec.c */ +/* the following helpers are only usable in user mode simulation as +   they can trigger unexpected exceptions */ +void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector); +void cpu_x86_fsave(CPUX86State *s, target_ulong ptr, int data32); +void cpu_x86_frstor(CPUX86State *s, target_ulong ptr, int data32); + +/* you can call this signal handler from your SIGBUS and SIGSEGV +   signal handlers to inform the virtual CPU of exceptions. non zero +   is returned if the signal was handled by the virtual CPU.  */ +int cpu_x86_signal_handler(int host_signum, void *pinfo, +                           void *puc); + +/* cpuid.c */ +void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, +                   uint32_t *eax, uint32_t *ebx, +                   uint32_t *ecx, uint32_t *edx); +void cpu_clear_apic_feature(CPUX86State *env); +void host_cpuid(uint32_t function, uint32_t count, +                uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx); + +/* helper.c */ +int x86_cpu_handle_mmu_fault(CPUState *cpu, vaddr addr, +                             int is_write, int mmu_idx); +void x86_cpu_set_a20(X86CPU *cpu, int a20_state); + +#ifndef CONFIG_USER_ONLY +uint8_t x86_ldub_phys(CPUState *cs, hwaddr addr); +uint32_t x86_lduw_phys(CPUState *cs, hwaddr addr); +uint32_t x86_ldl_phys(CPUState *cs, hwaddr addr); +uint64_t x86_ldq_phys(CPUState *cs, hwaddr addr); +void x86_stb_phys(CPUState *cs, hwaddr addr, uint8_t val); +void x86_stl_phys_notdirty(CPUState *cs, hwaddr addr, uint32_t val); +void x86_stw_phys(CPUState *cs, hwaddr addr, uint32_t val); +void x86_stl_phys(CPUState *cs, hwaddr addr, uint32_t val); +void x86_stq_phys(CPUState *cs, hwaddr addr, uint64_t val); +#endif + +static inline bool hw_local_breakpoint_enabled(unsigned long dr7, int index) +{ +    return (dr7 >> (index * 2)) & 1; +} + +static inline bool hw_global_breakpoint_enabled(unsigned long dr7, int index) +{ +    return (dr7 >> (index * 2)) & 2; + +} +static inline bool hw_breakpoint_enabled(unsigned long dr7, int index) +{ +    return hw_global_breakpoint_enabled(dr7, index) || +           hw_local_breakpoint_enabled(dr7, index); +} + +static inline int hw_breakpoint_type(unsigned long dr7, int index) +{ +    return (dr7 >> (DR7_TYPE_SHIFT + (index * 4))) & 3; +} + +static inline int hw_breakpoint_len(unsigned long dr7, int index) +{ +    int len = ((dr7 >> (DR7_LEN_SHIFT + (index * 4))) & 3); +    return (len == 2) ? 8 : len + 1; +} + +void hw_breakpoint_insert(CPUX86State *env, int index); +void hw_breakpoint_remove(CPUX86State *env, int index); +bool check_hw_breakpoints(CPUX86State *env, bool force_dr6_update); +void breakpoint_handler(CPUState *cs); + +/* will be suppressed */ +void cpu_x86_update_cr0(CPUX86State *env, uint32_t new_cr0); +void cpu_x86_update_cr3(CPUX86State *env, target_ulong new_cr3); +void cpu_x86_update_cr4(CPUX86State *env, uint32_t new_cr4); + +/* hw/pc.c */ +uint64_t cpu_get_tsc(CPUX86State *env); + +#define TARGET_PAGE_BITS 12 + +#ifdef TARGET_X86_64 +#define TARGET_PHYS_ADDR_SPACE_BITS 52 +/* ??? This is really 48 bits, sign-extended, but the only thing +   accessible to userland with bit 48 set is the VSYSCALL, and that +   is handled via other mechanisms.  */ +#define TARGET_VIRT_ADDR_SPACE_BITS 47 +#else +#define TARGET_PHYS_ADDR_SPACE_BITS 36 +#define TARGET_VIRT_ADDR_SPACE_BITS 32 +#endif + +/* XXX: This value should match the one returned by CPUID + * and in exec.c */ +# if defined(TARGET_X86_64) +# define PHYS_ADDR_MASK 0xffffffffffLL +# else +# define PHYS_ADDR_MASK 0xfffffffffLL +# endif + +#define cpu_init(cpu_model) CPU(cpu_x86_init(cpu_model)) + +#define cpu_exec cpu_x86_exec +#define cpu_gen_code cpu_x86_gen_code +#define cpu_signal_handler cpu_x86_signal_handler +#define cpu_list x86_cpu_list +#define cpudef_setup x86_cpudef_setup + +/* MMU modes definitions */ +#define MMU_MODE0_SUFFIX _ksmap +#define MMU_MODE1_SUFFIX _user +#define MMU_MODE2_SUFFIX _knosmap /* SMAP disabled or CPL<3 && AC=1 */ +#define MMU_KSMAP_IDX   0 +#define MMU_USER_IDX    1 +#define MMU_KNOSMAP_IDX 2 +static inline int cpu_mmu_index(CPUX86State *env) +{ +    return (env->hflags & HF_CPL_MASK) == 3 ? MMU_USER_IDX : +        (!(env->hflags & HF_SMAP_MASK) || (env->eflags & AC_MASK)) +        ? MMU_KNOSMAP_IDX : MMU_KSMAP_IDX; +} + +static inline int cpu_mmu_index_kernel(CPUX86State *env) +{ +    return !(env->hflags & HF_SMAP_MASK) ? MMU_KNOSMAP_IDX : +        ((env->hflags & HF_CPL_MASK) < 3 && (env->eflags & AC_MASK)) +        ? MMU_KNOSMAP_IDX : MMU_KSMAP_IDX; +} + +#define CC_DST  (env->cc_dst) +#define CC_SRC  (env->cc_src) +#define CC_SRC2 (env->cc_src2) +#define CC_OP   (env->cc_op) + +/* n must be a constant to be efficient */ +static inline target_long lshift(target_long x, int n) +{ +    if (n >= 0) { +        return x << n; +    } else { +        return x >> (-n); +    } +} + +/* float macros */ +#define FT0    (env->ft0) +#define ST0    (env->fpregs[env->fpstt].d) +#define ST(n)  (env->fpregs[(env->fpstt + (n)) & 7].d) +#define ST1    ST(1) + +/* translate.c */ +void optimize_flags_init(void); + +#include "exec/cpu-all.h" +#include "svm.h" + +#if !defined(CONFIG_USER_ONLY) +#include "hw/i386/apic.h" +#endif + +#include "exec/exec-all.h" + +static inline void cpu_get_tb_cpu_state(CPUX86State *env, target_ulong *pc, +                                        target_ulong *cs_base, int *flags) +{ +    *cs_base = env->segs[R_CS].base; +    *pc = *cs_base + env->eip; +    *flags = env->hflags | +        (env->eflags & (IOPL_MASK | TF_MASK | RF_MASK | VM_MASK | AC_MASK)); +} + +void do_cpu_init(X86CPU *cpu); +void do_cpu_sipi(X86CPU *cpu); + +#define MCE_INJECT_BROADCAST    1 +#define MCE_INJECT_UNCOND_AO    2 + +void cpu_x86_inject_mce(Monitor *mon, X86CPU *cpu, int bank, +                        uint64_t status, uint64_t mcg_status, uint64_t addr, +                        uint64_t misc, int flags); + +/* excp_helper.c */ +void QEMU_NORETURN raise_exception(CPUX86State *env, int exception_index); +void QEMU_NORETURN raise_exception_err(CPUX86State *env, int exception_index, +                                       int error_code); +void QEMU_NORETURN raise_interrupt(CPUX86State *nenv, int intno, int is_int, +                                   int error_code, int next_eip_addend); + +/* cc_helper.c */ +extern const uint8_t parity_table[256]; +uint32_t cpu_cc_compute_all(CPUX86State *env1, int op); +void update_fp_status(CPUX86State *env); + +static inline uint32_t cpu_compute_eflags(CPUX86State *env) +{ +    return env->eflags | cpu_cc_compute_all(env, CC_OP) | (env->df & DF_MASK); +} + +/* NOTE: the translator must set DisasContext.cc_op to CC_OP_EFLAGS + * after generating a call to a helper that uses this. + */ +static inline void cpu_load_eflags(CPUX86State *env, int eflags, +                                   int update_mask) +{ +    CC_SRC = eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); +    CC_OP = CC_OP_EFLAGS; +    env->df = 1 - (2 * ((eflags >> 10) & 1)); +    env->eflags = (env->eflags & ~update_mask) | +        (eflags & update_mask) | 0x2; +} + +/* load efer and update the corresponding hflags. XXX: do consistency +   checks with cpuid bits? */ +static inline void cpu_load_efer(CPUX86State *env, uint64_t val) +{ +    env->efer = val; +    env->hflags &= ~(HF_LMA_MASK | HF_SVME_MASK); +    if (env->efer & MSR_EFER_LMA) { +        env->hflags |= HF_LMA_MASK; +    } +    if (env->efer & MSR_EFER_SVME) { +        env->hflags |= HF_SVME_MASK; +    } +} + +static inline MemTxAttrs cpu_get_mem_attrs(CPUX86State *env) +{ +    return ((MemTxAttrs) { .secure = (env->hflags & HF_SMM_MASK) != 0 }); +} + +/* fpu_helper.c */ +void cpu_set_mxcsr(CPUX86State *env, uint32_t val); +void cpu_set_fpuc(CPUX86State *env, uint16_t val); + +/* svm_helper.c */ +void cpu_svm_check_intercept_param(CPUX86State *env1, uint32_t type, +                                   uint64_t param); +void cpu_vmexit(CPUX86State *nenv, uint32_t exit_code, uint64_t exit_info_1); + +/* seg_helper.c */ +void do_interrupt_x86_hardirq(CPUX86State *env, int intno, int is_hw); + +/* smm_helper.c */ +void do_smm_enter(X86CPU *cpu); +void cpu_smm_update(X86CPU *cpu); + +void cpu_report_tpr_access(CPUX86State *env, TPRAccess access); + +void x86_cpu_compat_set_features(const char *cpu_model, FeatureWord w, +                                 uint32_t feat_add, uint32_t feat_remove); + +void x86_cpu_compat_kvm_no_autoenable(FeatureWord w, uint32_t features); +void x86_cpu_compat_kvm_no_autodisable(FeatureWord w, uint32_t features); + + +/* Return name of 32-bit register, from a R_* constant */ +const char *get_register_name_32(unsigned int reg); + +void enable_compat_apic_id_mode(void); + +#define APIC_DEFAULT_ADDRESS 0xfee00000 +#define APIC_SPACE_SIZE      0x100000 + +#endif /* CPU_I386_H */ diff --git a/target-i386/excp_helper.c b/target-i386/excp_helper.c new file mode 100644 index 00000000..99fca847 --- /dev/null +++ b/target-i386/excp_helper.c @@ -0,0 +1,131 @@ +/* + *  x86 exception helpers + * + *  Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "cpu.h" +#include "qemu/log.h" +#include "sysemu/sysemu.h" +#include "exec/helper-proto.h" + +#if 0 +#define raise_exception_err(env, a, b)                                  \ +    do {                                                                \ +        qemu_log("raise_exception line=%d\n", __LINE__);                \ +        (raise_exception_err)(env, a, b);                               \ +    } while (0) +#endif + +void helper_raise_interrupt(CPUX86State *env, int intno, int next_eip_addend) +{ +    raise_interrupt(env, intno, 1, 0, next_eip_addend); +} + +void helper_raise_exception(CPUX86State *env, int exception_index) +{ +    raise_exception(env, exception_index); +} + +/* + * Check nested exceptions and change to double or triple fault if + * needed. It should only be called, if this is not an interrupt. + * Returns the new exception number. + */ +static int check_exception(CPUX86State *env, int intno, int *error_code) +{ +    int first_contributory = env->old_exception == 0 || +                              (env->old_exception >= 10 && +                               env->old_exception <= 13); +    int second_contributory = intno == 0 || +                               (intno >= 10 && intno <= 13); + +    qemu_log_mask(CPU_LOG_INT, "check_exception old: 0x%x new 0x%x\n", +                env->old_exception, intno); + +#if !defined(CONFIG_USER_ONLY) +    if (env->old_exception == EXCP08_DBLE) { +        if (env->hflags & HF_SVMI_MASK) { +            cpu_vmexit(env, SVM_EXIT_SHUTDOWN, 0); /* does not return */ +        } + +        qemu_log_mask(CPU_LOG_RESET, "Triple fault\n"); + +        qemu_system_reset_request(); +        return EXCP_HLT; +    } +#endif + +    if ((first_contributory && second_contributory) +        || (env->old_exception == EXCP0E_PAGE && +            (second_contributory || (intno == EXCP0E_PAGE)))) { +        intno = EXCP08_DBLE; +        *error_code = 0; +    } + +    if (second_contributory || (intno == EXCP0E_PAGE) || +        (intno == EXCP08_DBLE)) { +        env->old_exception = intno; +    } + +    return intno; +} + +/* + * Signal an interruption. It is executed in the main CPU loop. + * is_int is TRUE if coming from the int instruction. next_eip is the + * env->eip value AFTER the interrupt instruction. It is only relevant if + * is_int is TRUE. + */ +static void QEMU_NORETURN raise_interrupt2(CPUX86State *env, int intno, +                                           int is_int, int error_code, +                                           int next_eip_addend) +{ +    CPUState *cs = CPU(x86_env_get_cpu(env)); + +    if (!is_int) { +        cpu_svm_check_intercept_param(env, SVM_EXIT_EXCP_BASE + intno, +                                      error_code); +        intno = check_exception(env, intno, &error_code); +    } else { +        cpu_svm_check_intercept_param(env, SVM_EXIT_SWINT, 0); +    } + +    cs->exception_index = intno; +    env->error_code = error_code; +    env->exception_is_int = is_int; +    env->exception_next_eip = env->eip + next_eip_addend; +    cpu_loop_exit(cs); +} + +/* shortcuts to generate exceptions */ + +void QEMU_NORETURN raise_interrupt(CPUX86State *env, int intno, int is_int, +                                   int error_code, int next_eip_addend) +{ +    raise_interrupt2(env, intno, is_int, error_code, next_eip_addend); +} + +void raise_exception_err(CPUX86State *env, int exception_index, +                         int error_code) +{ +    raise_interrupt2(env, exception_index, 0, error_code, 0); +} + +void raise_exception(CPUX86State *env, int exception_index) +{ +    raise_interrupt2(env, exception_index, 0, 0, 0); +} diff --git a/target-i386/fpu_helper.c b/target-i386/fpu_helper.c new file mode 100644 index 00000000..1f954e0c --- /dev/null +++ b/target-i386/fpu_helper.c @@ -0,0 +1,1311 @@ +/* + *  x86 FPU, MMX/3DNow!/SSE/SSE2/SSE3/SSSE3/SSE4/PNI helpers + * + *  Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <math.h> +#include "cpu.h" +#include "exec/helper-proto.h" +#include "qemu/host-utils.h" +#include "exec/cpu_ldst.h" + +#define FPU_RC_MASK         0xc00 +#define FPU_RC_NEAR         0x000 +#define FPU_RC_DOWN         0x400 +#define FPU_RC_UP           0x800 +#define FPU_RC_CHOP         0xc00 + +#define MAXTAN 9223372036854775808.0 + +/* the following deal with x86 long double-precision numbers */ +#define MAXEXPD 0x7fff +#define EXPBIAS 16383 +#define EXPD(fp)        (fp.l.upper & 0x7fff) +#define SIGND(fp)       ((fp.l.upper) & 0x8000) +#define MANTD(fp)       (fp.l.lower) +#define BIASEXPONENT(fp) fp.l.upper = (fp.l.upper & ~(0x7fff)) | EXPBIAS + +#define FPUS_IE (1 << 0) +#define FPUS_DE (1 << 1) +#define FPUS_ZE (1 << 2) +#define FPUS_OE (1 << 3) +#define FPUS_UE (1 << 4) +#define FPUS_PE (1 << 5) +#define FPUS_SF (1 << 6) +#define FPUS_SE (1 << 7) +#define FPUS_B  (1 << 15) + +#define FPUC_EM 0x3f + +#define floatx80_lg2 make_floatx80(0x3ffd, 0x9a209a84fbcff799LL) +#define floatx80_l2e make_floatx80(0x3fff, 0xb8aa3b295c17f0bcLL) +#define floatx80_l2t make_floatx80(0x4000, 0xd49a784bcd1b8afeLL) + +static inline void fpush(CPUX86State *env) +{ +    env->fpstt = (env->fpstt - 1) & 7; +    env->fptags[env->fpstt] = 0; /* validate stack entry */ +} + +static inline void fpop(CPUX86State *env) +{ +    env->fptags[env->fpstt] = 1; /* invalidate stack entry */ +    env->fpstt = (env->fpstt + 1) & 7; +} + +static inline floatx80 helper_fldt(CPUX86State *env, target_ulong ptr) +{ +    CPU_LDoubleU temp; + +    temp.l.lower = cpu_ldq_data(env, ptr); +    temp.l.upper = cpu_lduw_data(env, ptr + 8); +    return temp.d; +} + +static inline void helper_fstt(CPUX86State *env, floatx80 f, target_ulong ptr) +{ +    CPU_LDoubleU temp; + +    temp.d = f; +    cpu_stq_data(env, ptr, temp.l.lower); +    cpu_stw_data(env, ptr + 8, temp.l.upper); +} + +/* x87 FPU helpers */ + +static inline double floatx80_to_double(CPUX86State *env, floatx80 a) +{ +    union { +        float64 f64; +        double d; +    } u; + +    u.f64 = floatx80_to_float64(a, &env->fp_status); +    return u.d; +} + +static inline floatx80 double_to_floatx80(CPUX86State *env, double a) +{ +    union { +        float64 f64; +        double d; +    } u; + +    u.d = a; +    return float64_to_floatx80(u.f64, &env->fp_status); +} + +static void fpu_set_exception(CPUX86State *env, int mask) +{ +    env->fpus |= mask; +    if (env->fpus & (~env->fpuc & FPUC_EM)) { +        env->fpus |= FPUS_SE | FPUS_B; +    } +} + +static inline floatx80 helper_fdiv(CPUX86State *env, floatx80 a, floatx80 b) +{ +    if (floatx80_is_zero(b)) { +        fpu_set_exception(env, FPUS_ZE); +    } +    return floatx80_div(a, b, &env->fp_status); +} + +static void fpu_raise_exception(CPUX86State *env) +{ +    if (env->cr[0] & CR0_NE_MASK) { +        raise_exception(env, EXCP10_COPR); +    } +#if !defined(CONFIG_USER_ONLY) +    else { +        cpu_set_ferr(env); +    } +#endif +} + +void helper_flds_FT0(CPUX86State *env, uint32_t val) +{ +    union { +        float32 f; +        uint32_t i; +    } u; + +    u.i = val; +    FT0 = float32_to_floatx80(u.f, &env->fp_status); +} + +void helper_fldl_FT0(CPUX86State *env, uint64_t val) +{ +    union { +        float64 f; +        uint64_t i; +    } u; + +    u.i = val; +    FT0 = float64_to_floatx80(u.f, &env->fp_status); +} + +void helper_fildl_FT0(CPUX86State *env, int32_t val) +{ +    FT0 = int32_to_floatx80(val, &env->fp_status); +} + +void helper_flds_ST0(CPUX86State *env, uint32_t val) +{ +    int new_fpstt; +    union { +        float32 f; +        uint32_t i; +    } u; + +    new_fpstt = (env->fpstt - 1) & 7; +    u.i = val; +    env->fpregs[new_fpstt].d = float32_to_floatx80(u.f, &env->fp_status); +    env->fpstt = new_fpstt; +    env->fptags[new_fpstt] = 0; /* validate stack entry */ +} + +void helper_fldl_ST0(CPUX86State *env, uint64_t val) +{ +    int new_fpstt; +    union { +        float64 f; +        uint64_t i; +    } u; + +    new_fpstt = (env->fpstt - 1) & 7; +    u.i = val; +    env->fpregs[new_fpstt].d = float64_to_floatx80(u.f, &env->fp_status); +    env->fpstt = new_fpstt; +    env->fptags[new_fpstt] = 0; /* validate stack entry */ +} + +void helper_fildl_ST0(CPUX86State *env, int32_t val) +{ +    int new_fpstt; + +    new_fpstt = (env->fpstt - 1) & 7; +    env->fpregs[new_fpstt].d = int32_to_floatx80(val, &env->fp_status); +    env->fpstt = new_fpstt; +    env->fptags[new_fpstt] = 0; /* validate stack entry */ +} + +void helper_fildll_ST0(CPUX86State *env, int64_t val) +{ +    int new_fpstt; + +    new_fpstt = (env->fpstt - 1) & 7; +    env->fpregs[new_fpstt].d = int64_to_floatx80(val, &env->fp_status); +    env->fpstt = new_fpstt; +    env->fptags[new_fpstt] = 0; /* validate stack entry */ +} + +uint32_t helper_fsts_ST0(CPUX86State *env) +{ +    union { +        float32 f; +        uint32_t i; +    } u; + +    u.f = floatx80_to_float32(ST0, &env->fp_status); +    return u.i; +} + +uint64_t helper_fstl_ST0(CPUX86State *env) +{ +    union { +        float64 f; +        uint64_t i; +    } u; + +    u.f = floatx80_to_float64(ST0, &env->fp_status); +    return u.i; +} + +int32_t helper_fist_ST0(CPUX86State *env) +{ +    int32_t val; + +    val = floatx80_to_int32(ST0, &env->fp_status); +    if (val != (int16_t)val) { +        val = -32768; +    } +    return val; +} + +int32_t helper_fistl_ST0(CPUX86State *env) +{ +    int32_t val; +    signed char old_exp_flags; + +    old_exp_flags = get_float_exception_flags(&env->fp_status); +    set_float_exception_flags(0, &env->fp_status); + +    val = floatx80_to_int32(ST0, &env->fp_status); +    if (get_float_exception_flags(&env->fp_status) & float_flag_invalid) { +        val = 0x80000000; +    } +    set_float_exception_flags(get_float_exception_flags(&env->fp_status) +                                | old_exp_flags, &env->fp_status); +    return val; +} + +int64_t helper_fistll_ST0(CPUX86State *env) +{ +    int64_t val; +    signed char old_exp_flags; + +    old_exp_flags = get_float_exception_flags(&env->fp_status); +    set_float_exception_flags(0, &env->fp_status); + +    val = floatx80_to_int64(ST0, &env->fp_status); +    if (get_float_exception_flags(&env->fp_status) & float_flag_invalid) { +        val = 0x8000000000000000ULL; +    } +    set_float_exception_flags(get_float_exception_flags(&env->fp_status) +                                | old_exp_flags, &env->fp_status); +    return val; +} + +int32_t helper_fistt_ST0(CPUX86State *env) +{ +    int32_t val; + +    val = floatx80_to_int32_round_to_zero(ST0, &env->fp_status); +    if (val != (int16_t)val) { +        val = -32768; +    } +    return val; +} + +int32_t helper_fisttl_ST0(CPUX86State *env) +{ +    int32_t val; + +    val = floatx80_to_int32_round_to_zero(ST0, &env->fp_status); +    return val; +} + +int64_t helper_fisttll_ST0(CPUX86State *env) +{ +    int64_t val; + +    val = floatx80_to_int64_round_to_zero(ST0, &env->fp_status); +    return val; +} + +void helper_fldt_ST0(CPUX86State *env, target_ulong ptr) +{ +    int new_fpstt; + +    new_fpstt = (env->fpstt - 1) & 7; +    env->fpregs[new_fpstt].d = helper_fldt(env, ptr); +    env->fpstt = new_fpstt; +    env->fptags[new_fpstt] = 0; /* validate stack entry */ +} + +void helper_fstt_ST0(CPUX86State *env, target_ulong ptr) +{ +    helper_fstt(env, ST0, ptr); +} + +void helper_fpush(CPUX86State *env) +{ +    fpush(env); +} + +void helper_fpop(CPUX86State *env) +{ +    fpop(env); +} + +void helper_fdecstp(CPUX86State *env) +{ +    env->fpstt = (env->fpstt - 1) & 7; +    env->fpus &= ~0x4700; +} + +void helper_fincstp(CPUX86State *env) +{ +    env->fpstt = (env->fpstt + 1) & 7; +    env->fpus &= ~0x4700; +} + +/* FPU move */ + +void helper_ffree_STN(CPUX86State *env, int st_index) +{ +    env->fptags[(env->fpstt + st_index) & 7] = 1; +} + +void helper_fmov_ST0_FT0(CPUX86State *env) +{ +    ST0 = FT0; +} + +void helper_fmov_FT0_STN(CPUX86State *env, int st_index) +{ +    FT0 = ST(st_index); +} + +void helper_fmov_ST0_STN(CPUX86State *env, int st_index) +{ +    ST0 = ST(st_index); +} + +void helper_fmov_STN_ST0(CPUX86State *env, int st_index) +{ +    ST(st_index) = ST0; +} + +void helper_fxchg_ST0_STN(CPUX86State *env, int st_index) +{ +    floatx80 tmp; + +    tmp = ST(st_index); +    ST(st_index) = ST0; +    ST0 = tmp; +} + +/* FPU operations */ + +static const int fcom_ccval[4] = {0x0100, 0x4000, 0x0000, 0x4500}; + +void helper_fcom_ST0_FT0(CPUX86State *env) +{ +    int ret; + +    ret = floatx80_compare(ST0, FT0, &env->fp_status); +    env->fpus = (env->fpus & ~0x4500) | fcom_ccval[ret + 1]; +} + +void helper_fucom_ST0_FT0(CPUX86State *env) +{ +    int ret; + +    ret = floatx80_compare_quiet(ST0, FT0, &env->fp_status); +    env->fpus = (env->fpus & ~0x4500) | fcom_ccval[ret + 1]; +} + +static const int fcomi_ccval[4] = {CC_C, CC_Z, 0, CC_Z | CC_P | CC_C}; + +void helper_fcomi_ST0_FT0(CPUX86State *env) +{ +    int eflags; +    int ret; + +    ret = floatx80_compare(ST0, FT0, &env->fp_status); +    eflags = cpu_cc_compute_all(env, CC_OP); +    eflags = (eflags & ~(CC_Z | CC_P | CC_C)) | fcomi_ccval[ret + 1]; +    CC_SRC = eflags; +} + +void helper_fucomi_ST0_FT0(CPUX86State *env) +{ +    int eflags; +    int ret; + +    ret = floatx80_compare_quiet(ST0, FT0, &env->fp_status); +    eflags = cpu_cc_compute_all(env, CC_OP); +    eflags = (eflags & ~(CC_Z | CC_P | CC_C)) | fcomi_ccval[ret + 1]; +    CC_SRC = eflags; +} + +void helper_fadd_ST0_FT0(CPUX86State *env) +{ +    ST0 = floatx80_add(ST0, FT0, &env->fp_status); +} + +void helper_fmul_ST0_FT0(CPUX86State *env) +{ +    ST0 = floatx80_mul(ST0, FT0, &env->fp_status); +} + +void helper_fsub_ST0_FT0(CPUX86State *env) +{ +    ST0 = floatx80_sub(ST0, FT0, &env->fp_status); +} + +void helper_fsubr_ST0_FT0(CPUX86State *env) +{ +    ST0 = floatx80_sub(FT0, ST0, &env->fp_status); +} + +void helper_fdiv_ST0_FT0(CPUX86State *env) +{ +    ST0 = helper_fdiv(env, ST0, FT0); +} + +void helper_fdivr_ST0_FT0(CPUX86State *env) +{ +    ST0 = helper_fdiv(env, FT0, ST0); +} + +/* fp operations between STN and ST0 */ + +void helper_fadd_STN_ST0(CPUX86State *env, int st_index) +{ +    ST(st_index) = floatx80_add(ST(st_index), ST0, &env->fp_status); +} + +void helper_fmul_STN_ST0(CPUX86State *env, int st_index) +{ +    ST(st_index) = floatx80_mul(ST(st_index), ST0, &env->fp_status); +} + +void helper_fsub_STN_ST0(CPUX86State *env, int st_index) +{ +    ST(st_index) = floatx80_sub(ST(st_index), ST0, &env->fp_status); +} + +void helper_fsubr_STN_ST0(CPUX86State *env, int st_index) +{ +    ST(st_index) = floatx80_sub(ST0, ST(st_index), &env->fp_status); +} + +void helper_fdiv_STN_ST0(CPUX86State *env, int st_index) +{ +    floatx80 *p; + +    p = &ST(st_index); +    *p = helper_fdiv(env, *p, ST0); +} + +void helper_fdivr_STN_ST0(CPUX86State *env, int st_index) +{ +    floatx80 *p; + +    p = &ST(st_index); +    *p = helper_fdiv(env, ST0, *p); +} + +/* misc FPU operations */ +void helper_fchs_ST0(CPUX86State *env) +{ +    ST0 = floatx80_chs(ST0); +} + +void helper_fabs_ST0(CPUX86State *env) +{ +    ST0 = floatx80_abs(ST0); +} + +void helper_fld1_ST0(CPUX86State *env) +{ +    ST0 = floatx80_one; +} + +void helper_fldl2t_ST0(CPUX86State *env) +{ +    ST0 = floatx80_l2t; +} + +void helper_fldl2e_ST0(CPUX86State *env) +{ +    ST0 = floatx80_l2e; +} + +void helper_fldpi_ST0(CPUX86State *env) +{ +    ST0 = floatx80_pi; +} + +void helper_fldlg2_ST0(CPUX86State *env) +{ +    ST0 = floatx80_lg2; +} + +void helper_fldln2_ST0(CPUX86State *env) +{ +    ST0 = floatx80_ln2; +} + +void helper_fldz_ST0(CPUX86State *env) +{ +    ST0 = floatx80_zero; +} + +void helper_fldz_FT0(CPUX86State *env) +{ +    FT0 = floatx80_zero; +} + +uint32_t helper_fnstsw(CPUX86State *env) +{ +    return (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; +} + +uint32_t helper_fnstcw(CPUX86State *env) +{ +    return env->fpuc; +} + +void update_fp_status(CPUX86State *env) +{ +    int rnd_type; + +    /* set rounding mode */ +    switch (env->fpuc & FPU_RC_MASK) { +    default: +    case FPU_RC_NEAR: +        rnd_type = float_round_nearest_even; +        break; +    case FPU_RC_DOWN: +        rnd_type = float_round_down; +        break; +    case FPU_RC_UP: +        rnd_type = float_round_up; +        break; +    case FPU_RC_CHOP: +        rnd_type = float_round_to_zero; +        break; +    } +    set_float_rounding_mode(rnd_type, &env->fp_status); +    switch ((env->fpuc >> 8) & 3) { +    case 0: +        rnd_type = 32; +        break; +    case 2: +        rnd_type = 64; +        break; +    case 3: +    default: +        rnd_type = 80; +        break; +    } +    set_floatx80_rounding_precision(rnd_type, &env->fp_status); +} + +void helper_fldcw(CPUX86State *env, uint32_t val) +{ +    cpu_set_fpuc(env, val); +} + +void helper_fclex(CPUX86State *env) +{ +    env->fpus &= 0x7f00; +} + +void helper_fwait(CPUX86State *env) +{ +    if (env->fpus & FPUS_SE) { +        fpu_raise_exception(env); +    } +} + +void helper_fninit(CPUX86State *env) +{ +    env->fpus = 0; +    env->fpstt = 0; +    cpu_set_fpuc(env, 0x37f); +    env->fptags[0] = 1; +    env->fptags[1] = 1; +    env->fptags[2] = 1; +    env->fptags[3] = 1; +    env->fptags[4] = 1; +    env->fptags[5] = 1; +    env->fptags[6] = 1; +    env->fptags[7] = 1; +} + +/* BCD ops */ + +void helper_fbld_ST0(CPUX86State *env, target_ulong ptr) +{ +    floatx80 tmp; +    uint64_t val; +    unsigned int v; +    int i; + +    val = 0; +    for (i = 8; i >= 0; i--) { +        v = cpu_ldub_data(env, ptr + i); +        val = (val * 100) + ((v >> 4) * 10) + (v & 0xf); +    } +    tmp = int64_to_floatx80(val, &env->fp_status); +    if (cpu_ldub_data(env, ptr + 9) & 0x80) { +        tmp = floatx80_chs(tmp); +    } +    fpush(env); +    ST0 = tmp; +} + +void helper_fbst_ST0(CPUX86State *env, target_ulong ptr) +{ +    int v; +    target_ulong mem_ref, mem_end; +    int64_t val; + +    val = floatx80_to_int64(ST0, &env->fp_status); +    mem_ref = ptr; +    mem_end = mem_ref + 9; +    if (val < 0) { +        cpu_stb_data(env, mem_end, 0x80); +        val = -val; +    } else { +        cpu_stb_data(env, mem_end, 0x00); +    } +    while (mem_ref < mem_end) { +        if (val == 0) { +            break; +        } +        v = val % 100; +        val = val / 100; +        v = ((v / 10) << 4) | (v % 10); +        cpu_stb_data(env, mem_ref++, v); +    } +    while (mem_ref < mem_end) { +        cpu_stb_data(env, mem_ref++, 0); +    } +} + +void helper_f2xm1(CPUX86State *env) +{ +    double val = floatx80_to_double(env, ST0); + +    val = pow(2.0, val) - 1.0; +    ST0 = double_to_floatx80(env, val); +} + +void helper_fyl2x(CPUX86State *env) +{ +    double fptemp = floatx80_to_double(env, ST0); + +    if (fptemp > 0.0) { +        fptemp = log(fptemp) / log(2.0); /* log2(ST) */ +        fptemp *= floatx80_to_double(env, ST1); +        ST1 = double_to_floatx80(env, fptemp); +        fpop(env); +    } else { +        env->fpus &= ~0x4700; +        env->fpus |= 0x400; +    } +} + +void helper_fptan(CPUX86State *env) +{ +    double fptemp = floatx80_to_double(env, ST0); + +    if ((fptemp > MAXTAN) || (fptemp < -MAXTAN)) { +        env->fpus |= 0x400; +    } else { +        fptemp = tan(fptemp); +        ST0 = double_to_floatx80(env, fptemp); +        fpush(env); +        ST0 = floatx80_one; +        env->fpus &= ~0x400; /* C2 <-- 0 */ +        /* the above code is for |arg| < 2**52 only */ +    } +} + +void helper_fpatan(CPUX86State *env) +{ +    double fptemp, fpsrcop; + +    fpsrcop = floatx80_to_double(env, ST1); +    fptemp = floatx80_to_double(env, ST0); +    ST1 = double_to_floatx80(env, atan2(fpsrcop, fptemp)); +    fpop(env); +} + +void helper_fxtract(CPUX86State *env) +{ +    CPU_LDoubleU temp; + +    temp.d = ST0; + +    if (floatx80_is_zero(ST0)) { +        /* Easy way to generate -inf and raising division by 0 exception */ +        ST0 = floatx80_div(floatx80_chs(floatx80_one), floatx80_zero, +                           &env->fp_status); +        fpush(env); +        ST0 = temp.d; +    } else { +        int expdif; + +        expdif = EXPD(temp) - EXPBIAS; +        /* DP exponent bias */ +        ST0 = int32_to_floatx80(expdif, &env->fp_status); +        fpush(env); +        BIASEXPONENT(temp); +        ST0 = temp.d; +    } +} + +void helper_fprem1(CPUX86State *env) +{ +    double st0, st1, dblq, fpsrcop, fptemp; +    CPU_LDoubleU fpsrcop1, fptemp1; +    int expdif; +    signed long long int q; + +    st0 = floatx80_to_double(env, ST0); +    st1 = floatx80_to_double(env, ST1); + +    if (isinf(st0) || isnan(st0) || isnan(st1) || (st1 == 0.0)) { +        ST0 = double_to_floatx80(env, 0.0 / 0.0); /* NaN */ +        env->fpus &= ~0x4700; /* (C3,C2,C1,C0) <-- 0000 */ +        return; +    } + +    fpsrcop = st0; +    fptemp = st1; +    fpsrcop1.d = ST0; +    fptemp1.d = ST1; +    expdif = EXPD(fpsrcop1) - EXPD(fptemp1); + +    if (expdif < 0) { +        /* optimisation? taken from the AMD docs */ +        env->fpus &= ~0x4700; /* (C3,C2,C1,C0) <-- 0000 */ +        /* ST0 is unchanged */ +        return; +    } + +    if (expdif < 53) { +        dblq = fpsrcop / fptemp; +        /* round dblq towards nearest integer */ +        dblq = rint(dblq); +        st0 = fpsrcop - fptemp * dblq; + +        /* convert dblq to q by truncating towards zero */ +        if (dblq < 0.0) { +            q = (signed long long int)(-dblq); +        } else { +            q = (signed long long int)dblq; +        } + +        env->fpus &= ~0x4700; /* (C3,C2,C1,C0) <-- 0000 */ +        /* (C0,C3,C1) <-- (q2,q1,q0) */ +        env->fpus |= (q & 0x4) << (8 - 2);  /* (C0) <-- q2 */ +        env->fpus |= (q & 0x2) << (14 - 1); /* (C3) <-- q1 */ +        env->fpus |= (q & 0x1) << (9 - 0);  /* (C1) <-- q0 */ +    } else { +        env->fpus |= 0x400;  /* C2 <-- 1 */ +        fptemp = pow(2.0, expdif - 50); +        fpsrcop = (st0 / st1) / fptemp; +        /* fpsrcop = integer obtained by chopping */ +        fpsrcop = (fpsrcop < 0.0) ? +                  -(floor(fabs(fpsrcop))) : floor(fpsrcop); +        st0 -= (st1 * fpsrcop * fptemp); +    } +    ST0 = double_to_floatx80(env, st0); +} + +void helper_fprem(CPUX86State *env) +{ +    double st0, st1, dblq, fpsrcop, fptemp; +    CPU_LDoubleU fpsrcop1, fptemp1; +    int expdif; +    signed long long int q; + +    st0 = floatx80_to_double(env, ST0); +    st1 = floatx80_to_double(env, ST1); + +    if (isinf(st0) || isnan(st0) || isnan(st1) || (st1 == 0.0)) { +        ST0 = double_to_floatx80(env, 0.0 / 0.0); /* NaN */ +        env->fpus &= ~0x4700; /* (C3,C2,C1,C0) <-- 0000 */ +        return; +    } + +    fpsrcop = st0; +    fptemp = st1; +    fpsrcop1.d = ST0; +    fptemp1.d = ST1; +    expdif = EXPD(fpsrcop1) - EXPD(fptemp1); + +    if (expdif < 0) { +        /* optimisation? taken from the AMD docs */ +        env->fpus &= ~0x4700; /* (C3,C2,C1,C0) <-- 0000 */ +        /* ST0 is unchanged */ +        return; +    } + +    if (expdif < 53) { +        dblq = fpsrcop / fptemp; /* ST0 / ST1 */ +        /* round dblq towards zero */ +        dblq = (dblq < 0.0) ? ceil(dblq) : floor(dblq); +        st0 = fpsrcop - fptemp * dblq; /* fpsrcop is ST0 */ + +        /* convert dblq to q by truncating towards zero */ +        if (dblq < 0.0) { +            q = (signed long long int)(-dblq); +        } else { +            q = (signed long long int)dblq; +        } + +        env->fpus &= ~0x4700; /* (C3,C2,C1,C0) <-- 0000 */ +        /* (C0,C3,C1) <-- (q2,q1,q0) */ +        env->fpus |= (q & 0x4) << (8 - 2);  /* (C0) <-- q2 */ +        env->fpus |= (q & 0x2) << (14 - 1); /* (C3) <-- q1 */ +        env->fpus |= (q & 0x1) << (9 - 0);  /* (C1) <-- q0 */ +    } else { +        int N = 32 + (expdif % 32); /* as per AMD docs */ + +        env->fpus |= 0x400;  /* C2 <-- 1 */ +        fptemp = pow(2.0, (double)(expdif - N)); +        fpsrcop = (st0 / st1) / fptemp; +        /* fpsrcop = integer obtained by chopping */ +        fpsrcop = (fpsrcop < 0.0) ? +                  -(floor(fabs(fpsrcop))) : floor(fpsrcop); +        st0 -= (st1 * fpsrcop * fptemp); +    } +    ST0 = double_to_floatx80(env, st0); +} + +void helper_fyl2xp1(CPUX86State *env) +{ +    double fptemp = floatx80_to_double(env, ST0); + +    if ((fptemp + 1.0) > 0.0) { +        fptemp = log(fptemp + 1.0) / log(2.0); /* log2(ST + 1.0) */ +        fptemp *= floatx80_to_double(env, ST1); +        ST1 = double_to_floatx80(env, fptemp); +        fpop(env); +    } else { +        env->fpus &= ~0x4700; +        env->fpus |= 0x400; +    } +} + +void helper_fsqrt(CPUX86State *env) +{ +    if (floatx80_is_neg(ST0)) { +        env->fpus &= ~0x4700;  /* (C3,C2,C1,C0) <-- 0000 */ +        env->fpus |= 0x400; +    } +    ST0 = floatx80_sqrt(ST0, &env->fp_status); +} + +void helper_fsincos(CPUX86State *env) +{ +    double fptemp = floatx80_to_double(env, ST0); + +    if ((fptemp > MAXTAN) || (fptemp < -MAXTAN)) { +        env->fpus |= 0x400; +    } else { +        ST0 = double_to_floatx80(env, sin(fptemp)); +        fpush(env); +        ST0 = double_to_floatx80(env, cos(fptemp)); +        env->fpus &= ~0x400;  /* C2 <-- 0 */ +        /* the above code is for |arg| < 2**63 only */ +    } +} + +void helper_frndint(CPUX86State *env) +{ +    ST0 = floatx80_round_to_int(ST0, &env->fp_status); +} + +void helper_fscale(CPUX86State *env) +{ +    if (floatx80_is_any_nan(ST1)) { +        ST0 = ST1; +    } else { +        int n = floatx80_to_int32_round_to_zero(ST1, &env->fp_status); +        ST0 = floatx80_scalbn(ST0, n, &env->fp_status); +    } +} + +void helper_fsin(CPUX86State *env) +{ +    double fptemp = floatx80_to_double(env, ST0); + +    if ((fptemp > MAXTAN) || (fptemp < -MAXTAN)) { +        env->fpus |= 0x400; +    } else { +        ST0 = double_to_floatx80(env, sin(fptemp)); +        env->fpus &= ~0x400;  /* C2 <-- 0 */ +        /* the above code is for |arg| < 2**53 only */ +    } +} + +void helper_fcos(CPUX86State *env) +{ +    double fptemp = floatx80_to_double(env, ST0); + +    if ((fptemp > MAXTAN) || (fptemp < -MAXTAN)) { +        env->fpus |= 0x400; +    } else { +        ST0 = double_to_floatx80(env, cos(fptemp)); +        env->fpus &= ~0x400;  /* C2 <-- 0 */ +        /* the above code is for |arg| < 2**63 only */ +    } +} + +void helper_fxam_ST0(CPUX86State *env) +{ +    CPU_LDoubleU temp; +    int expdif; + +    temp.d = ST0; + +    env->fpus &= ~0x4700; /* (C3,C2,C1,C0) <-- 0000 */ +    if (SIGND(temp)) { +        env->fpus |= 0x200; /* C1 <-- 1 */ +    } + +    /* XXX: test fptags too */ +    expdif = EXPD(temp); +    if (expdif == MAXEXPD) { +        if (MANTD(temp) == 0x8000000000000000ULL) { +            env->fpus |= 0x500; /* Infinity */ +        } else { +            env->fpus |= 0x100; /* NaN */ +        } +    } else if (expdif == 0) { +        if (MANTD(temp) == 0) { +            env->fpus |=  0x4000; /* Zero */ +        } else { +            env->fpus |= 0x4400; /* Denormal */ +        } +    } else { +        env->fpus |= 0x400; +    } +} + +void helper_fstenv(CPUX86State *env, target_ulong ptr, int data32) +{ +    int fpus, fptag, exp, i; +    uint64_t mant; +    CPU_LDoubleU tmp; + +    fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; +    fptag = 0; +    for (i = 7; i >= 0; i--) { +        fptag <<= 2; +        if (env->fptags[i]) { +            fptag |= 3; +        } else { +            tmp.d = env->fpregs[i].d; +            exp = EXPD(tmp); +            mant = MANTD(tmp); +            if (exp == 0 && mant == 0) { +                /* zero */ +                fptag |= 1; +            } else if (exp == 0 || exp == MAXEXPD +                       || (mant & (1LL << 63)) == 0) { +                /* NaNs, infinity, denormal */ +                fptag |= 2; +            } +        } +    } +    if (data32) { +        /* 32 bit */ +        cpu_stl_data(env, ptr, env->fpuc); +        cpu_stl_data(env, ptr + 4, fpus); +        cpu_stl_data(env, ptr + 8, fptag); +        cpu_stl_data(env, ptr + 12, 0); /* fpip */ +        cpu_stl_data(env, ptr + 16, 0); /* fpcs */ +        cpu_stl_data(env, ptr + 20, 0); /* fpoo */ +        cpu_stl_data(env, ptr + 24, 0); /* fpos */ +    } else { +        /* 16 bit */ +        cpu_stw_data(env, ptr, env->fpuc); +        cpu_stw_data(env, ptr + 2, fpus); +        cpu_stw_data(env, ptr + 4, fptag); +        cpu_stw_data(env, ptr + 6, 0); +        cpu_stw_data(env, ptr + 8, 0); +        cpu_stw_data(env, ptr + 10, 0); +        cpu_stw_data(env, ptr + 12, 0); +    } +} + +void helper_fldenv(CPUX86State *env, target_ulong ptr, int data32) +{ +    int i, fpus, fptag; + +    if (data32) { +        cpu_set_fpuc(env, cpu_lduw_data(env, ptr)); +        fpus = cpu_lduw_data(env, ptr + 4); +        fptag = cpu_lduw_data(env, ptr + 8); +    } else { +        cpu_set_fpuc(env, cpu_lduw_data(env, ptr)); +        fpus = cpu_lduw_data(env, ptr + 2); +        fptag = cpu_lduw_data(env, ptr + 4); +    } +    env->fpstt = (fpus >> 11) & 7; +    env->fpus = fpus & ~0x3800; +    for (i = 0; i < 8; i++) { +        env->fptags[i] = ((fptag & 3) == 3); +        fptag >>= 2; +    } +} + +void helper_fsave(CPUX86State *env, target_ulong ptr, int data32) +{ +    floatx80 tmp; +    int i; + +    helper_fstenv(env, ptr, data32); + +    ptr += (14 << data32); +    for (i = 0; i < 8; i++) { +        tmp = ST(i); +        helper_fstt(env, tmp, ptr); +        ptr += 10; +    } + +    /* fninit */ +    env->fpus = 0; +    env->fpstt = 0; +    cpu_set_fpuc(env, 0x37f); +    env->fptags[0] = 1; +    env->fptags[1] = 1; +    env->fptags[2] = 1; +    env->fptags[3] = 1; +    env->fptags[4] = 1; +    env->fptags[5] = 1; +    env->fptags[6] = 1; +    env->fptags[7] = 1; +} + +void helper_frstor(CPUX86State *env, target_ulong ptr, int data32) +{ +    floatx80 tmp; +    int i; + +    helper_fldenv(env, ptr, data32); +    ptr += (14 << data32); + +    for (i = 0; i < 8; i++) { +        tmp = helper_fldt(env, ptr); +        ST(i) = tmp; +        ptr += 10; +    } +} + +#if defined(CONFIG_USER_ONLY) +void cpu_x86_fsave(CPUX86State *env, target_ulong ptr, int data32) +{ +    helper_fsave(env, ptr, data32); +} + +void cpu_x86_frstor(CPUX86State *env, target_ulong ptr, int data32) +{ +    helper_frstor(env, ptr, data32); +} +#endif + +void helper_fxsave(CPUX86State *env, target_ulong ptr, int data64) +{ +    int fpus, fptag, i, nb_xmm_regs; +    floatx80 tmp; +    target_ulong addr; + +    /* The operand must be 16 byte aligned */ +    if (ptr & 0xf) { +        raise_exception(env, EXCP0D_GPF); +    } + +    fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; +    fptag = 0; +    for (i = 0; i < 8; i++) { +        fptag |= (env->fptags[i] << i); +    } +    cpu_stw_data(env, ptr, env->fpuc); +    cpu_stw_data(env, ptr + 2, fpus); +    cpu_stw_data(env, ptr + 4, fptag ^ 0xff); +#ifdef TARGET_X86_64 +    if (data64) { +        cpu_stq_data(env, ptr + 0x08, 0); /* rip */ +        cpu_stq_data(env, ptr + 0x10, 0); /* rdp */ +    } else +#endif +    { +        cpu_stl_data(env, ptr + 0x08, 0); /* eip */ +        cpu_stl_data(env, ptr + 0x0c, 0); /* sel  */ +        cpu_stl_data(env, ptr + 0x10, 0); /* dp */ +        cpu_stl_data(env, ptr + 0x14, 0); /* sel  */ +    } + +    addr = ptr + 0x20; +    for (i = 0; i < 8; i++) { +        tmp = ST(i); +        helper_fstt(env, tmp, addr); +        addr += 16; +    } + +    if (env->cr[4] & CR4_OSFXSR_MASK) { +        /* XXX: finish it */ +        cpu_stl_data(env, ptr + 0x18, env->mxcsr); /* mxcsr */ +        cpu_stl_data(env, ptr + 0x1c, 0x0000ffff); /* mxcsr_mask */ +        if (env->hflags & HF_CS64_MASK) { +            nb_xmm_regs = 16; +        } else { +            nb_xmm_regs = 8; +        } +        addr = ptr + 0xa0; +        /* Fast FXSAVE leaves out the XMM registers */ +        if (!(env->efer & MSR_EFER_FFXSR) +            || (env->hflags & HF_CPL_MASK) +            || !(env->hflags & HF_LMA_MASK)) { +            for (i = 0; i < nb_xmm_regs; i++) { +                cpu_stq_data(env, addr, env->xmm_regs[i].XMM_Q(0)); +                cpu_stq_data(env, addr + 8, env->xmm_regs[i].XMM_Q(1)); +                addr += 16; +            } +        } +    } +} + +void helper_fxrstor(CPUX86State *env, target_ulong ptr, int data64) +{ +    int i, fpus, fptag, nb_xmm_regs; +    floatx80 tmp; +    target_ulong addr; + +    /* The operand must be 16 byte aligned */ +    if (ptr & 0xf) { +        raise_exception(env, EXCP0D_GPF); +    } + +    cpu_set_fpuc(env, cpu_lduw_data(env, ptr)); +    fpus = cpu_lduw_data(env, ptr + 2); +    fptag = cpu_lduw_data(env, ptr + 4); +    env->fpstt = (fpus >> 11) & 7; +    env->fpus = fpus & ~0x3800; +    fptag ^= 0xff; +    for (i = 0; i < 8; i++) { +        env->fptags[i] = ((fptag >> i) & 1); +    } + +    addr = ptr + 0x20; +    for (i = 0; i < 8; i++) { +        tmp = helper_fldt(env, addr); +        ST(i) = tmp; +        addr += 16; +    } + +    if (env->cr[4] & CR4_OSFXSR_MASK) { +        /* XXX: finish it */ +        cpu_set_mxcsr(env, cpu_ldl_data(env, ptr + 0x18)); +        /* cpu_ldl_data(env, ptr + 0x1c); */ +        if (env->hflags & HF_CS64_MASK) { +            nb_xmm_regs = 16; +        } else { +            nb_xmm_regs = 8; +        } +        addr = ptr + 0xa0; +        /* Fast FXRESTORE leaves out the XMM registers */ +        if (!(env->efer & MSR_EFER_FFXSR) +            || (env->hflags & HF_CPL_MASK) +            || !(env->hflags & HF_LMA_MASK)) { +            for (i = 0; i < nb_xmm_regs; i++) { +                env->xmm_regs[i].XMM_Q(0) = cpu_ldq_data(env, addr); +                env->xmm_regs[i].XMM_Q(1) = cpu_ldq_data(env, addr + 8); +                addr += 16; +            } +        } +    } +} + +void cpu_get_fp80(uint64_t *pmant, uint16_t *pexp, floatx80 f) +{ +    CPU_LDoubleU temp; + +    temp.d = f; +    *pmant = temp.l.lower; +    *pexp = temp.l.upper; +} + +floatx80 cpu_set_fp80(uint64_t mant, uint16_t upper) +{ +    CPU_LDoubleU temp; + +    temp.l.upper = upper; +    temp.l.lower = mant; +    return temp.d; +} + +/* MMX/SSE */ +/* XXX: optimize by storing fptt and fptags in the static cpu state */ + +#define SSE_DAZ             0x0040 +#define SSE_RC_MASK         0x6000 +#define SSE_RC_NEAR         0x0000 +#define SSE_RC_DOWN         0x2000 +#define SSE_RC_UP           0x4000 +#define SSE_RC_CHOP         0x6000 +#define SSE_FZ              0x8000 + +void cpu_set_mxcsr(CPUX86State *env, uint32_t mxcsr) +{ +    int rnd_type; + +    env->mxcsr = mxcsr; + +    /* set rounding mode */ +    switch (mxcsr & SSE_RC_MASK) { +    default: +    case SSE_RC_NEAR: +        rnd_type = float_round_nearest_even; +        break; +    case SSE_RC_DOWN: +        rnd_type = float_round_down; +        break; +    case SSE_RC_UP: +        rnd_type = float_round_up; +        break; +    case SSE_RC_CHOP: +        rnd_type = float_round_to_zero; +        break; +    } +    set_float_rounding_mode(rnd_type, &env->sse_status); + +    /* set denormals are zero */ +    set_flush_inputs_to_zero((mxcsr & SSE_DAZ) ? 1 : 0, &env->sse_status); + +    /* set flush to zero */ +    set_flush_to_zero((mxcsr & SSE_FZ) ? 1 : 0, &env->fp_status); +} + +void cpu_set_fpuc(CPUX86State *env, uint16_t val) +{ +    env->fpuc = val; +    update_fp_status(env); +} + +void helper_ldmxcsr(CPUX86State *env, uint32_t val) +{ +    cpu_set_mxcsr(env, val); +} + +void helper_enter_mmx(CPUX86State *env) +{ +    env->fpstt = 0; +    *(uint32_t *)(env->fptags) = 0; +    *(uint32_t *)(env->fptags + 4) = 0; +} + +void helper_emms(CPUX86State *env) +{ +    /* set to empty state */ +    *(uint32_t *)(env->fptags) = 0x01010101; +    *(uint32_t *)(env->fptags + 4) = 0x01010101; +} + +/* XXX: suppress */ +void helper_movq(CPUX86State *env, void *d, void *s) +{ +    *(uint64_t *)d = *(uint64_t *)s; +} + +#define SHIFT 0 +#include "ops_sse.h" + +#define SHIFT 1 +#include "ops_sse.h" diff --git a/target-i386/gdbstub.c b/target-i386/gdbstub.c new file mode 100644 index 00000000..ff99cfb0 --- /dev/null +++ b/target-i386/gdbstub.c @@ -0,0 +1,233 @@ +/* + * x86 gdb server stub + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2013 SUSE LINUX Products GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#include "config.h" +#include "qemu-common.h" +#include "exec/gdbstub.h" + +#ifdef TARGET_X86_64 +static const int gpr_map[16] = { +    R_EAX, R_EBX, R_ECX, R_EDX, R_ESI, R_EDI, R_EBP, R_ESP, +    8, 9, 10, 11, 12, 13, 14, 15 +}; +#else +#define gpr_map gpr_map32 +#endif +static const int gpr_map32[8] = { 0, 1, 2, 3, 4, 5, 6, 7 }; + +#define IDX_IP_REG      CPU_NB_REGS +#define IDX_FLAGS_REG   (IDX_IP_REG + 1) +#define IDX_SEG_REGS    (IDX_FLAGS_REG + 1) +#define IDX_FP_REGS     (IDX_SEG_REGS + 6) +#define IDX_XMM_REGS    (IDX_FP_REGS + 16) +#define IDX_MXCSR_REG   (IDX_XMM_REGS + CPU_NB_REGS) + +int x86_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +{ +    X86CPU *cpu = X86_CPU(cs); +    CPUX86State *env = &cpu->env; + +    if (n < CPU_NB_REGS) { +        if (TARGET_LONG_BITS == 64 && env->hflags & HF_CS64_MASK) { +            return gdb_get_reg64(mem_buf, env->regs[gpr_map[n]]); +        } else if (n < CPU_NB_REGS32) { +            return gdb_get_reg32(mem_buf, env->regs[gpr_map32[n]]); +        } +    } else if (n >= IDX_FP_REGS && n < IDX_FP_REGS + 8) { +#ifdef USE_X86LDOUBLE +        /* FIXME: byteswap float values - after fixing fpregs layout. */ +        memcpy(mem_buf, &env->fpregs[n - IDX_FP_REGS], 10); +#else +        memset(mem_buf, 0, 10); +#endif +        return 10; +    } else if (n >= IDX_XMM_REGS && n < IDX_XMM_REGS + CPU_NB_REGS) { +        n -= IDX_XMM_REGS; +        if (n < CPU_NB_REGS32 || +            (TARGET_LONG_BITS == 64 && env->hflags & HF_CS64_MASK)) { +            stq_p(mem_buf, env->xmm_regs[n].XMM_Q(0)); +            stq_p(mem_buf + 8, env->xmm_regs[n].XMM_Q(1)); +            return 16; +        } +    } else { +        switch (n) { +        case IDX_IP_REG: +            if (TARGET_LONG_BITS == 64 && env->hflags & HF_CS64_MASK) { +                return gdb_get_reg64(mem_buf, env->eip); +            } else { +                return gdb_get_reg32(mem_buf, env->eip); +            } +        case IDX_FLAGS_REG: +            return gdb_get_reg32(mem_buf, env->eflags); + +        case IDX_SEG_REGS: +            return gdb_get_reg32(mem_buf, env->segs[R_CS].selector); +        case IDX_SEG_REGS + 1: +            return gdb_get_reg32(mem_buf, env->segs[R_SS].selector); +        case IDX_SEG_REGS + 2: +            return gdb_get_reg32(mem_buf, env->segs[R_DS].selector); +        case IDX_SEG_REGS + 3: +            return gdb_get_reg32(mem_buf, env->segs[R_ES].selector); +        case IDX_SEG_REGS + 4: +            return gdb_get_reg32(mem_buf, env->segs[R_FS].selector); +        case IDX_SEG_REGS + 5: +            return gdb_get_reg32(mem_buf, env->segs[R_GS].selector); + +        case IDX_FP_REGS + 8: +            return gdb_get_reg32(mem_buf, env->fpuc); +        case IDX_FP_REGS + 9: +            return gdb_get_reg32(mem_buf, (env->fpus & ~0x3800) | +                                          (env->fpstt & 0x7) << 11); +        case IDX_FP_REGS + 10: +            return gdb_get_reg32(mem_buf, 0); /* ftag */ +        case IDX_FP_REGS + 11: +            return gdb_get_reg32(mem_buf, 0); /* fiseg */ +        case IDX_FP_REGS + 12: +            return gdb_get_reg32(mem_buf, 0); /* fioff */ +        case IDX_FP_REGS + 13: +            return gdb_get_reg32(mem_buf, 0); /* foseg */ +        case IDX_FP_REGS + 14: +            return gdb_get_reg32(mem_buf, 0); /* fooff */ +        case IDX_FP_REGS + 15: +            return gdb_get_reg32(mem_buf, 0); /* fop */ + +        case IDX_MXCSR_REG: +            return gdb_get_reg32(mem_buf, env->mxcsr); +        } +    } +    return 0; +} + +static int x86_cpu_gdb_load_seg(X86CPU *cpu, int sreg, uint8_t *mem_buf) +{ +    CPUX86State *env = &cpu->env; +    uint16_t selector = ldl_p(mem_buf); + +    if (selector != env->segs[sreg].selector) { +#if defined(CONFIG_USER_ONLY) +        cpu_x86_load_seg(env, sreg, selector); +#else +        unsigned int limit, flags; +        target_ulong base; + +        if (!(env->cr[0] & CR0_PE_MASK) || (env->eflags & VM_MASK)) { +            int dpl = (env->eflags & VM_MASK) ? 3 : 0; +            base = selector << 4; +            limit = 0xffff; +            flags = DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | +                    DESC_A_MASK | (dpl << DESC_DPL_SHIFT); +        } else { +            if (!cpu_x86_get_descr_debug(env, selector, &base, &limit, +                                         &flags)) { +                return 4; +            } +        } +        cpu_x86_load_seg_cache(env, sreg, selector, base, limit, flags); +#endif +    } +    return 4; +} + +int x86_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ +    X86CPU *cpu = X86_CPU(cs); +    CPUX86State *env = &cpu->env; +    uint32_t tmp; + +    if (n < CPU_NB_REGS) { +        if (TARGET_LONG_BITS == 64 && env->hflags & HF_CS64_MASK) { +            env->regs[gpr_map[n]] = ldtul_p(mem_buf); +            return sizeof(target_ulong); +        } else if (n < CPU_NB_REGS32) { +            n = gpr_map32[n]; +            env->regs[n] &= ~0xffffffffUL; +            env->regs[n] |= (uint32_t)ldl_p(mem_buf); +            return 4; +        } +    } else if (n >= IDX_FP_REGS && n < IDX_FP_REGS + 8) { +#ifdef USE_X86LDOUBLE +        /* FIXME: byteswap float values - after fixing fpregs layout. */ +        memcpy(&env->fpregs[n - IDX_FP_REGS], mem_buf, 10); +#endif +        return 10; +    } else if (n >= IDX_XMM_REGS && n < IDX_XMM_REGS + CPU_NB_REGS) { +        n -= IDX_XMM_REGS; +        if (n < CPU_NB_REGS32 || +            (TARGET_LONG_BITS == 64 && env->hflags & HF_CS64_MASK)) { +            env->xmm_regs[n].XMM_Q(0) = ldq_p(mem_buf); +            env->xmm_regs[n].XMM_Q(1) = ldq_p(mem_buf + 8); +            return 16; +        } +    } else { +        switch (n) { +        case IDX_IP_REG: +            if (TARGET_LONG_BITS == 64 && env->hflags & HF_CS64_MASK) { +                env->eip = ldq_p(mem_buf); +                return 8; +            } else { +                env->eip &= ~0xffffffffUL; +                env->eip |= (uint32_t)ldl_p(mem_buf); +                return 4; +            } +        case IDX_FLAGS_REG: +            env->eflags = ldl_p(mem_buf); +            return 4; + +        case IDX_SEG_REGS: +            return x86_cpu_gdb_load_seg(cpu, R_CS, mem_buf); +        case IDX_SEG_REGS + 1: +            return x86_cpu_gdb_load_seg(cpu, R_SS, mem_buf); +        case IDX_SEG_REGS + 2: +            return x86_cpu_gdb_load_seg(cpu, R_DS, mem_buf); +        case IDX_SEG_REGS + 3: +            return x86_cpu_gdb_load_seg(cpu, R_ES, mem_buf); +        case IDX_SEG_REGS + 4: +            return x86_cpu_gdb_load_seg(cpu, R_FS, mem_buf); +        case IDX_SEG_REGS + 5: +            return x86_cpu_gdb_load_seg(cpu, R_GS, mem_buf); + +        case IDX_FP_REGS + 8: +            cpu_set_fpuc(env, ldl_p(mem_buf)); +            return 4; +        case IDX_FP_REGS + 9: +            tmp = ldl_p(mem_buf); +            env->fpstt = (tmp >> 11) & 7; +            env->fpus = tmp & ~0x3800; +            return 4; +        case IDX_FP_REGS + 10: /* ftag */ +            return 4; +        case IDX_FP_REGS + 11: /* fiseg */ +            return 4; +        case IDX_FP_REGS + 12: /* fioff */ +            return 4; +        case IDX_FP_REGS + 13: /* foseg */ +            return 4; +        case IDX_FP_REGS + 14: /* fooff */ +            return 4; +        case IDX_FP_REGS + 15: /* fop */ +            return 4; + +        case IDX_MXCSR_REG: +            cpu_set_mxcsr(env, ldl_p(mem_buf)); +            return 4; +        } +    } +    /* Unrecognised register.  */ +    return 0; +} diff --git a/target-i386/helper.c b/target-i386/helper.c new file mode 100644 index 00000000..5480a96a --- /dev/null +++ b/target-i386/helper.c @@ -0,0 +1,1371 @@ +/* + *  i386 helpers (without register variable usage) + * + *  Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "cpu.h" +#include "sysemu/kvm.h" +#include "kvm_i386.h" +#ifndef CONFIG_USER_ONLY +#include "sysemu/sysemu.h" +#include "monitor/monitor.h" +#endif + +static void cpu_x86_version(CPUX86State *env, int *family, int *model) +{ +    int cpuver = env->cpuid_version; + +    if (family == NULL || model == NULL) { +        return; +    } + +    *family = (cpuver >> 8) & 0x0f; +    *model = ((cpuver >> 12) & 0xf0) + ((cpuver >> 4) & 0x0f); +} + +/* Broadcast MCA signal for processor version 06H_EH and above */ +int cpu_x86_support_mca_broadcast(CPUX86State *env) +{ +    int family = 0; +    int model = 0; + +    cpu_x86_version(env, &family, &model); +    if ((family == 6 && model >= 14) || family > 6) { +        return 1; +    } + +    return 0; +} + +/***********************************************************/ +/* x86 debug */ + +static const char *cc_op_str[CC_OP_NB] = { +    "DYNAMIC", +    "EFLAGS", + +    "MULB", +    "MULW", +    "MULL", +    "MULQ", + +    "ADDB", +    "ADDW", +    "ADDL", +    "ADDQ", + +    "ADCB", +    "ADCW", +    "ADCL", +    "ADCQ", + +    "SUBB", +    "SUBW", +    "SUBL", +    "SUBQ", + +    "SBBB", +    "SBBW", +    "SBBL", +    "SBBQ", + +    "LOGICB", +    "LOGICW", +    "LOGICL", +    "LOGICQ", + +    "INCB", +    "INCW", +    "INCL", +    "INCQ", + +    "DECB", +    "DECW", +    "DECL", +    "DECQ", + +    "SHLB", +    "SHLW", +    "SHLL", +    "SHLQ", + +    "SARB", +    "SARW", +    "SARL", +    "SARQ", + +    "BMILGB", +    "BMILGW", +    "BMILGL", +    "BMILGQ", + +    "ADCX", +    "ADOX", +    "ADCOX", + +    "CLR", +}; + +static void +cpu_x86_dump_seg_cache(CPUX86State *env, FILE *f, fprintf_function cpu_fprintf, +                       const char *name, struct SegmentCache *sc) +{ +#ifdef TARGET_X86_64 +    if (env->hflags & HF_CS64_MASK) { +        cpu_fprintf(f, "%-3s=%04x %016" PRIx64 " %08x %08x", name, +                    sc->selector, sc->base, sc->limit, sc->flags & 0x00ffff00); +    } else +#endif +    { +        cpu_fprintf(f, "%-3s=%04x %08x %08x %08x", name, sc->selector, +                    (uint32_t)sc->base, sc->limit, sc->flags & 0x00ffff00); +    } + +    if (!(env->hflags & HF_PE_MASK) || !(sc->flags & DESC_P_MASK)) +        goto done; + +    cpu_fprintf(f, " DPL=%d ", (sc->flags & DESC_DPL_MASK) >> DESC_DPL_SHIFT); +    if (sc->flags & DESC_S_MASK) { +        if (sc->flags & DESC_CS_MASK) { +            cpu_fprintf(f, (sc->flags & DESC_L_MASK) ? "CS64" : +                           ((sc->flags & DESC_B_MASK) ? "CS32" : "CS16")); +            cpu_fprintf(f, " [%c%c", (sc->flags & DESC_C_MASK) ? 'C' : '-', +                        (sc->flags & DESC_R_MASK) ? 'R' : '-'); +        } else { +            cpu_fprintf(f, +                        (sc->flags & DESC_B_MASK || env->hflags & HF_LMA_MASK) +                        ? "DS  " : "DS16"); +            cpu_fprintf(f, " [%c%c", (sc->flags & DESC_E_MASK) ? 'E' : '-', +                        (sc->flags & DESC_W_MASK) ? 'W' : '-'); +        } +        cpu_fprintf(f, "%c]", (sc->flags & DESC_A_MASK) ? 'A' : '-'); +    } else { +        static const char *sys_type_name[2][16] = { +            { /* 32 bit mode */ +                "Reserved", "TSS16-avl", "LDT", "TSS16-busy", +                "CallGate16", "TaskGate", "IntGate16", "TrapGate16", +                "Reserved", "TSS32-avl", "Reserved", "TSS32-busy", +                "CallGate32", "Reserved", "IntGate32", "TrapGate32" +            }, +            { /* 64 bit mode */ +                "<hiword>", "Reserved", "LDT", "Reserved", "Reserved", +                "Reserved", "Reserved", "Reserved", "Reserved", +                "TSS64-avl", "Reserved", "TSS64-busy", "CallGate64", +                "Reserved", "IntGate64", "TrapGate64" +            } +        }; +        cpu_fprintf(f, "%s", +                    sys_type_name[(env->hflags & HF_LMA_MASK) ? 1 : 0] +                                 [(sc->flags & DESC_TYPE_MASK) +                                  >> DESC_TYPE_SHIFT]); +    } +done: +    cpu_fprintf(f, "\n"); +} + +#define DUMP_CODE_BYTES_TOTAL    50 +#define DUMP_CODE_BYTES_BACKWARD 20 + +void x86_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, +                        int flags) +{ +    X86CPU *cpu = X86_CPU(cs); +    CPUX86State *env = &cpu->env; +    int eflags, i, nb; +    char cc_op_name[32]; +    static const char *seg_name[6] = { "ES", "CS", "SS", "DS", "FS", "GS" }; + +    eflags = cpu_compute_eflags(env); +#ifdef TARGET_X86_64 +    if (env->hflags & HF_CS64_MASK) { +        cpu_fprintf(f, +                    "RAX=%016" PRIx64 " RBX=%016" PRIx64 " RCX=%016" PRIx64 " RDX=%016" PRIx64 "\n" +                    "RSI=%016" PRIx64 " RDI=%016" PRIx64 " RBP=%016" PRIx64 " RSP=%016" PRIx64 "\n" +                    "R8 =%016" PRIx64 " R9 =%016" PRIx64 " R10=%016" PRIx64 " R11=%016" PRIx64 "\n" +                    "R12=%016" PRIx64 " R13=%016" PRIx64 " R14=%016" PRIx64 " R15=%016" PRIx64 "\n" +                    "RIP=%016" PRIx64 " RFL=%08x [%c%c%c%c%c%c%c] CPL=%d II=%d A20=%d SMM=%d HLT=%d\n", +                    env->regs[R_EAX], +                    env->regs[R_EBX], +                    env->regs[R_ECX], +                    env->regs[R_EDX], +                    env->regs[R_ESI], +                    env->regs[R_EDI], +                    env->regs[R_EBP], +                    env->regs[R_ESP], +                    env->regs[8], +                    env->regs[9], +                    env->regs[10], +                    env->regs[11], +                    env->regs[12], +                    env->regs[13], +                    env->regs[14], +                    env->regs[15], +                    env->eip, eflags, +                    eflags & DF_MASK ? 'D' : '-', +                    eflags & CC_O ? 'O' : '-', +                    eflags & CC_S ? 'S' : '-', +                    eflags & CC_Z ? 'Z' : '-', +                    eflags & CC_A ? 'A' : '-', +                    eflags & CC_P ? 'P' : '-', +                    eflags & CC_C ? 'C' : '-', +                    env->hflags & HF_CPL_MASK, +                    (env->hflags >> HF_INHIBIT_IRQ_SHIFT) & 1, +                    (env->a20_mask >> 20) & 1, +                    (env->hflags >> HF_SMM_SHIFT) & 1, +                    cs->halted); +    } else +#endif +    { +        cpu_fprintf(f, "EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n" +                    "ESI=%08x EDI=%08x EBP=%08x ESP=%08x\n" +                    "EIP=%08x EFL=%08x [%c%c%c%c%c%c%c] CPL=%d II=%d A20=%d SMM=%d HLT=%d\n", +                    (uint32_t)env->regs[R_EAX], +                    (uint32_t)env->regs[R_EBX], +                    (uint32_t)env->regs[R_ECX], +                    (uint32_t)env->regs[R_EDX], +                    (uint32_t)env->regs[R_ESI], +                    (uint32_t)env->regs[R_EDI], +                    (uint32_t)env->regs[R_EBP], +                    (uint32_t)env->regs[R_ESP], +                    (uint32_t)env->eip, eflags, +                    eflags & DF_MASK ? 'D' : '-', +                    eflags & CC_O ? 'O' : '-', +                    eflags & CC_S ? 'S' : '-', +                    eflags & CC_Z ? 'Z' : '-', +                    eflags & CC_A ? 'A' : '-', +                    eflags & CC_P ? 'P' : '-', +                    eflags & CC_C ? 'C' : '-', +                    env->hflags & HF_CPL_MASK, +                    (env->hflags >> HF_INHIBIT_IRQ_SHIFT) & 1, +                    (env->a20_mask >> 20) & 1, +                    (env->hflags >> HF_SMM_SHIFT) & 1, +                    cs->halted); +    } + +    for(i = 0; i < 6; i++) { +        cpu_x86_dump_seg_cache(env, f, cpu_fprintf, seg_name[i], +                               &env->segs[i]); +    } +    cpu_x86_dump_seg_cache(env, f, cpu_fprintf, "LDT", &env->ldt); +    cpu_x86_dump_seg_cache(env, f, cpu_fprintf, "TR", &env->tr); + +#ifdef TARGET_X86_64 +    if (env->hflags & HF_LMA_MASK) { +        cpu_fprintf(f, "GDT=     %016" PRIx64 " %08x\n", +                    env->gdt.base, env->gdt.limit); +        cpu_fprintf(f, "IDT=     %016" PRIx64 " %08x\n", +                    env->idt.base, env->idt.limit); +        cpu_fprintf(f, "CR0=%08x CR2=%016" PRIx64 " CR3=%016" PRIx64 " CR4=%08x\n", +                    (uint32_t)env->cr[0], +                    env->cr[2], +                    env->cr[3], +                    (uint32_t)env->cr[4]); +        for(i = 0; i < 4; i++) +            cpu_fprintf(f, "DR%d=%016" PRIx64 " ", i, env->dr[i]); +        cpu_fprintf(f, "\nDR6=%016" PRIx64 " DR7=%016" PRIx64 "\n", +                    env->dr[6], env->dr[7]); +    } else +#endif +    { +        cpu_fprintf(f, "GDT=     %08x %08x\n", +                    (uint32_t)env->gdt.base, env->gdt.limit); +        cpu_fprintf(f, "IDT=     %08x %08x\n", +                    (uint32_t)env->idt.base, env->idt.limit); +        cpu_fprintf(f, "CR0=%08x CR2=%08x CR3=%08x CR4=%08x\n", +                    (uint32_t)env->cr[0], +                    (uint32_t)env->cr[2], +                    (uint32_t)env->cr[3], +                    (uint32_t)env->cr[4]); +        for(i = 0; i < 4; i++) { +            cpu_fprintf(f, "DR%d=" TARGET_FMT_lx " ", i, env->dr[i]); +        } +        cpu_fprintf(f, "\nDR6=" TARGET_FMT_lx " DR7=" TARGET_FMT_lx "\n", +                    env->dr[6], env->dr[7]); +    } +    if (flags & CPU_DUMP_CCOP) { +        if ((unsigned)env->cc_op < CC_OP_NB) +            snprintf(cc_op_name, sizeof(cc_op_name), "%s", cc_op_str[env->cc_op]); +        else +            snprintf(cc_op_name, sizeof(cc_op_name), "[%d]", env->cc_op); +#ifdef TARGET_X86_64 +        if (env->hflags & HF_CS64_MASK) { +            cpu_fprintf(f, "CCS=%016" PRIx64 " CCD=%016" PRIx64 " CCO=%-8s\n", +                        env->cc_src, env->cc_dst, +                        cc_op_name); +        } else +#endif +        { +            cpu_fprintf(f, "CCS=%08x CCD=%08x CCO=%-8s\n", +                        (uint32_t)env->cc_src, (uint32_t)env->cc_dst, +                        cc_op_name); +        } +    } +    cpu_fprintf(f, "EFER=%016" PRIx64 "\n", env->efer); +    if (flags & CPU_DUMP_FPU) { +        int fptag; +        fptag = 0; +        for(i = 0; i < 8; i++) { +            fptag |= ((!env->fptags[i]) << i); +        } +        cpu_fprintf(f, "FCW=%04x FSW=%04x [ST=%d] FTW=%02x MXCSR=%08x\n", +                    env->fpuc, +                    (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11, +                    env->fpstt, +                    fptag, +                    env->mxcsr); +        for(i=0;i<8;i++) { +            CPU_LDoubleU u; +            u.d = env->fpregs[i].d; +            cpu_fprintf(f, "FPR%d=%016" PRIx64 " %04x", +                        i, u.l.lower, u.l.upper); +            if ((i & 1) == 1) +                cpu_fprintf(f, "\n"); +            else +                cpu_fprintf(f, " "); +        } +        if (env->hflags & HF_CS64_MASK) +            nb = 16; +        else +            nb = 8; +        for(i=0;i<nb;i++) { +            cpu_fprintf(f, "XMM%02d=%08x%08x%08x%08x", +                        i, +                        env->xmm_regs[i].XMM_L(3), +                        env->xmm_regs[i].XMM_L(2), +                        env->xmm_regs[i].XMM_L(1), +                        env->xmm_regs[i].XMM_L(0)); +            if ((i & 1) == 1) +                cpu_fprintf(f, "\n"); +            else +                cpu_fprintf(f, " "); +        } +    } +    if (flags & CPU_DUMP_CODE) { +        target_ulong base = env->segs[R_CS].base + env->eip; +        target_ulong offs = MIN(env->eip, DUMP_CODE_BYTES_BACKWARD); +        uint8_t code; +        char codestr[3]; + +        cpu_fprintf(f, "Code="); +        for (i = 0; i < DUMP_CODE_BYTES_TOTAL; i++) { +            if (cpu_memory_rw_debug(cs, base - offs + i, &code, 1, 0) == 0) { +                snprintf(codestr, sizeof(codestr), "%02x", code); +            } else { +                snprintf(codestr, sizeof(codestr), "??"); +            } +            cpu_fprintf(f, "%s%s%s%s", i > 0 ? " " : "", +                        i == offs ? "<" : "", codestr, i == offs ? ">" : ""); +        } +        cpu_fprintf(f, "\n"); +    } +} + +/***********************************************************/ +/* x86 mmu */ +/* XXX: add PGE support */ + +void x86_cpu_set_a20(X86CPU *cpu, int a20_state) +{ +    CPUX86State *env = &cpu->env; + +    a20_state = (a20_state != 0); +    if (a20_state != ((env->a20_mask >> 20) & 1)) { +        CPUState *cs = CPU(cpu); + +        qemu_log_mask(CPU_LOG_MMU, "A20 update: a20=%d\n", a20_state); +        /* if the cpu is currently executing code, we must unlink it and +           all the potentially executing TB */ +        cpu_interrupt(cs, CPU_INTERRUPT_EXITTB); + +        /* when a20 is changed, all the MMU mappings are invalid, so +           we must flush everything */ +        tlb_flush(cs, 1); +        env->a20_mask = ~(1 << 20) | (a20_state << 20); +    } +} + +void cpu_x86_update_cr0(CPUX86State *env, uint32_t new_cr0) +{ +    X86CPU *cpu = x86_env_get_cpu(env); +    int pe_state; + +    qemu_log_mask(CPU_LOG_MMU, "CR0 update: CR0=0x%08x\n", new_cr0); +    if ((new_cr0 & (CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK)) != +        (env->cr[0] & (CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK))) { +        tlb_flush(CPU(cpu), 1); +    } + +#ifdef TARGET_X86_64 +    if (!(env->cr[0] & CR0_PG_MASK) && (new_cr0 & CR0_PG_MASK) && +        (env->efer & MSR_EFER_LME)) { +        /* enter in long mode */ +        /* XXX: generate an exception */ +        if (!(env->cr[4] & CR4_PAE_MASK)) +            return; +        env->efer |= MSR_EFER_LMA; +        env->hflags |= HF_LMA_MASK; +    } else if ((env->cr[0] & CR0_PG_MASK) && !(new_cr0 & CR0_PG_MASK) && +               (env->efer & MSR_EFER_LMA)) { +        /* exit long mode */ +        env->efer &= ~MSR_EFER_LMA; +        env->hflags &= ~(HF_LMA_MASK | HF_CS64_MASK); +        env->eip &= 0xffffffff; +    } +#endif +    env->cr[0] = new_cr0 | CR0_ET_MASK; + +    /* update PE flag in hidden flags */ +    pe_state = (env->cr[0] & CR0_PE_MASK); +    env->hflags = (env->hflags & ~HF_PE_MASK) | (pe_state << HF_PE_SHIFT); +    /* ensure that ADDSEG is always set in real mode */ +    env->hflags |= ((pe_state ^ 1) << HF_ADDSEG_SHIFT); +    /* update FPU flags */ +    env->hflags = (env->hflags & ~(HF_MP_MASK | HF_EM_MASK | HF_TS_MASK)) | +        ((new_cr0 << (HF_MP_SHIFT - 1)) & (HF_MP_MASK | HF_EM_MASK | HF_TS_MASK)); +} + +/* XXX: in legacy PAE mode, generate a GPF if reserved bits are set in +   the PDPT */ +void cpu_x86_update_cr3(CPUX86State *env, target_ulong new_cr3) +{ +    X86CPU *cpu = x86_env_get_cpu(env); + +    env->cr[3] = new_cr3; +    if (env->cr[0] & CR0_PG_MASK) { +        qemu_log_mask(CPU_LOG_MMU, +                        "CR3 update: CR3=" TARGET_FMT_lx "\n", new_cr3); +        tlb_flush(CPU(cpu), 0); +    } +} + +void cpu_x86_update_cr4(CPUX86State *env, uint32_t new_cr4) +{ +    X86CPU *cpu = x86_env_get_cpu(env); + +#if defined(DEBUG_MMU) +    printf("CR4 update: CR4=%08x\n", (uint32_t)env->cr[4]); +#endif +    if ((new_cr4 ^ env->cr[4]) & +        (CR4_PGE_MASK | CR4_PAE_MASK | CR4_PSE_MASK | +         CR4_SMEP_MASK | CR4_SMAP_MASK)) { +        tlb_flush(CPU(cpu), 1); +    } +    /* SSE handling */ +    if (!(env->features[FEAT_1_EDX] & CPUID_SSE)) { +        new_cr4 &= ~CR4_OSFXSR_MASK; +    } +    env->hflags &= ~HF_OSFXSR_MASK; +    if (new_cr4 & CR4_OSFXSR_MASK) { +        env->hflags |= HF_OSFXSR_MASK; +    } + +    if (!(env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_SMAP)) { +        new_cr4 &= ~CR4_SMAP_MASK; +    } +    env->hflags &= ~HF_SMAP_MASK; +    if (new_cr4 & CR4_SMAP_MASK) { +        env->hflags |= HF_SMAP_MASK; +    } + +    env->cr[4] = new_cr4; +} + +#if defined(CONFIG_USER_ONLY) + +int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, +                             int is_write, int mmu_idx) +{ +    X86CPU *cpu = X86_CPU(cs); +    CPUX86State *env = &cpu->env; + +    /* user mode only emulation */ +    is_write &= 1; +    env->cr[2] = addr; +    env->error_code = (is_write << PG_ERROR_W_BIT); +    env->error_code |= PG_ERROR_U_MASK; +    cs->exception_index = EXCP0E_PAGE; +    return 1; +} + +#else + +/* return value: + * -1 = cannot handle fault + * 0  = nothing more to do + * 1  = generate PF fault + */ +int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, +                             int is_write1, int mmu_idx) +{ +    X86CPU *cpu = X86_CPU(cs); +    CPUX86State *env = &cpu->env; +    uint64_t ptep, pte; +    target_ulong pde_addr, pte_addr; +    int error_code = 0; +    int is_dirty, prot, page_size, is_write, is_user; +    hwaddr paddr; +    uint64_t rsvd_mask = PG_HI_RSVD_MASK; +    uint32_t page_offset; +    target_ulong vaddr; + +    is_user = mmu_idx == MMU_USER_IDX; +#if defined(DEBUG_MMU) +    printf("MMU fault: addr=%" VADDR_PRIx " w=%d u=%d eip=" TARGET_FMT_lx "\n", +           addr, is_write1, is_user, env->eip); +#endif +    is_write = is_write1 & 1; + +    if (!(env->cr[0] & CR0_PG_MASK)) { +        pte = addr; +#ifdef TARGET_X86_64 +        if (!(env->hflags & HF_LMA_MASK)) { +            /* Without long mode we can only address 32bits in real mode */ +            pte = (uint32_t)pte; +        } +#endif +        prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; +        page_size = 4096; +        goto do_mapping; +    } + +    if (!(env->efer & MSR_EFER_NXE)) { +        rsvd_mask |= PG_NX_MASK; +    } + +    if (env->cr[4] & CR4_PAE_MASK) { +        uint64_t pde, pdpe; +        target_ulong pdpe_addr; + +#ifdef TARGET_X86_64 +        if (env->hflags & HF_LMA_MASK) { +            uint64_t pml4e_addr, pml4e; +            int32_t sext; + +            /* test virtual address sign extension */ +            sext = (int64_t)addr >> 47; +            if (sext != 0 && sext != -1) { +                env->error_code = 0; +                cs->exception_index = EXCP0D_GPF; +                return 1; +            } + +            pml4e_addr = ((env->cr[3] & ~0xfff) + (((addr >> 39) & 0x1ff) << 3)) & +                env->a20_mask; +            pml4e = x86_ldq_phys(cs, pml4e_addr); +            if (!(pml4e & PG_PRESENT_MASK)) { +                goto do_fault; +            } +            if (pml4e & (rsvd_mask | PG_PSE_MASK)) { +                goto do_fault_rsvd; +            } +            if (!(pml4e & PG_ACCESSED_MASK)) { +                pml4e |= PG_ACCESSED_MASK; +                x86_stl_phys_notdirty(cs, pml4e_addr, pml4e); +            } +            ptep = pml4e ^ PG_NX_MASK; +            pdpe_addr = ((pml4e & PG_ADDRESS_MASK) + (((addr >> 30) & 0x1ff) << 3)) & +                env->a20_mask; +            pdpe = x86_ldq_phys(cs, pdpe_addr); +            if (!(pdpe & PG_PRESENT_MASK)) { +                goto do_fault; +            } +            if (pdpe & rsvd_mask) { +                goto do_fault_rsvd; +            } +            ptep &= pdpe ^ PG_NX_MASK; +            if (!(pdpe & PG_ACCESSED_MASK)) { +                pdpe |= PG_ACCESSED_MASK; +                x86_stl_phys_notdirty(cs, pdpe_addr, pdpe); +            } +            if (pdpe & PG_PSE_MASK) { +                /* 1 GB page */ +                page_size = 1024 * 1024 * 1024; +                pte_addr = pdpe_addr; +                pte = pdpe; +                goto do_check_protect; +            } +        } else +#endif +        { +            /* XXX: load them when cr3 is loaded ? */ +            pdpe_addr = ((env->cr[3] & ~0x1f) + ((addr >> 27) & 0x18)) & +                env->a20_mask; +            pdpe = x86_ldq_phys(cs, pdpe_addr); +            if (!(pdpe & PG_PRESENT_MASK)) { +                goto do_fault; +            } +            rsvd_mask |= PG_HI_USER_MASK; +            if (pdpe & (rsvd_mask | PG_NX_MASK)) { +                goto do_fault_rsvd; +            } +            ptep = PG_NX_MASK | PG_USER_MASK | PG_RW_MASK; +        } + +        pde_addr = ((pdpe & PG_ADDRESS_MASK) + (((addr >> 21) & 0x1ff) << 3)) & +            env->a20_mask; +        pde = x86_ldq_phys(cs, pde_addr); +        if (!(pde & PG_PRESENT_MASK)) { +            goto do_fault; +        } +        if (pde & rsvd_mask) { +            goto do_fault_rsvd; +        } +        ptep &= pde ^ PG_NX_MASK; +        if (pde & PG_PSE_MASK) { +            /* 2 MB page */ +            page_size = 2048 * 1024; +            pte_addr = pde_addr; +            pte = pde; +            goto do_check_protect; +        } +        /* 4 KB page */ +        if (!(pde & PG_ACCESSED_MASK)) { +            pde |= PG_ACCESSED_MASK; +            x86_stl_phys_notdirty(cs, pde_addr, pde); +        } +        pte_addr = ((pde & PG_ADDRESS_MASK) + (((addr >> 12) & 0x1ff) << 3)) & +            env->a20_mask; +        pte = x86_ldq_phys(cs, pte_addr); +        if (!(pte & PG_PRESENT_MASK)) { +            goto do_fault; +        } +        if (pte & rsvd_mask) { +            goto do_fault_rsvd; +        } +        /* combine pde and pte nx, user and rw protections */ +        ptep &= pte ^ PG_NX_MASK; +        page_size = 4096; +    } else { +        uint32_t pde; + +        /* page directory entry */ +        pde_addr = ((env->cr[3] & ~0xfff) + ((addr >> 20) & 0xffc)) & +            env->a20_mask; +        pde = x86_ldl_phys(cs, pde_addr); +        if (!(pde & PG_PRESENT_MASK)) { +            goto do_fault; +        } +        ptep = pde | PG_NX_MASK; + +        /* if PSE bit is set, then we use a 4MB page */ +        if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) { +            page_size = 4096 * 1024; +            pte_addr = pde_addr; + +            /* Bits 20-13 provide bits 39-32 of the address, bit 21 is reserved. +             * Leave bits 20-13 in place for setting accessed/dirty bits below. +             */ +            pte = pde | ((pde & 0x1fe000) << (32 - 13)); +            rsvd_mask = 0x200000; +            goto do_check_protect_pse36; +        } + +        if (!(pde & PG_ACCESSED_MASK)) { +            pde |= PG_ACCESSED_MASK; +            x86_stl_phys_notdirty(cs, pde_addr, pde); +        } + +        /* page directory entry */ +        pte_addr = ((pde & ~0xfff) + ((addr >> 10) & 0xffc)) & +            env->a20_mask; +        pte = x86_ldl_phys(cs, pte_addr); +        if (!(pte & PG_PRESENT_MASK)) { +            goto do_fault; +        } +        /* combine pde and pte user and rw protections */ +        ptep &= pte | PG_NX_MASK; +        page_size = 4096; +        rsvd_mask = 0; +    } + +do_check_protect: +    rsvd_mask |= (page_size - 1) & PG_ADDRESS_MASK & ~PG_PSE_PAT_MASK; +do_check_protect_pse36: +    if (pte & rsvd_mask) { +        goto do_fault_rsvd; +    } +    ptep ^= PG_NX_MASK; +    if ((ptep & PG_NX_MASK) && is_write1 == 2) { +        goto do_fault_protect; +    } +    switch (mmu_idx) { +    case MMU_USER_IDX: +        if (!(ptep & PG_USER_MASK)) { +            goto do_fault_protect; +        } +        if (is_write && !(ptep & PG_RW_MASK)) { +            goto do_fault_protect; +        } +        break; + +    case MMU_KSMAP_IDX: +        if (is_write1 != 2 && (ptep & PG_USER_MASK)) { +            goto do_fault_protect; +        } +        /* fall through */ +    case MMU_KNOSMAP_IDX: +        if (is_write1 == 2 && (env->cr[4] & CR4_SMEP_MASK) && +            (ptep & PG_USER_MASK)) { +            goto do_fault_protect; +        } +        if ((env->cr[0] & CR0_WP_MASK) && +            is_write && !(ptep & PG_RW_MASK)) { +            goto do_fault_protect; +        } +        break; + +    default: /* cannot happen */ +        break; +    } +    is_dirty = is_write && !(pte & PG_DIRTY_MASK); +    if (!(pte & PG_ACCESSED_MASK) || is_dirty) { +        pte |= PG_ACCESSED_MASK; +        if (is_dirty) { +            pte |= PG_DIRTY_MASK; +        } +        x86_stl_phys_notdirty(cs, pte_addr, pte); +    } + +    /* the page can be put in the TLB */ +    prot = PAGE_READ; +    if (!(ptep & PG_NX_MASK) && +        (mmu_idx == MMU_USER_IDX || +         !((env->cr[4] & CR4_SMEP_MASK) && (ptep & PG_USER_MASK)))) { +        prot |= PAGE_EXEC; +    } +    if (pte & PG_DIRTY_MASK) { +        /* only set write access if already dirty... otherwise wait +           for dirty access */ +        if (is_user) { +            if (ptep & PG_RW_MASK) +                prot |= PAGE_WRITE; +        } else { +            if (!(env->cr[0] & CR0_WP_MASK) || +                (ptep & PG_RW_MASK)) +                prot |= PAGE_WRITE; +        } +    } + do_mapping: +    pte = pte & env->a20_mask; + +    /* align to page_size */ +    pte &= PG_ADDRESS_MASK & ~(page_size - 1); + +    /* Even if 4MB pages, we map only one 4KB page in the cache to +       avoid filling it too fast */ +    vaddr = addr & TARGET_PAGE_MASK; +    page_offset = vaddr & (page_size - 1); +    paddr = pte + page_offset; + +    tlb_set_page_with_attrs(cs, vaddr, paddr, cpu_get_mem_attrs(env), +                            prot, mmu_idx, page_size); +    return 0; + do_fault_rsvd: +    error_code |= PG_ERROR_RSVD_MASK; + do_fault_protect: +    error_code |= PG_ERROR_P_MASK; + do_fault: +    error_code |= (is_write << PG_ERROR_W_BIT); +    if (is_user) +        error_code |= PG_ERROR_U_MASK; +    if (is_write1 == 2 && +        (((env->efer & MSR_EFER_NXE) && +          (env->cr[4] & CR4_PAE_MASK)) || +         (env->cr[4] & CR4_SMEP_MASK))) +        error_code |= PG_ERROR_I_D_MASK; +    if (env->intercept_exceptions & (1 << EXCP0E_PAGE)) { +        /* cr2 is not modified in case of exceptions */ +        x86_stq_phys(cs, +                 env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), +                 addr); +    } else { +        env->cr[2] = addr; +    } +    env->error_code = error_code; +    cs->exception_index = EXCP0E_PAGE; +    return 1; +} + +hwaddr x86_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) +{ +    X86CPU *cpu = X86_CPU(cs); +    CPUX86State *env = &cpu->env; +    target_ulong pde_addr, pte_addr; +    uint64_t pte; +    uint32_t page_offset; +    int page_size; + +    if (!(env->cr[0] & CR0_PG_MASK)) { +        pte = addr & env->a20_mask; +        page_size = 4096; +    } else if (env->cr[4] & CR4_PAE_MASK) { +        target_ulong pdpe_addr; +        uint64_t pde, pdpe; + +#ifdef TARGET_X86_64 +        if (env->hflags & HF_LMA_MASK) { +            uint64_t pml4e_addr, pml4e; +            int32_t sext; + +            /* test virtual address sign extension */ +            sext = (int64_t)addr >> 47; +            if (sext != 0 && sext != -1) { +                return -1; +            } +            pml4e_addr = ((env->cr[3] & ~0xfff) + (((addr >> 39) & 0x1ff) << 3)) & +                env->a20_mask; +            pml4e = x86_ldq_phys(cs, pml4e_addr); +            if (!(pml4e & PG_PRESENT_MASK)) { +                return -1; +            } +            pdpe_addr = ((pml4e & PG_ADDRESS_MASK) + +                         (((addr >> 30) & 0x1ff) << 3)) & env->a20_mask; +            pdpe = x86_ldq_phys(cs, pdpe_addr); +            if (!(pdpe & PG_PRESENT_MASK)) { +                return -1; +            } +            if (pdpe & PG_PSE_MASK) { +                page_size = 1024 * 1024 * 1024; +                pte = pdpe; +                goto out; +            } + +        } else +#endif +        { +            pdpe_addr = ((env->cr[3] & ~0x1f) + ((addr >> 27) & 0x18)) & +                env->a20_mask; +            pdpe = x86_ldq_phys(cs, pdpe_addr); +            if (!(pdpe & PG_PRESENT_MASK)) +                return -1; +        } + +        pde_addr = ((pdpe & PG_ADDRESS_MASK) + +                    (((addr >> 21) & 0x1ff) << 3)) & env->a20_mask; +        pde = x86_ldq_phys(cs, pde_addr); +        if (!(pde & PG_PRESENT_MASK)) { +            return -1; +        } +        if (pde & PG_PSE_MASK) { +            /* 2 MB page */ +            page_size = 2048 * 1024; +            pte = pde; +        } else { +            /* 4 KB page */ +            pte_addr = ((pde & PG_ADDRESS_MASK) + +                        (((addr >> 12) & 0x1ff) << 3)) & env->a20_mask; +            page_size = 4096; +            pte = x86_ldq_phys(cs, pte_addr); +        } +        if (!(pte & PG_PRESENT_MASK)) { +            return -1; +        } +    } else { +        uint32_t pde; + +        /* page directory entry */ +        pde_addr = ((env->cr[3] & ~0xfff) + ((addr >> 20) & 0xffc)) & env->a20_mask; +        pde = x86_ldl_phys(cs, pde_addr); +        if (!(pde & PG_PRESENT_MASK)) +            return -1; +        if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) { +            pte = pde | ((pde & 0x1fe000) << (32 - 13)); +            page_size = 4096 * 1024; +        } else { +            /* page directory entry */ +            pte_addr = ((pde & ~0xfff) + ((addr >> 10) & 0xffc)) & env->a20_mask; +            pte = x86_ldl_phys(cs, pte_addr); +            if (!(pte & PG_PRESENT_MASK)) { +                return -1; +            } +            page_size = 4096; +        } +        pte = pte & env->a20_mask; +    } + +#ifdef TARGET_X86_64 +out: +#endif +    pte &= PG_ADDRESS_MASK & ~(page_size - 1); +    page_offset = (addr & TARGET_PAGE_MASK) & (page_size - 1); +    return pte | page_offset; +} + +void hw_breakpoint_insert(CPUX86State *env, int index) +{ +    CPUState *cs = CPU(x86_env_get_cpu(env)); +    int type = 0, err = 0; + +    switch (hw_breakpoint_type(env->dr[7], index)) { +    case DR7_TYPE_BP_INST: +        if (hw_breakpoint_enabled(env->dr[7], index)) { +            err = cpu_breakpoint_insert(cs, env->dr[index], BP_CPU, +                                        &env->cpu_breakpoint[index]); +        } +        break; +    case DR7_TYPE_DATA_WR: +        type = BP_CPU | BP_MEM_WRITE; +        break; +    case DR7_TYPE_IO_RW: +        /* No support for I/O watchpoints yet */ +        break; +    case DR7_TYPE_DATA_RW: +        type = BP_CPU | BP_MEM_ACCESS; +        break; +    } + +    if (type != 0) { +        err = cpu_watchpoint_insert(cs, env->dr[index], +                                    hw_breakpoint_len(env->dr[7], index), +                                    type, &env->cpu_watchpoint[index]); +    } + +    if (err) { +        env->cpu_breakpoint[index] = NULL; +    } +} + +void hw_breakpoint_remove(CPUX86State *env, int index) +{ +    CPUState *cs; + +    if (!env->cpu_breakpoint[index]) { +        return; +    } +    cs = CPU(x86_env_get_cpu(env)); +    switch (hw_breakpoint_type(env->dr[7], index)) { +    case DR7_TYPE_BP_INST: +        if (hw_breakpoint_enabled(env->dr[7], index)) { +            cpu_breakpoint_remove_by_ref(cs, env->cpu_breakpoint[index]); +        } +        break; +    case DR7_TYPE_DATA_WR: +    case DR7_TYPE_DATA_RW: +        cpu_watchpoint_remove_by_ref(cs, env->cpu_watchpoint[index]); +        break; +    case DR7_TYPE_IO_RW: +        /* No support for I/O watchpoints yet */ +        break; +    } +} + +bool check_hw_breakpoints(CPUX86State *env, bool force_dr6_update) +{ +    target_ulong dr6; +    int reg; +    bool hit_enabled = false; + +    dr6 = env->dr[6] & ~0xf; +    for (reg = 0; reg < DR7_MAX_BP; reg++) { +        bool bp_match = false; +        bool wp_match = false; + +        switch (hw_breakpoint_type(env->dr[7], reg)) { +        case DR7_TYPE_BP_INST: +            if (env->dr[reg] == env->eip) { +                bp_match = true; +            } +            break; +        case DR7_TYPE_DATA_WR: +        case DR7_TYPE_DATA_RW: +            if (env->cpu_watchpoint[reg] && +                env->cpu_watchpoint[reg]->flags & BP_WATCHPOINT_HIT) { +                wp_match = true; +            } +            break; +        case DR7_TYPE_IO_RW: +            break; +        } +        if (bp_match || wp_match) { +            dr6 |= 1 << reg; +            if (hw_breakpoint_enabled(env->dr[7], reg)) { +                hit_enabled = true; +            } +        } +    } + +    if (hit_enabled || force_dr6_update) { +        env->dr[6] = dr6; +    } + +    return hit_enabled; +} + +void breakpoint_handler(CPUState *cs) +{ +    X86CPU *cpu = X86_CPU(cs); +    CPUX86State *env = &cpu->env; +    CPUBreakpoint *bp; + +    if (cs->watchpoint_hit) { +        if (cs->watchpoint_hit->flags & BP_CPU) { +            cs->watchpoint_hit = NULL; +            if (check_hw_breakpoints(env, false)) { +                raise_exception(env, EXCP01_DB); +            } else { +                cpu_resume_from_signal(cs, NULL); +            } +        } +    } else { +        QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { +            if (bp->pc == env->eip) { +                if (bp->flags & BP_CPU) { +                    check_hw_breakpoints(env, true); +                    raise_exception(env, EXCP01_DB); +                } +                break; +            } +        } +    } +} + +typedef struct MCEInjectionParams { +    Monitor *mon; +    X86CPU *cpu; +    int bank; +    uint64_t status; +    uint64_t mcg_status; +    uint64_t addr; +    uint64_t misc; +    int flags; +} MCEInjectionParams; + +static void do_inject_x86_mce(void *data) +{ +    MCEInjectionParams *params = data; +    CPUX86State *cenv = ¶ms->cpu->env; +    CPUState *cpu = CPU(params->cpu); +    uint64_t *banks = cenv->mce_banks + 4 * params->bank; + +    cpu_synchronize_state(cpu); + +    /* +     * If there is an MCE exception being processed, ignore this SRAO MCE +     * unless unconditional injection was requested. +     */ +    if (!(params->flags & MCE_INJECT_UNCOND_AO) +        && !(params->status & MCI_STATUS_AR) +        && (cenv->mcg_status & MCG_STATUS_MCIP)) { +        return; +    } + +    if (params->status & MCI_STATUS_UC) { +        /* +         * if MSR_MCG_CTL is not all 1s, the uncorrected error +         * reporting is disabled +         */ +        if ((cenv->mcg_cap & MCG_CTL_P) && cenv->mcg_ctl != ~(uint64_t)0) { +            monitor_printf(params->mon, +                           "CPU %d: Uncorrected error reporting disabled\n", +                           cpu->cpu_index); +            return; +        } + +        /* +         * if MSR_MCi_CTL is not all 1s, the uncorrected error +         * reporting is disabled for the bank +         */ +        if (banks[0] != ~(uint64_t)0) { +            monitor_printf(params->mon, +                           "CPU %d: Uncorrected error reporting disabled for" +                           " bank %d\n", +                           cpu->cpu_index, params->bank); +            return; +        } + +        if ((cenv->mcg_status & MCG_STATUS_MCIP) || +            !(cenv->cr[4] & CR4_MCE_MASK)) { +            monitor_printf(params->mon, +                           "CPU %d: Previous MCE still in progress, raising" +                           " triple fault\n", +                           cpu->cpu_index); +            qemu_log_mask(CPU_LOG_RESET, "Triple fault\n"); +            qemu_system_reset_request(); +            return; +        } +        if (banks[1] & MCI_STATUS_VAL) { +            params->status |= MCI_STATUS_OVER; +        } +        banks[2] = params->addr; +        banks[3] = params->misc; +        cenv->mcg_status = params->mcg_status; +        banks[1] = params->status; +        cpu_interrupt(cpu, CPU_INTERRUPT_MCE); +    } else if (!(banks[1] & MCI_STATUS_VAL) +               || !(banks[1] & MCI_STATUS_UC)) { +        if (banks[1] & MCI_STATUS_VAL) { +            params->status |= MCI_STATUS_OVER; +        } +        banks[2] = params->addr; +        banks[3] = params->misc; +        banks[1] = params->status; +    } else { +        banks[1] |= MCI_STATUS_OVER; +    } +} + +void cpu_x86_inject_mce(Monitor *mon, X86CPU *cpu, int bank, +                        uint64_t status, uint64_t mcg_status, uint64_t addr, +                        uint64_t misc, int flags) +{ +    CPUState *cs = CPU(cpu); +    CPUX86State *cenv = &cpu->env; +    MCEInjectionParams params = { +        .mon = mon, +        .cpu = cpu, +        .bank = bank, +        .status = status, +        .mcg_status = mcg_status, +        .addr = addr, +        .misc = misc, +        .flags = flags, +    }; +    unsigned bank_num = cenv->mcg_cap & 0xff; + +    if (!cenv->mcg_cap) { +        monitor_printf(mon, "MCE injection not supported\n"); +        return; +    } +    if (bank >= bank_num) { +        monitor_printf(mon, "Invalid MCE bank number\n"); +        return; +    } +    if (!(status & MCI_STATUS_VAL)) { +        monitor_printf(mon, "Invalid MCE status code\n"); +        return; +    } +    if ((flags & MCE_INJECT_BROADCAST) +        && !cpu_x86_support_mca_broadcast(cenv)) { +        monitor_printf(mon, "Guest CPU does not support MCA broadcast\n"); +        return; +    } + +    run_on_cpu(cs, do_inject_x86_mce, ¶ms); +    if (flags & MCE_INJECT_BROADCAST) { +        CPUState *other_cs; + +        params.bank = 1; +        params.status = MCI_STATUS_VAL | MCI_STATUS_UC; +        params.mcg_status = MCG_STATUS_MCIP | MCG_STATUS_RIPV; +        params.addr = 0; +        params.misc = 0; +        CPU_FOREACH(other_cs) { +            if (other_cs == cs) { +                continue; +            } +            params.cpu = X86_CPU(other_cs); +            run_on_cpu(other_cs, do_inject_x86_mce, ¶ms); +        } +    } +} + +void cpu_report_tpr_access(CPUX86State *env, TPRAccess access) +{ +    X86CPU *cpu = x86_env_get_cpu(env); +    CPUState *cs = CPU(cpu); + +    if (kvm_enabled()) { +        env->tpr_access_type = access; + +        cpu_interrupt(cs, CPU_INTERRUPT_TPR); +    } else { +        cpu_restore_state(cs, cs->mem_io_pc); + +        apic_handle_tpr_access_report(cpu->apic_state, env->eip, access); +    } +} +#endif /* !CONFIG_USER_ONLY */ + +int cpu_x86_get_descr_debug(CPUX86State *env, unsigned int selector, +                            target_ulong *base, unsigned int *limit, +                            unsigned int *flags) +{ +    X86CPU *cpu = x86_env_get_cpu(env); +    CPUState *cs = CPU(cpu); +    SegmentCache *dt; +    target_ulong ptr; +    uint32_t e1, e2; +    int index; + +    if (selector & 0x4) +        dt = &env->ldt; +    else +        dt = &env->gdt; +    index = selector & ~7; +    ptr = dt->base + index; +    if ((index + 7) > dt->limit +        || cpu_memory_rw_debug(cs, ptr, (uint8_t *)&e1, sizeof(e1), 0) != 0 +        || cpu_memory_rw_debug(cs, ptr+4, (uint8_t *)&e2, sizeof(e2), 0) != 0) +        return 0; + +    *base = ((e1 >> 16) | ((e2 & 0xff) << 16) | (e2 & 0xff000000)); +    *limit = (e1 & 0xffff) | (e2 & 0x000f0000); +    if (e2 & DESC_G_MASK) +        *limit = (*limit << 12) | 0xfff; +    *flags = e2; + +    return 1; +} + +#if !defined(CONFIG_USER_ONLY) +void do_cpu_init(X86CPU *cpu) +{ +    CPUState *cs = CPU(cpu); +    CPUX86State *env = &cpu->env; +    CPUX86State *save = g_new(CPUX86State, 1); +    int sipi = cs->interrupt_request & CPU_INTERRUPT_SIPI; + +    *save = *env; + +    cpu_reset(cs); +    cs->interrupt_request = sipi; +    memcpy(&env->start_init_save, &save->start_init_save, +           offsetof(CPUX86State, end_init_save) - +           offsetof(CPUX86State, start_init_save)); +    g_free(save); + +    if (kvm_enabled()) { +        kvm_arch_do_init_vcpu(cpu); +    } +    apic_init_reset(cpu->apic_state); +} + +void do_cpu_sipi(X86CPU *cpu) +{ +    apic_sipi(cpu->apic_state); +} +#else +void do_cpu_init(X86CPU *cpu) +{ +} +void do_cpu_sipi(X86CPU *cpu) +{ +} +#endif + +/* Frob eflags into and out of the CPU temporary format.  */ + +void x86_cpu_exec_enter(CPUState *cs) +{ +    X86CPU *cpu = X86_CPU(cs); +    CPUX86State *env = &cpu->env; + +    CC_SRC = env->eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); +    env->df = 1 - (2 * ((env->eflags >> 10) & 1)); +    CC_OP = CC_OP_EFLAGS; +    env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); +} + +void x86_cpu_exec_exit(CPUState *cs) +{ +    X86CPU *cpu = X86_CPU(cs); +    CPUX86State *env = &cpu->env; + +    env->eflags = cpu_compute_eflags(env); +} + +#ifndef CONFIG_USER_ONLY +uint8_t x86_ldub_phys(CPUState *cs, hwaddr addr) +{ +    X86CPU *cpu = X86_CPU(cs); +    CPUX86State *env = &cpu->env; + +    return address_space_ldub(cs->as, addr, +                              cpu_get_mem_attrs(env), +                              NULL); +} + +uint32_t x86_lduw_phys(CPUState *cs, hwaddr addr) +{ +    X86CPU *cpu = X86_CPU(cs); +    CPUX86State *env = &cpu->env; + +    return address_space_lduw(cs->as, addr, +                              cpu_get_mem_attrs(env), +                              NULL); +} + +uint32_t x86_ldl_phys(CPUState *cs, hwaddr addr) +{ +    X86CPU *cpu = X86_CPU(cs); +    CPUX86State *env = &cpu->env; + +    return address_space_ldl(cs->as, addr, +                             cpu_get_mem_attrs(env), +                             NULL); +} + +uint64_t x86_ldq_phys(CPUState *cs, hwaddr addr) +{ +    X86CPU *cpu = X86_CPU(cs); +    CPUX86State *env = &cpu->env; + +    return address_space_ldq(cs->as, addr, +                             cpu_get_mem_attrs(env), +                             NULL); +} + +void x86_stb_phys(CPUState *cs, hwaddr addr, uint8_t val) +{ +    X86CPU *cpu = X86_CPU(cs); +    CPUX86State *env = &cpu->env; + +    address_space_stb(cs->as, addr, val, +                      cpu_get_mem_attrs(env), +                      NULL); +} + +void x86_stl_phys_notdirty(CPUState *cs, hwaddr addr, uint32_t val) +{ +    X86CPU *cpu = X86_CPU(cs); +    CPUX86State *env = &cpu->env; + +    address_space_stl_notdirty(cs->as, addr, val, +                               cpu_get_mem_attrs(env), +                               NULL); +} + +void x86_stw_phys(CPUState *cs, hwaddr addr, uint32_t val) +{ +    X86CPU *cpu = X86_CPU(cs); +    CPUX86State *env = &cpu->env; + +    address_space_stw(cs->as, addr, val, +                      cpu_get_mem_attrs(env), +                      NULL); +} + +void x86_stl_phys(CPUState *cs, hwaddr addr, uint32_t val) +{ +    X86CPU *cpu = X86_CPU(cs); +    CPUX86State *env = &cpu->env; + +    address_space_stl(cs->as, addr, val, +                      cpu_get_mem_attrs(env), +                      NULL); +} + +void x86_stq_phys(CPUState *cs, hwaddr addr, uint64_t val) +{ +    X86CPU *cpu = X86_CPU(cs); +    CPUX86State *env = &cpu->env; + +    address_space_stq(cs->as, addr, val, +                      cpu_get_mem_attrs(env), +                      NULL); +} +#endif diff --git a/target-i386/helper.h b/target-i386/helper.h new file mode 100644 index 00000000..74308f44 --- /dev/null +++ b/target-i386/helper.h @@ -0,0 +1,219 @@ +DEF_HELPER_FLAGS_4(cc_compute_all, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl, int) +DEF_HELPER_FLAGS_4(cc_compute_c, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl, int) + +DEF_HELPER_0(lock, void) +DEF_HELPER_0(unlock, void) +DEF_HELPER_3(write_eflags, void, env, tl, i32) +DEF_HELPER_1(read_eflags, tl, env) +DEF_HELPER_2(divb_AL, void, env, tl) +DEF_HELPER_2(idivb_AL, void, env, tl) +DEF_HELPER_2(divw_AX, void, env, tl) +DEF_HELPER_2(idivw_AX, void, env, tl) +DEF_HELPER_2(divl_EAX, void, env, tl) +DEF_HELPER_2(idivl_EAX, void, env, tl) +#ifdef TARGET_X86_64 +DEF_HELPER_2(divq_EAX, void, env, tl) +DEF_HELPER_2(idivq_EAX, void, env, tl) +#endif + +DEF_HELPER_2(aam, void, env, int) +DEF_HELPER_2(aad, void, env, int) +DEF_HELPER_1(aaa, void, env) +DEF_HELPER_1(aas, void, env) +DEF_HELPER_1(daa, void, env) +DEF_HELPER_1(das, void, env) + +DEF_HELPER_2(lsl, tl, env, tl) +DEF_HELPER_2(lar, tl, env, tl) +DEF_HELPER_2(verr, void, env, tl) +DEF_HELPER_2(verw, void, env, tl) +DEF_HELPER_2(lldt, void, env, int) +DEF_HELPER_2(ltr, void, env, int) +DEF_HELPER_3(load_seg, void, env, int, int) +DEF_HELPER_4(ljmp_protected, void, env, int, tl, int) +DEF_HELPER_5(lcall_real, void, env, int, tl, int, int) +DEF_HELPER_5(lcall_protected, void, env, int, tl, int, int) +DEF_HELPER_2(iret_real, void, env, int) +DEF_HELPER_3(iret_protected, void, env, int, int) +DEF_HELPER_3(lret_protected, void, env, int, int) +DEF_HELPER_2(read_crN, tl, env, int) +DEF_HELPER_3(write_crN, void, env, int, tl) +DEF_HELPER_2(lmsw, void, env, tl) +DEF_HELPER_1(clts, void, env) +DEF_HELPER_3(movl_drN_T0, void, env, int, tl) +DEF_HELPER_2(invlpg, void, env, tl) + +DEF_HELPER_4(enter_level, void, env, int, int, tl) +#ifdef TARGET_X86_64 +DEF_HELPER_4(enter64_level, void, env, int, int, tl) +#endif +DEF_HELPER_1(sysenter, void, env) +DEF_HELPER_2(sysexit, void, env, int) +#ifdef TARGET_X86_64 +DEF_HELPER_2(syscall, void, env, int) +DEF_HELPER_2(sysret, void, env, int) +#endif +DEF_HELPER_2(hlt, void, env, int) +DEF_HELPER_2(monitor, void, env, tl) +DEF_HELPER_2(mwait, void, env, int) +DEF_HELPER_2(pause, void, env, int) +DEF_HELPER_1(debug, void, env) +DEF_HELPER_1(reset_rf, void, env) +DEF_HELPER_3(raise_interrupt, void, env, int, int) +DEF_HELPER_2(raise_exception, void, env, int) +DEF_HELPER_1(cli, void, env) +DEF_HELPER_1(sti, void, env) +DEF_HELPER_1(clac, void, env) +DEF_HELPER_1(stac, void, env) +DEF_HELPER_1(set_inhibit_irq, void, env) +DEF_HELPER_1(reset_inhibit_irq, void, env) +DEF_HELPER_3(boundw, void, env, tl, int) +DEF_HELPER_3(boundl, void, env, tl, int) +DEF_HELPER_1(rsm, void, env) +DEF_HELPER_2(into, void, env, int) +DEF_HELPER_2(cmpxchg8b, void, env, tl) +#ifdef TARGET_X86_64 +DEF_HELPER_2(cmpxchg16b, void, env, tl) +#endif +DEF_HELPER_1(single_step, void, env) +DEF_HELPER_1(cpuid, void, env) +DEF_HELPER_1(rdtsc, void, env) +DEF_HELPER_1(rdtscp, void, env) +DEF_HELPER_1(rdpmc, void, env) +DEF_HELPER_1(rdmsr, void, env) +DEF_HELPER_1(wrmsr, void, env) + +DEF_HELPER_2(check_iob, void, env, i32) +DEF_HELPER_2(check_iow, void, env, i32) +DEF_HELPER_2(check_iol, void, env, i32) +DEF_HELPER_3(outb, void, env, i32, i32) +DEF_HELPER_2(inb, tl, env, i32) +DEF_HELPER_3(outw, void, env, i32, i32) +DEF_HELPER_2(inw, tl, env, i32) +DEF_HELPER_3(outl, void, env, i32, i32) +DEF_HELPER_2(inl, tl, env, i32) + +DEF_HELPER_3(svm_check_intercept_param, void, env, i32, i64) +DEF_HELPER_3(vmexit, void, env, i32, i64) +DEF_HELPER_4(svm_check_io, void, env, i32, i32, i32) +DEF_HELPER_3(vmrun, void, env, int, int) +DEF_HELPER_1(vmmcall, void, env) +DEF_HELPER_2(vmload, void, env, int) +DEF_HELPER_2(vmsave, void, env, int) +DEF_HELPER_1(stgi, void, env) +DEF_HELPER_1(clgi, void, env) +DEF_HELPER_1(skinit, void, env) +DEF_HELPER_2(invlpga, void, env, int) + +/* x86 FPU */ + +DEF_HELPER_2(flds_FT0, void, env, i32) +DEF_HELPER_2(fldl_FT0, void, env, i64) +DEF_HELPER_2(fildl_FT0, void, env, s32) +DEF_HELPER_2(flds_ST0, void, env, i32) +DEF_HELPER_2(fldl_ST0, void, env, i64) +DEF_HELPER_2(fildl_ST0, void, env, s32) +DEF_HELPER_2(fildll_ST0, void, env, s64) +DEF_HELPER_1(fsts_ST0, i32, env) +DEF_HELPER_1(fstl_ST0, i64, env) +DEF_HELPER_1(fist_ST0, s32, env) +DEF_HELPER_1(fistl_ST0, s32, env) +DEF_HELPER_1(fistll_ST0, s64, env) +DEF_HELPER_1(fistt_ST0, s32, env) +DEF_HELPER_1(fisttl_ST0, s32, env) +DEF_HELPER_1(fisttll_ST0, s64, env) +DEF_HELPER_2(fldt_ST0, void, env, tl) +DEF_HELPER_2(fstt_ST0, void, env, tl) +DEF_HELPER_1(fpush, void, env) +DEF_HELPER_1(fpop, void, env) +DEF_HELPER_1(fdecstp, void, env) +DEF_HELPER_1(fincstp, void, env) +DEF_HELPER_2(ffree_STN, void, env, int) +DEF_HELPER_1(fmov_ST0_FT0, void, env) +DEF_HELPER_2(fmov_FT0_STN, void, env, int) +DEF_HELPER_2(fmov_ST0_STN, void, env, int) +DEF_HELPER_2(fmov_STN_ST0, void, env, int) +DEF_HELPER_2(fxchg_ST0_STN, void, env, int) +DEF_HELPER_1(fcom_ST0_FT0, void, env) +DEF_HELPER_1(fucom_ST0_FT0, void, env) +DEF_HELPER_1(fcomi_ST0_FT0, void, env) +DEF_HELPER_1(fucomi_ST0_FT0, void, env) +DEF_HELPER_1(fadd_ST0_FT0, void, env) +DEF_HELPER_1(fmul_ST0_FT0, void, env) +DEF_HELPER_1(fsub_ST0_FT0, void, env) +DEF_HELPER_1(fsubr_ST0_FT0, void, env) +DEF_HELPER_1(fdiv_ST0_FT0, void, env) +DEF_HELPER_1(fdivr_ST0_FT0, void, env) +DEF_HELPER_2(fadd_STN_ST0, void, env, int) +DEF_HELPER_2(fmul_STN_ST0, void, env, int) +DEF_HELPER_2(fsub_STN_ST0, void, env, int) +DEF_HELPER_2(fsubr_STN_ST0, void, env, int) +DEF_HELPER_2(fdiv_STN_ST0, void, env, int) +DEF_HELPER_2(fdivr_STN_ST0, void, env, int) +DEF_HELPER_1(fchs_ST0, void, env) +DEF_HELPER_1(fabs_ST0, void, env) +DEF_HELPER_1(fxam_ST0, void, env) +DEF_HELPER_1(fld1_ST0, void, env) +DEF_HELPER_1(fldl2t_ST0, void, env) +DEF_HELPER_1(fldl2e_ST0, void, env) +DEF_HELPER_1(fldpi_ST0, void, env) +DEF_HELPER_1(fldlg2_ST0, void, env) +DEF_HELPER_1(fldln2_ST0, void, env) +DEF_HELPER_1(fldz_ST0, void, env) +DEF_HELPER_1(fldz_FT0, void, env) +DEF_HELPER_1(fnstsw, i32, env) +DEF_HELPER_1(fnstcw, i32, env) +DEF_HELPER_2(fldcw, void, env, i32) +DEF_HELPER_1(fclex, void, env) +DEF_HELPER_1(fwait, void, env) +DEF_HELPER_1(fninit, void, env) +DEF_HELPER_2(fbld_ST0, void, env, tl) +DEF_HELPER_2(fbst_ST0, void, env, tl) +DEF_HELPER_1(f2xm1, void, env) +DEF_HELPER_1(fyl2x, void, env) +DEF_HELPER_1(fptan, void, env) +DEF_HELPER_1(fpatan, void, env) +DEF_HELPER_1(fxtract, void, env) +DEF_HELPER_1(fprem1, void, env) +DEF_HELPER_1(fprem, void, env) +DEF_HELPER_1(fyl2xp1, void, env) +DEF_HELPER_1(fsqrt, void, env) +DEF_HELPER_1(fsincos, void, env) +DEF_HELPER_1(frndint, void, env) +DEF_HELPER_1(fscale, void, env) +DEF_HELPER_1(fsin, void, env) +DEF_HELPER_1(fcos, void, env) +DEF_HELPER_3(fstenv, void, env, tl, int) +DEF_HELPER_3(fldenv, void, env, tl, int) +DEF_HELPER_3(fsave, void, env, tl, int) +DEF_HELPER_3(frstor, void, env, tl, int) +DEF_HELPER_3(fxsave, void, env, tl, int) +DEF_HELPER_3(fxrstor, void, env, tl, int) + +DEF_HELPER_FLAGS_1(clz, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(ctz, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_2(pdep, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(pext, TCG_CALL_NO_RWG_SE, tl, tl, tl) + +/* MMX/SSE */ + +DEF_HELPER_2(ldmxcsr, void, env, i32) +DEF_HELPER_1(enter_mmx, void, env) +DEF_HELPER_1(emms, void, env) +DEF_HELPER_3(movq, void, env, ptr, ptr) + +#define SHIFT 0 +#include "ops_sse_header.h" +#define SHIFT 1 +#include "ops_sse_header.h" + +DEF_HELPER_3(rclb, tl, env, tl, tl) +DEF_HELPER_3(rclw, tl, env, tl, tl) +DEF_HELPER_3(rcll, tl, env, tl, tl) +DEF_HELPER_3(rcrb, tl, env, tl, tl) +DEF_HELPER_3(rcrw, tl, env, tl, tl) +DEF_HELPER_3(rcrl, tl, env, tl, tl) +#ifdef TARGET_X86_64 +DEF_HELPER_3(rclq, tl, env, tl, tl) +DEF_HELPER_3(rcrq, tl, env, tl, tl) +#endif diff --git a/target-i386/int_helper.c b/target-i386/int_helper.c new file mode 100644 index 00000000..b0d78e6e --- /dev/null +++ b/target-i386/int_helper.c @@ -0,0 +1,471 @@ +/* + *  x86 integer helpers + * + *  Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "cpu.h" +#include "qemu/host-utils.h" +#include "exec/helper-proto.h" + +//#define DEBUG_MULDIV + +/* modulo 9 table */ +static const uint8_t rclb_table[32] = { +    0, 1, 2, 3, 4, 5, 6, 7, +    8, 0, 1, 2, 3, 4, 5, 6, +    7, 8, 0, 1, 2, 3, 4, 5, +    6, 7, 8, 0, 1, 2, 3, 4, +}; + +/* modulo 17 table */ +static const uint8_t rclw_table[32] = { +    0, 1, 2, 3, 4, 5, 6, 7, +    8, 9, 10, 11, 12, 13, 14, 15, +    16, 0, 1, 2, 3, 4, 5, 6, +    7, 8, 9, 10, 11, 12, 13, 14, +}; + +/* division, flags are undefined */ + +void helper_divb_AL(CPUX86State *env, target_ulong t0) +{ +    unsigned int num, den, q, r; + +    num = (env->regs[R_EAX] & 0xffff); +    den = (t0 & 0xff); +    if (den == 0) { +        raise_exception(env, EXCP00_DIVZ); +    } +    q = (num / den); +    if (q > 0xff) { +        raise_exception(env, EXCP00_DIVZ); +    } +    q &= 0xff; +    r = (num % den) & 0xff; +    env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | (r << 8) | q; +} + +void helper_idivb_AL(CPUX86State *env, target_ulong t0) +{ +    int num, den, q, r; + +    num = (int16_t)env->regs[R_EAX]; +    den = (int8_t)t0; +    if (den == 0) { +        raise_exception(env, EXCP00_DIVZ); +    } +    q = (num / den); +    if (q != (int8_t)q) { +        raise_exception(env, EXCP00_DIVZ); +    } +    q &= 0xff; +    r = (num % den) & 0xff; +    env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | (r << 8) | q; +} + +void helper_divw_AX(CPUX86State *env, target_ulong t0) +{ +    unsigned int num, den, q, r; + +    num = (env->regs[R_EAX] & 0xffff) | ((env->regs[R_EDX] & 0xffff) << 16); +    den = (t0 & 0xffff); +    if (den == 0) { +        raise_exception(env, EXCP00_DIVZ); +    } +    q = (num / den); +    if (q > 0xffff) { +        raise_exception(env, EXCP00_DIVZ); +    } +    q &= 0xffff; +    r = (num % den) & 0xffff; +    env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | q; +    env->regs[R_EDX] = (env->regs[R_EDX] & ~0xffff) | r; +} + +void helper_idivw_AX(CPUX86State *env, target_ulong t0) +{ +    int num, den, q, r; + +    num = (env->regs[R_EAX] & 0xffff) | ((env->regs[R_EDX] & 0xffff) << 16); +    den = (int16_t)t0; +    if (den == 0) { +        raise_exception(env, EXCP00_DIVZ); +    } +    q = (num / den); +    if (q != (int16_t)q) { +        raise_exception(env, EXCP00_DIVZ); +    } +    q &= 0xffff; +    r = (num % den) & 0xffff; +    env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | q; +    env->regs[R_EDX] = (env->regs[R_EDX] & ~0xffff) | r; +} + +void helper_divl_EAX(CPUX86State *env, target_ulong t0) +{ +    unsigned int den, r; +    uint64_t num, q; + +    num = ((uint32_t)env->regs[R_EAX]) | ((uint64_t)((uint32_t)env->regs[R_EDX]) << 32); +    den = t0; +    if (den == 0) { +        raise_exception(env, EXCP00_DIVZ); +    } +    q = (num / den); +    r = (num % den); +    if (q > 0xffffffff) { +        raise_exception(env, EXCP00_DIVZ); +    } +    env->regs[R_EAX] = (uint32_t)q; +    env->regs[R_EDX] = (uint32_t)r; +} + +void helper_idivl_EAX(CPUX86State *env, target_ulong t0) +{ +    int den, r; +    int64_t num, q; + +    num = ((uint32_t)env->regs[R_EAX]) | ((uint64_t)((uint32_t)env->regs[R_EDX]) << 32); +    den = t0; +    if (den == 0) { +        raise_exception(env, EXCP00_DIVZ); +    } +    q = (num / den); +    r = (num % den); +    if (q != (int32_t)q) { +        raise_exception(env, EXCP00_DIVZ); +    } +    env->regs[R_EAX] = (uint32_t)q; +    env->regs[R_EDX] = (uint32_t)r; +} + +/* bcd */ + +/* XXX: exception */ +void helper_aam(CPUX86State *env, int base) +{ +    int al, ah; + +    al = env->regs[R_EAX] & 0xff; +    ah = al / base; +    al = al % base; +    env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | al | (ah << 8); +    CC_DST = al; +} + +void helper_aad(CPUX86State *env, int base) +{ +    int al, ah; + +    al = env->regs[R_EAX] & 0xff; +    ah = (env->regs[R_EAX] >> 8) & 0xff; +    al = ((ah * base) + al) & 0xff; +    env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | al; +    CC_DST = al; +} + +void helper_aaa(CPUX86State *env) +{ +    int icarry; +    int al, ah, af; +    int eflags; + +    eflags = cpu_cc_compute_all(env, CC_OP); +    af = eflags & CC_A; +    al = env->regs[R_EAX] & 0xff; +    ah = (env->regs[R_EAX] >> 8) & 0xff; + +    icarry = (al > 0xf9); +    if (((al & 0x0f) > 9) || af) { +        al = (al + 6) & 0x0f; +        ah = (ah + 1 + icarry) & 0xff; +        eflags |= CC_C | CC_A; +    } else { +        eflags &= ~(CC_C | CC_A); +        al &= 0x0f; +    } +    env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | al | (ah << 8); +    CC_SRC = eflags; +} + +void helper_aas(CPUX86State *env) +{ +    int icarry; +    int al, ah, af; +    int eflags; + +    eflags = cpu_cc_compute_all(env, CC_OP); +    af = eflags & CC_A; +    al = env->regs[R_EAX] & 0xff; +    ah = (env->regs[R_EAX] >> 8) & 0xff; + +    icarry = (al < 6); +    if (((al & 0x0f) > 9) || af) { +        al = (al - 6) & 0x0f; +        ah = (ah - 1 - icarry) & 0xff; +        eflags |= CC_C | CC_A; +    } else { +        eflags &= ~(CC_C | CC_A); +        al &= 0x0f; +    } +    env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | al | (ah << 8); +    CC_SRC = eflags; +} + +void helper_daa(CPUX86State *env) +{ +    int old_al, al, af, cf; +    int eflags; + +    eflags = cpu_cc_compute_all(env, CC_OP); +    cf = eflags & CC_C; +    af = eflags & CC_A; +    old_al = al = env->regs[R_EAX] & 0xff; + +    eflags = 0; +    if (((al & 0x0f) > 9) || af) { +        al = (al + 6) & 0xff; +        eflags |= CC_A; +    } +    if ((old_al > 0x99) || cf) { +        al = (al + 0x60) & 0xff; +        eflags |= CC_C; +    } +    env->regs[R_EAX] = (env->regs[R_EAX] & ~0xff) | al; +    /* well, speed is not an issue here, so we compute the flags by hand */ +    eflags |= (al == 0) << 6; /* zf */ +    eflags |= parity_table[al]; /* pf */ +    eflags |= (al & 0x80); /* sf */ +    CC_SRC = eflags; +} + +void helper_das(CPUX86State *env) +{ +    int al, al1, af, cf; +    int eflags; + +    eflags = cpu_cc_compute_all(env, CC_OP); +    cf = eflags & CC_C; +    af = eflags & CC_A; +    al = env->regs[R_EAX] & 0xff; + +    eflags = 0; +    al1 = al; +    if (((al & 0x0f) > 9) || af) { +        eflags |= CC_A; +        if (al < 6 || cf) { +            eflags |= CC_C; +        } +        al = (al - 6) & 0xff; +    } +    if ((al1 > 0x99) || cf) { +        al = (al - 0x60) & 0xff; +        eflags |= CC_C; +    } +    env->regs[R_EAX] = (env->regs[R_EAX] & ~0xff) | al; +    /* well, speed is not an issue here, so we compute the flags by hand */ +    eflags |= (al == 0) << 6; /* zf */ +    eflags |= parity_table[al]; /* pf */ +    eflags |= (al & 0x80); /* sf */ +    CC_SRC = eflags; +} + +#ifdef TARGET_X86_64 +static void add128(uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b) +{ +    *plow += a; +    /* carry test */ +    if (*plow < a) { +        (*phigh)++; +    } +    *phigh += b; +} + +static void neg128(uint64_t *plow, uint64_t *phigh) +{ +    *plow = ~*plow; +    *phigh = ~*phigh; +    add128(plow, phigh, 1, 0); +} + +/* return TRUE if overflow */ +static int div64(uint64_t *plow, uint64_t *phigh, uint64_t b) +{ +    uint64_t q, r, a1, a0; +    int i, qb, ab; + +    a0 = *plow; +    a1 = *phigh; +    if (a1 == 0) { +        q = a0 / b; +        r = a0 % b; +        *plow = q; +        *phigh = r; +    } else { +        if (a1 >= b) { +            return 1; +        } +        /* XXX: use a better algorithm */ +        for (i = 0; i < 64; i++) { +            ab = a1 >> 63; +            a1 = (a1 << 1) | (a0 >> 63); +            if (ab || a1 >= b) { +                a1 -= b; +                qb = 1; +            } else { +                qb = 0; +            } +            a0 = (a0 << 1) | qb; +        } +#if defined(DEBUG_MULDIV) +        printf("div: 0x%016" PRIx64 "%016" PRIx64 " / 0x%016" PRIx64 +               ": q=0x%016" PRIx64 " r=0x%016" PRIx64 "\n", +               *phigh, *plow, b, a0, a1); +#endif +        *plow = a0; +        *phigh = a1; +    } +    return 0; +} + +/* return TRUE if overflow */ +static int idiv64(uint64_t *plow, uint64_t *phigh, int64_t b) +{ +    int sa, sb; + +    sa = ((int64_t)*phigh < 0); +    if (sa) { +        neg128(plow, phigh); +    } +    sb = (b < 0); +    if (sb) { +        b = -b; +    } +    if (div64(plow, phigh, b) != 0) { +        return 1; +    } +    if (sa ^ sb) { +        if (*plow > (1ULL << 63)) { +            return 1; +        } +        *plow = -*plow; +    } else { +        if (*plow >= (1ULL << 63)) { +            return 1; +        } +    } +    if (sa) { +        *phigh = -*phigh; +    } +    return 0; +} + +void helper_divq_EAX(CPUX86State *env, target_ulong t0) +{ +    uint64_t r0, r1; + +    if (t0 == 0) { +        raise_exception(env, EXCP00_DIVZ); +    } +    r0 = env->regs[R_EAX]; +    r1 = env->regs[R_EDX]; +    if (div64(&r0, &r1, t0)) { +        raise_exception(env, EXCP00_DIVZ); +    } +    env->regs[R_EAX] = r0; +    env->regs[R_EDX] = r1; +} + +void helper_idivq_EAX(CPUX86State *env, target_ulong t0) +{ +    uint64_t r0, r1; + +    if (t0 == 0) { +        raise_exception(env, EXCP00_DIVZ); +    } +    r0 = env->regs[R_EAX]; +    r1 = env->regs[R_EDX]; +    if (idiv64(&r0, &r1, t0)) { +        raise_exception(env, EXCP00_DIVZ); +    } +    env->regs[R_EAX] = r0; +    env->regs[R_EDX] = r1; +} +#endif + +#if TARGET_LONG_BITS == 32 +# define ctztl  ctz32 +# define clztl  clz32 +#else +# define ctztl  ctz64 +# define clztl  clz64 +#endif + +/* bit operations */ +target_ulong helper_ctz(target_ulong t0) +{ +    return ctztl(t0); +} + +target_ulong helper_clz(target_ulong t0) +{ +    return clztl(t0); +} + +target_ulong helper_pdep(target_ulong src, target_ulong mask) +{ +    target_ulong dest = 0; +    int i, o; + +    for (i = 0; mask != 0; i++) { +        o = ctztl(mask); +        mask &= mask - 1; +        dest |= ((src >> i) & 1) << o; +    } +    return dest; +} + +target_ulong helper_pext(target_ulong src, target_ulong mask) +{ +    target_ulong dest = 0; +    int i, o; + +    for (o = 0; mask != 0; o++) { +        i = ctztl(mask); +        mask &= mask - 1; +        dest |= ((src >> i) & 1) << o; +    } +    return dest; +} + +#define SHIFT 0 +#include "shift_helper_template.h" +#undef SHIFT + +#define SHIFT 1 +#include "shift_helper_template.h" +#undef SHIFT + +#define SHIFT 2 +#include "shift_helper_template.h" +#undef SHIFT + +#ifdef TARGET_X86_64 +#define SHIFT 3 +#include "shift_helper_template.h" +#undef SHIFT +#endif diff --git a/target-i386/kvm-stub.c b/target-i386/kvm-stub.c new file mode 100644 index 00000000..6fefd65c --- /dev/null +++ b/target-i386/kvm-stub.c @@ -0,0 +1,35 @@ +/* + * QEMU KVM x86 specific function stubs + * + * Copyright Linaro Limited 2012 + * + * Author: Peter Maydell <peter.maydell@linaro.org> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ +#include "qemu-common.h" +#include "kvm_i386.h" + +bool kvm_allows_irq0_override(void) +{ +    return 1; +} + +#ifndef __OPTIMIZE__ +bool kvm_has_smm(void) +{ +    return 1; +} + +/* This function is only called inside conditionals which we + * rely on the compiler to optimize out when CONFIG_KVM is not + * defined. + */ +uint32_t kvm_arch_get_supported_cpuid(KVMState *env, uint32_t function, +                                      uint32_t index, int reg) +{ +    abort(); +} +#endif diff --git a/target-i386/kvm.c b/target-i386/kvm.c new file mode 100644 index 00000000..066d03d9 --- /dev/null +++ b/target-i386/kvm.c @@ -0,0 +1,2923 @@ +/* + * QEMU KVM support + * + * Copyright (C) 2006-2008 Qumranet Technologies + * Copyright IBM, Corp. 2008 + * + * Authors: + *  Anthony Liguori   <aliguori@us.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/utsname.h> + +#include <linux/kvm.h> +#include <linux/kvm_para.h> + +#include "qemu-common.h" +#include "sysemu/sysemu.h" +#include "sysemu/kvm_int.h" +#include "kvm_i386.h" +#include "cpu.h" +#include "exec/gdbstub.h" +#include "qemu/host-utils.h" +#include "qemu/config-file.h" +#include "hw/i386/pc.h" +#include "hw/i386/apic.h" +#include "hw/i386/apic_internal.h" +#include "hw/i386/apic-msidef.h" +#include "exec/ioport.h" +#include <asm/hyperv.h> +#include "hw/pci/pci.h" +#include "migration/migration.h" +#include "exec/memattrs.h" + +//#define DEBUG_KVM + +#ifdef DEBUG_KVM +#define DPRINTF(fmt, ...) \ +    do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) \ +    do { } while (0) +#endif + +#define MSR_KVM_WALL_CLOCK  0x11 +#define MSR_KVM_SYSTEM_TIME 0x12 + +#ifndef BUS_MCEERR_AR +#define BUS_MCEERR_AR 4 +#endif +#ifndef BUS_MCEERR_AO +#define BUS_MCEERR_AO 5 +#endif + +const KVMCapabilityInfo kvm_arch_required_capabilities[] = { +    KVM_CAP_INFO(SET_TSS_ADDR), +    KVM_CAP_INFO(EXT_CPUID), +    KVM_CAP_INFO(MP_STATE), +    KVM_CAP_LAST_INFO +}; + +static bool has_msr_star; +static bool has_msr_hsave_pa; +static bool has_msr_tsc_adjust; +static bool has_msr_tsc_deadline; +static bool has_msr_feature_control; +static bool has_msr_async_pf_en; +static bool has_msr_pv_eoi_en; +static bool has_msr_misc_enable; +static bool has_msr_smbase; +static bool has_msr_bndcfgs; +static bool has_msr_kvm_steal_time; +static int lm_capable_kernel; +static bool has_msr_hv_hypercall; +static bool has_msr_hv_vapic; +static bool has_msr_hv_tsc; +static bool has_msr_mtrr; +static bool has_msr_xss; + +static bool has_msr_architectural_pmu; +static uint32_t num_architectural_pmu_counters; + +bool kvm_has_smm(void) +{ +    return kvm_check_extension(kvm_state, KVM_CAP_X86_SMM); +} + +bool kvm_allows_irq0_override(void) +{ +    return !kvm_irqchip_in_kernel() || kvm_has_gsi_routing(); +} + +static struct kvm_cpuid2 *try_get_cpuid(KVMState *s, int max) +{ +    struct kvm_cpuid2 *cpuid; +    int r, size; + +    size = sizeof(*cpuid) + max * sizeof(*cpuid->entries); +    cpuid = g_malloc0(size); +    cpuid->nent = max; +    r = kvm_ioctl(s, KVM_GET_SUPPORTED_CPUID, cpuid); +    if (r == 0 && cpuid->nent >= max) { +        r = -E2BIG; +    } +    if (r < 0) { +        if (r == -E2BIG) { +            g_free(cpuid); +            return NULL; +        } else { +            fprintf(stderr, "KVM_GET_SUPPORTED_CPUID failed: %s\n", +                    strerror(-r)); +            exit(1); +        } +    } +    return cpuid; +} + +/* Run KVM_GET_SUPPORTED_CPUID ioctl(), allocating a buffer large enough + * for all entries. + */ +static struct kvm_cpuid2 *get_supported_cpuid(KVMState *s) +{ +    struct kvm_cpuid2 *cpuid; +    int max = 1; +    while ((cpuid = try_get_cpuid(s, max)) == NULL) { +        max *= 2; +    } +    return cpuid; +} + +static const struct kvm_para_features { +    int cap; +    int feature; +} para_features[] = { +    { KVM_CAP_CLOCKSOURCE, KVM_FEATURE_CLOCKSOURCE }, +    { KVM_CAP_NOP_IO_DELAY, KVM_FEATURE_NOP_IO_DELAY }, +    { KVM_CAP_PV_MMU, KVM_FEATURE_MMU_OP }, +    { KVM_CAP_ASYNC_PF, KVM_FEATURE_ASYNC_PF }, +}; + +static int get_para_features(KVMState *s) +{ +    int i, features = 0; + +    for (i = 0; i < ARRAY_SIZE(para_features); i++) { +        if (kvm_check_extension(s, para_features[i].cap)) { +            features |= (1 << para_features[i].feature); +        } +    } + +    return features; +} + + +/* Returns the value for a specific register on the cpuid entry + */ +static uint32_t cpuid_entry_get_reg(struct kvm_cpuid_entry2 *entry, int reg) +{ +    uint32_t ret = 0; +    switch (reg) { +    case R_EAX: +        ret = entry->eax; +        break; +    case R_EBX: +        ret = entry->ebx; +        break; +    case R_ECX: +        ret = entry->ecx; +        break; +    case R_EDX: +        ret = entry->edx; +        break; +    } +    return ret; +} + +/* Find matching entry for function/index on kvm_cpuid2 struct + */ +static struct kvm_cpuid_entry2 *cpuid_find_entry(struct kvm_cpuid2 *cpuid, +                                                 uint32_t function, +                                                 uint32_t index) +{ +    int i; +    for (i = 0; i < cpuid->nent; ++i) { +        if (cpuid->entries[i].function == function && +            cpuid->entries[i].index == index) { +            return &cpuid->entries[i]; +        } +    } +    /* not found: */ +    return NULL; +} + +uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, +                                      uint32_t index, int reg) +{ +    struct kvm_cpuid2 *cpuid; +    uint32_t ret = 0; +    uint32_t cpuid_1_edx; +    bool found = false; + +    cpuid = get_supported_cpuid(s); + +    struct kvm_cpuid_entry2 *entry = cpuid_find_entry(cpuid, function, index); +    if (entry) { +        found = true; +        ret = cpuid_entry_get_reg(entry, reg); +    } + +    /* Fixups for the data returned by KVM, below */ + +    if (function == 1 && reg == R_EDX) { +        /* KVM before 2.6.30 misreports the following features */ +        ret |= CPUID_MTRR | CPUID_PAT | CPUID_MCE | CPUID_MCA; +    } else if (function == 1 && reg == R_ECX) { +        /* We can set the hypervisor flag, even if KVM does not return it on +         * GET_SUPPORTED_CPUID +         */ +        ret |= CPUID_EXT_HYPERVISOR; +        /* tsc-deadline flag is not returned by GET_SUPPORTED_CPUID, but it +         * can be enabled if the kernel has KVM_CAP_TSC_DEADLINE_TIMER, +         * and the irqchip is in the kernel. +         */ +        if (kvm_irqchip_in_kernel() && +                kvm_check_extension(s, KVM_CAP_TSC_DEADLINE_TIMER)) { +            ret |= CPUID_EXT_TSC_DEADLINE_TIMER; +        } + +        /* x2apic is reported by GET_SUPPORTED_CPUID, but it can't be enabled +         * without the in-kernel irqchip +         */ +        if (!kvm_irqchip_in_kernel()) { +            ret &= ~CPUID_EXT_X2APIC; +        } +    } else if (function == 6 && reg == R_EAX) { +        ret |= CPUID_6_EAX_ARAT; /* safe to allow because of emulated APIC */ +    } else if (function == 0x80000001 && reg == R_EDX) { +        /* On Intel, kvm returns cpuid according to the Intel spec, +         * so add missing bits according to the AMD spec: +         */ +        cpuid_1_edx = kvm_arch_get_supported_cpuid(s, 1, 0, R_EDX); +        ret |= cpuid_1_edx & CPUID_EXT2_AMD_ALIASES; +    } + +    g_free(cpuid); + +    /* fallback for older kernels */ +    if ((function == KVM_CPUID_FEATURES) && !found) { +        ret = get_para_features(s); +    } + +    return ret; +} + +typedef struct HWPoisonPage { +    ram_addr_t ram_addr; +    QLIST_ENTRY(HWPoisonPage) list; +} HWPoisonPage; + +static QLIST_HEAD(, HWPoisonPage) hwpoison_page_list = +    QLIST_HEAD_INITIALIZER(hwpoison_page_list); + +static void kvm_unpoison_all(void *param) +{ +    HWPoisonPage *page, *next_page; + +    QLIST_FOREACH_SAFE(page, &hwpoison_page_list, list, next_page) { +        QLIST_REMOVE(page, list); +        qemu_ram_remap(page->ram_addr, TARGET_PAGE_SIZE); +        g_free(page); +    } +} + +static void kvm_hwpoison_page_add(ram_addr_t ram_addr) +{ +    HWPoisonPage *page; + +    QLIST_FOREACH(page, &hwpoison_page_list, list) { +        if (page->ram_addr == ram_addr) { +            return; +        } +    } +    page = g_new(HWPoisonPage, 1); +    page->ram_addr = ram_addr; +    QLIST_INSERT_HEAD(&hwpoison_page_list, page, list); +} + +static int kvm_get_mce_cap_supported(KVMState *s, uint64_t *mce_cap, +                                     int *max_banks) +{ +    int r; + +    r = kvm_check_extension(s, KVM_CAP_MCE); +    if (r > 0) { +        *max_banks = r; +        return kvm_ioctl(s, KVM_X86_GET_MCE_CAP_SUPPORTED, mce_cap); +    } +    return -ENOSYS; +} + +static void kvm_mce_inject(X86CPU *cpu, hwaddr paddr, int code) +{ +    CPUX86State *env = &cpu->env; +    uint64_t status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN | +                      MCI_STATUS_MISCV | MCI_STATUS_ADDRV | MCI_STATUS_S; +    uint64_t mcg_status = MCG_STATUS_MCIP; + +    if (code == BUS_MCEERR_AR) { +        status |= MCI_STATUS_AR | 0x134; +        mcg_status |= MCG_STATUS_EIPV; +    } else { +        status |= 0xc0; +        mcg_status |= MCG_STATUS_RIPV; +    } +    cpu_x86_inject_mce(NULL, cpu, 9, status, mcg_status, paddr, +                       (MCM_ADDR_PHYS << 6) | 0xc, +                       cpu_x86_support_mca_broadcast(env) ? +                       MCE_INJECT_BROADCAST : 0); +} + +static void hardware_memory_error(void) +{ +    fprintf(stderr, "Hardware memory error!\n"); +    exit(1); +} + +int kvm_arch_on_sigbus_vcpu(CPUState *c, int code, void *addr) +{ +    X86CPU *cpu = X86_CPU(c); +    CPUX86State *env = &cpu->env; +    ram_addr_t ram_addr; +    hwaddr paddr; + +    if ((env->mcg_cap & MCG_SER_P) && addr +        && (code == BUS_MCEERR_AR || code == BUS_MCEERR_AO)) { +        if (qemu_ram_addr_from_host(addr, &ram_addr) == NULL || +            !kvm_physical_memory_addr_from_host(c->kvm_state, addr, &paddr)) { +            fprintf(stderr, "Hardware memory error for memory used by " +                    "QEMU itself instead of guest system!\n"); +            /* Hope we are lucky for AO MCE */ +            if (code == BUS_MCEERR_AO) { +                return 0; +            } else { +                hardware_memory_error(); +            } +        } +        kvm_hwpoison_page_add(ram_addr); +        kvm_mce_inject(cpu, paddr, code); +    } else { +        if (code == BUS_MCEERR_AO) { +            return 0; +        } else if (code == BUS_MCEERR_AR) { +            hardware_memory_error(); +        } else { +            return 1; +        } +    } +    return 0; +} + +int kvm_arch_on_sigbus(int code, void *addr) +{ +    X86CPU *cpu = X86_CPU(first_cpu); + +    if ((cpu->env.mcg_cap & MCG_SER_P) && addr && code == BUS_MCEERR_AO) { +        ram_addr_t ram_addr; +        hwaddr paddr; + +        /* Hope we are lucky for AO MCE */ +        if (qemu_ram_addr_from_host(addr, &ram_addr) == NULL || +            !kvm_physical_memory_addr_from_host(first_cpu->kvm_state, +                                                addr, &paddr)) { +            fprintf(stderr, "Hardware memory error for memory used by " +                    "QEMU itself instead of guest system!: %p\n", addr); +            return 0; +        } +        kvm_hwpoison_page_add(ram_addr); +        kvm_mce_inject(X86_CPU(first_cpu), paddr, code); +    } else { +        if (code == BUS_MCEERR_AO) { +            return 0; +        } else if (code == BUS_MCEERR_AR) { +            hardware_memory_error(); +        } else { +            return 1; +        } +    } +    return 0; +} + +static int kvm_inject_mce_oldstyle(X86CPU *cpu) +{ +    CPUX86State *env = &cpu->env; + +    if (!kvm_has_vcpu_events() && env->exception_injected == EXCP12_MCHK) { +        unsigned int bank, bank_num = env->mcg_cap & 0xff; +        struct kvm_x86_mce mce; + +        env->exception_injected = -1; + +        /* +         * There must be at least one bank in use if an MCE is pending. +         * Find it and use its values for the event injection. +         */ +        for (bank = 0; bank < bank_num; bank++) { +            if (env->mce_banks[bank * 4 + 1] & MCI_STATUS_VAL) { +                break; +            } +        } +        assert(bank < bank_num); + +        mce.bank = bank; +        mce.status = env->mce_banks[bank * 4 + 1]; +        mce.mcg_status = env->mcg_status; +        mce.addr = env->mce_banks[bank * 4 + 2]; +        mce.misc = env->mce_banks[bank * 4 + 3]; + +        return kvm_vcpu_ioctl(CPU(cpu), KVM_X86_SET_MCE, &mce); +    } +    return 0; +} + +static void cpu_update_state(void *opaque, int running, RunState state) +{ +    CPUX86State *env = opaque; + +    if (running) { +        env->tsc_valid = false; +    } +} + +unsigned long kvm_arch_vcpu_id(CPUState *cs) +{ +    X86CPU *cpu = X86_CPU(cs); +    return cpu->apic_id; +} + +#ifndef KVM_CPUID_SIGNATURE_NEXT +#define KVM_CPUID_SIGNATURE_NEXT                0x40000100 +#endif + +static bool hyperv_hypercall_available(X86CPU *cpu) +{ +    return cpu->hyperv_vapic || +           (cpu->hyperv_spinlock_attempts != HYPERV_SPINLOCK_NEVER_RETRY); +} + +static bool hyperv_enabled(X86CPU *cpu) +{ +    CPUState *cs = CPU(cpu); +    return kvm_check_extension(cs->kvm_state, KVM_CAP_HYPERV) > 0 && +           (hyperv_hypercall_available(cpu) || +            cpu->hyperv_time  || +            cpu->hyperv_relaxed_timing); +} + +static Error *invtsc_mig_blocker; + +#define KVM_MAX_CPUID_ENTRIES  100 + +int kvm_arch_init_vcpu(CPUState *cs) +{ +    struct { +        struct kvm_cpuid2 cpuid; +        struct kvm_cpuid_entry2 entries[KVM_MAX_CPUID_ENTRIES]; +    } QEMU_PACKED cpuid_data; +    X86CPU *cpu = X86_CPU(cs); +    CPUX86State *env = &cpu->env; +    uint32_t limit, i, j, cpuid_i; +    uint32_t unused; +    struct kvm_cpuid_entry2 *c; +    uint32_t signature[3]; +    int kvm_base = KVM_CPUID_SIGNATURE; +    int r; + +    memset(&cpuid_data, 0, sizeof(cpuid_data)); + +    cpuid_i = 0; + +    /* Paravirtualization CPUIDs */ +    if (hyperv_enabled(cpu)) { +        c = &cpuid_data.entries[cpuid_i++]; +        c->function = HYPERV_CPUID_VENDOR_AND_MAX_FUNCTIONS; +        memcpy(signature, "Microsoft Hv", 12); +        c->eax = HYPERV_CPUID_MIN; +        c->ebx = signature[0]; +        c->ecx = signature[1]; +        c->edx = signature[2]; + +        c = &cpuid_data.entries[cpuid_i++]; +        c->function = HYPERV_CPUID_INTERFACE; +        memcpy(signature, "Hv#1\0\0\0\0\0\0\0\0", 12); +        c->eax = signature[0]; +        c->ebx = 0; +        c->ecx = 0; +        c->edx = 0; + +        c = &cpuid_data.entries[cpuid_i++]; +        c->function = HYPERV_CPUID_VERSION; +        c->eax = 0x00001bbc; +        c->ebx = 0x00060001; + +        c = &cpuid_data.entries[cpuid_i++]; +        c->function = HYPERV_CPUID_FEATURES; +        if (cpu->hyperv_relaxed_timing) { +            c->eax |= HV_X64_MSR_HYPERCALL_AVAILABLE; +        } +        if (cpu->hyperv_vapic) { +            c->eax |= HV_X64_MSR_HYPERCALL_AVAILABLE; +            c->eax |= HV_X64_MSR_APIC_ACCESS_AVAILABLE; +            has_msr_hv_vapic = true; +        } +        if (cpu->hyperv_time && +            kvm_check_extension(cs->kvm_state, KVM_CAP_HYPERV_TIME) > 0) { +            c->eax |= HV_X64_MSR_HYPERCALL_AVAILABLE; +            c->eax |= HV_X64_MSR_TIME_REF_COUNT_AVAILABLE; +            c->eax |= 0x200; +            has_msr_hv_tsc = true; +        } +        c = &cpuid_data.entries[cpuid_i++]; +        c->function = HYPERV_CPUID_ENLIGHTMENT_INFO; +        if (cpu->hyperv_relaxed_timing) { +            c->eax |= HV_X64_RELAXED_TIMING_RECOMMENDED; +        } +        if (has_msr_hv_vapic) { +            c->eax |= HV_X64_APIC_ACCESS_RECOMMENDED; +        } +        c->ebx = cpu->hyperv_spinlock_attempts; + +        c = &cpuid_data.entries[cpuid_i++]; +        c->function = HYPERV_CPUID_IMPLEMENT_LIMITS; +        c->eax = 0x40; +        c->ebx = 0x40; + +        kvm_base = KVM_CPUID_SIGNATURE_NEXT; +        has_msr_hv_hypercall = true; +    } + +    if (cpu->expose_kvm) { +        memcpy(signature, "KVMKVMKVM\0\0\0", 12); +        c = &cpuid_data.entries[cpuid_i++]; +        c->function = KVM_CPUID_SIGNATURE | kvm_base; +        c->eax = KVM_CPUID_FEATURES | kvm_base; +        c->ebx = signature[0]; +        c->ecx = signature[1]; +        c->edx = signature[2]; + +        c = &cpuid_data.entries[cpuid_i++]; +        c->function = KVM_CPUID_FEATURES | kvm_base; +        c->eax = env->features[FEAT_KVM]; + +        has_msr_async_pf_en = c->eax & (1 << KVM_FEATURE_ASYNC_PF); + +        has_msr_pv_eoi_en = c->eax & (1 << KVM_FEATURE_PV_EOI); + +        has_msr_kvm_steal_time = c->eax & (1 << KVM_FEATURE_STEAL_TIME); +    } + +    cpu_x86_cpuid(env, 0, 0, &limit, &unused, &unused, &unused); + +    for (i = 0; i <= limit; i++) { +        if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { +            fprintf(stderr, "unsupported level value: 0x%x\n", limit); +            abort(); +        } +        c = &cpuid_data.entries[cpuid_i++]; + +        switch (i) { +        case 2: { +            /* Keep reading function 2 till all the input is received */ +            int times; + +            c->function = i; +            c->flags = KVM_CPUID_FLAG_STATEFUL_FUNC | +                       KVM_CPUID_FLAG_STATE_READ_NEXT; +            cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); +            times = c->eax & 0xff; + +            for (j = 1; j < times; ++j) { +                if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { +                    fprintf(stderr, "cpuid_data is full, no space for " +                            "cpuid(eax:2):eax & 0xf = 0x%x\n", times); +                    abort(); +                } +                c = &cpuid_data.entries[cpuid_i++]; +                c->function = i; +                c->flags = KVM_CPUID_FLAG_STATEFUL_FUNC; +                cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); +            } +            break; +        } +        case 4: +        case 0xb: +        case 0xd: +            for (j = 0; ; j++) { +                if (i == 0xd && j == 64) { +                    break; +                } +                c->function = i; +                c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; +                c->index = j; +                cpu_x86_cpuid(env, i, j, &c->eax, &c->ebx, &c->ecx, &c->edx); + +                if (i == 4 && c->eax == 0) { +                    break; +                } +                if (i == 0xb && !(c->ecx & 0xff00)) { +                    break; +                } +                if (i == 0xd && c->eax == 0) { +                    continue; +                } +                if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { +                    fprintf(stderr, "cpuid_data is full, no space for " +                            "cpuid(eax:0x%x,ecx:0x%x)\n", i, j); +                    abort(); +                } +                c = &cpuid_data.entries[cpuid_i++]; +            } +            break; +        default: +            c->function = i; +            c->flags = 0; +            cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); +            break; +        } +    } + +    if (limit >= 0x0a) { +        uint32_t ver; + +        cpu_x86_cpuid(env, 0x0a, 0, &ver, &unused, &unused, &unused); +        if ((ver & 0xff) > 0) { +            has_msr_architectural_pmu = true; +            num_architectural_pmu_counters = (ver & 0xff00) >> 8; + +            /* Shouldn't be more than 32, since that's the number of bits +             * available in EBX to tell us _which_ counters are available. +             * Play it safe. +             */ +            if (num_architectural_pmu_counters > MAX_GP_COUNTERS) { +                num_architectural_pmu_counters = MAX_GP_COUNTERS; +            } +        } +    } + +    cpu_x86_cpuid(env, 0x80000000, 0, &limit, &unused, &unused, &unused); + +    for (i = 0x80000000; i <= limit; i++) { +        if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { +            fprintf(stderr, "unsupported xlevel value: 0x%x\n", limit); +            abort(); +        } +        c = &cpuid_data.entries[cpuid_i++]; + +        c->function = i; +        c->flags = 0; +        cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); +    } + +    /* Call Centaur's CPUID instructions they are supported. */ +    if (env->cpuid_xlevel2 > 0) { +        cpu_x86_cpuid(env, 0xC0000000, 0, &limit, &unused, &unused, &unused); + +        for (i = 0xC0000000; i <= limit; i++) { +            if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { +                fprintf(stderr, "unsupported xlevel2 value: 0x%x\n", limit); +                abort(); +            } +            c = &cpuid_data.entries[cpuid_i++]; + +            c->function = i; +            c->flags = 0; +            cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); +        } +    } + +    cpuid_data.cpuid.nent = cpuid_i; + +    if (((env->cpuid_version >> 8)&0xF) >= 6 +        && (env->features[FEAT_1_EDX] & (CPUID_MCE | CPUID_MCA)) == +           (CPUID_MCE | CPUID_MCA) +        && kvm_check_extension(cs->kvm_state, KVM_CAP_MCE) > 0) { +        uint64_t mcg_cap; +        int banks; +        int ret; + +        ret = kvm_get_mce_cap_supported(cs->kvm_state, &mcg_cap, &banks); +        if (ret < 0) { +            fprintf(stderr, "kvm_get_mce_cap_supported: %s", strerror(-ret)); +            return ret; +        } + +        if (banks > MCE_BANKS_DEF) { +            banks = MCE_BANKS_DEF; +        } +        mcg_cap &= MCE_CAP_DEF; +        mcg_cap |= banks; +        ret = kvm_vcpu_ioctl(cs, KVM_X86_SETUP_MCE, &mcg_cap); +        if (ret < 0) { +            fprintf(stderr, "KVM_X86_SETUP_MCE: %s", strerror(-ret)); +            return ret; +        } + +        env->mcg_cap = mcg_cap; +    } + +    qemu_add_vm_change_state_handler(cpu_update_state, env); + +    c = cpuid_find_entry(&cpuid_data.cpuid, 1, 0); +    if (c) { +        has_msr_feature_control = !!(c->ecx & CPUID_EXT_VMX) || +                                  !!(c->ecx & CPUID_EXT_SMX); +    } + +    c = cpuid_find_entry(&cpuid_data.cpuid, 0x80000007, 0); +    if (c && (c->edx & 1<<8) && invtsc_mig_blocker == NULL) { +        /* for migration */ +        error_setg(&invtsc_mig_blocker, +                   "State blocked by non-migratable CPU device" +                   " (invtsc flag)"); +        migrate_add_blocker(invtsc_mig_blocker); +        /* for savevm */ +        vmstate_x86_cpu.unmigratable = 1; +    } + +    cpuid_data.cpuid.padding = 0; +    r = kvm_vcpu_ioctl(cs, KVM_SET_CPUID2, &cpuid_data); +    if (r) { +        return r; +    } + +    r = kvm_check_extension(cs->kvm_state, KVM_CAP_TSC_CONTROL); +    if (r && env->tsc_khz) { +        r = kvm_vcpu_ioctl(cs, KVM_SET_TSC_KHZ, env->tsc_khz); +        if (r < 0) { +            fprintf(stderr, "KVM_SET_TSC_KHZ failed\n"); +            return r; +        } +    } + +    if (kvm_has_xsave()) { +        env->kvm_xsave_buf = qemu_memalign(4096, sizeof(struct kvm_xsave)); +    } + +    if (env->features[FEAT_1_EDX] & CPUID_MTRR) { +        has_msr_mtrr = true; +    } + +    return 0; +} + +void kvm_arch_reset_vcpu(X86CPU *cpu) +{ +    CPUX86State *env = &cpu->env; + +    env->exception_injected = -1; +    env->interrupt_injected = -1; +    env->xcr0 = 1; +    if (kvm_irqchip_in_kernel()) { +        env->mp_state = cpu_is_bsp(cpu) ? KVM_MP_STATE_RUNNABLE : +                                          KVM_MP_STATE_UNINITIALIZED; +    } else { +        env->mp_state = KVM_MP_STATE_RUNNABLE; +    } +} + +void kvm_arch_do_init_vcpu(X86CPU *cpu) +{ +    CPUX86State *env = &cpu->env; + +    /* APs get directly into wait-for-SIPI state.  */ +    if (env->mp_state == KVM_MP_STATE_UNINITIALIZED) { +        env->mp_state = KVM_MP_STATE_INIT_RECEIVED; +    } +} + +static int kvm_get_supported_msrs(KVMState *s) +{ +    static int kvm_supported_msrs; +    int ret = 0; + +    /* first time */ +    if (kvm_supported_msrs == 0) { +        struct kvm_msr_list msr_list, *kvm_msr_list; + +        kvm_supported_msrs = -1; + +        /* Obtain MSR list from KVM.  These are the MSRs that we must +         * save/restore */ +        msr_list.nmsrs = 0; +        ret = kvm_ioctl(s, KVM_GET_MSR_INDEX_LIST, &msr_list); +        if (ret < 0 && ret != -E2BIG) { +            return ret; +        } +        /* Old kernel modules had a bug and could write beyond the provided +           memory. Allocate at least a safe amount of 1K. */ +        kvm_msr_list = g_malloc0(MAX(1024, sizeof(msr_list) + +                                              msr_list.nmsrs * +                                              sizeof(msr_list.indices[0]))); + +        kvm_msr_list->nmsrs = msr_list.nmsrs; +        ret = kvm_ioctl(s, KVM_GET_MSR_INDEX_LIST, kvm_msr_list); +        if (ret >= 0) { +            int i; + +            for (i = 0; i < kvm_msr_list->nmsrs; i++) { +                if (kvm_msr_list->indices[i] == MSR_STAR) { +                    has_msr_star = true; +                    continue; +                } +                if (kvm_msr_list->indices[i] == MSR_VM_HSAVE_PA) { +                    has_msr_hsave_pa = true; +                    continue; +                } +                if (kvm_msr_list->indices[i] == MSR_TSC_ADJUST) { +                    has_msr_tsc_adjust = true; +                    continue; +                } +                if (kvm_msr_list->indices[i] == MSR_IA32_TSCDEADLINE) { +                    has_msr_tsc_deadline = true; +                    continue; +                } +                if (kvm_msr_list->indices[i] == MSR_IA32_SMBASE) { +                    has_msr_smbase = true; +                    continue; +                } +                if (kvm_msr_list->indices[i] == MSR_IA32_MISC_ENABLE) { +                    has_msr_misc_enable = true; +                    continue; +                } +                if (kvm_msr_list->indices[i] == MSR_IA32_BNDCFGS) { +                    has_msr_bndcfgs = true; +                    continue; +                } +                if (kvm_msr_list->indices[i] == MSR_IA32_XSS) { +                    has_msr_xss = true; +                    continue; +                } +            } +        } + +        g_free(kvm_msr_list); +    } + +    return ret; +} + +static Notifier smram_machine_done; +static KVMMemoryListener smram_listener; +static AddressSpace smram_address_space; +static MemoryRegion smram_as_root; +static MemoryRegion smram_as_mem; + +static void register_smram_listener(Notifier *n, void *unused) +{ +    MemoryRegion *smram = +        (MemoryRegion *) object_resolve_path("/machine/smram", NULL); + +    /* Outer container... */ +    memory_region_init(&smram_as_root, OBJECT(kvm_state), "mem-container-smram", ~0ull); +    memory_region_set_enabled(&smram_as_root, true); + +    /* ... with two regions inside: normal system memory with low +     * priority, and... +     */ +    memory_region_init_alias(&smram_as_mem, OBJECT(kvm_state), "mem-smram", +                             get_system_memory(), 0, ~0ull); +    memory_region_add_subregion_overlap(&smram_as_root, 0, &smram_as_mem, 0); +    memory_region_set_enabled(&smram_as_mem, true); + +    if (smram) { +        /* ... SMRAM with higher priority */ +        memory_region_add_subregion_overlap(&smram_as_root, 0, smram, 10); +        memory_region_set_enabled(smram, true); +    } + +    address_space_init(&smram_address_space, &smram_as_root, "KVM-SMRAM"); +    kvm_memory_listener_register(kvm_state, &smram_listener, +                                 &smram_address_space, 1); +} + +int kvm_arch_init(MachineState *ms, KVMState *s) +{ +    uint64_t identity_base = 0xfffbc000; +    uint64_t shadow_mem; +    int ret; +    struct utsname utsname; + +    ret = kvm_get_supported_msrs(s); +    if (ret < 0) { +        return ret; +    } + +    uname(&utsname); +    lm_capable_kernel = strcmp(utsname.machine, "x86_64") == 0; + +    /* +     * On older Intel CPUs, KVM uses vm86 mode to emulate 16-bit code directly. +     * In order to use vm86 mode, an EPT identity map and a TSS  are needed. +     * Since these must be part of guest physical memory, we need to allocate +     * them, both by setting their start addresses in the kernel and by +     * creating a corresponding e820 entry. We need 4 pages before the BIOS. +     * +     * Older KVM versions may not support setting the identity map base. In +     * that case we need to stick with the default, i.e. a 256K maximum BIOS +     * size. +     */ +    if (kvm_check_extension(s, KVM_CAP_SET_IDENTITY_MAP_ADDR)) { +        /* Allows up to 16M BIOSes. */ +        identity_base = 0xfeffc000; + +        ret = kvm_vm_ioctl(s, KVM_SET_IDENTITY_MAP_ADDR, &identity_base); +        if (ret < 0) { +            return ret; +        } +    } + +    /* Set TSS base one page after EPT identity map. */ +    ret = kvm_vm_ioctl(s, KVM_SET_TSS_ADDR, identity_base + 0x1000); +    if (ret < 0) { +        return ret; +    } + +    /* Tell fw_cfg to notify the BIOS to reserve the range. */ +    ret = e820_add_entry(identity_base, 0x4000, E820_RESERVED); +    if (ret < 0) { +        fprintf(stderr, "e820_add_entry() table is full\n"); +        return ret; +    } +    qemu_register_reset(kvm_unpoison_all, NULL); + +    shadow_mem = machine_kvm_shadow_mem(ms); +    if (shadow_mem != -1) { +        shadow_mem /= 4096; +        ret = kvm_vm_ioctl(s, KVM_SET_NR_MMU_PAGES, shadow_mem); +        if (ret < 0) { +            return ret; +        } +    } + +    if (kvm_check_extension(s, KVM_CAP_X86_SMM)) { +        smram_machine_done.notify = register_smram_listener; +        qemu_add_machine_init_done_notifier(&smram_machine_done); +    } +    return 0; +} + +static void set_v8086_seg(struct kvm_segment *lhs, const SegmentCache *rhs) +{ +    lhs->selector = rhs->selector; +    lhs->base = rhs->base; +    lhs->limit = rhs->limit; +    lhs->type = 3; +    lhs->present = 1; +    lhs->dpl = 3; +    lhs->db = 0; +    lhs->s = 1; +    lhs->l = 0; +    lhs->g = 0; +    lhs->avl = 0; +    lhs->unusable = 0; +} + +static void set_seg(struct kvm_segment *lhs, const SegmentCache *rhs) +{ +    unsigned flags = rhs->flags; +    lhs->selector = rhs->selector; +    lhs->base = rhs->base; +    lhs->limit = rhs->limit; +    lhs->type = (flags >> DESC_TYPE_SHIFT) & 15; +    lhs->present = (flags & DESC_P_MASK) != 0; +    lhs->dpl = (flags >> DESC_DPL_SHIFT) & 3; +    lhs->db = (flags >> DESC_B_SHIFT) & 1; +    lhs->s = (flags & DESC_S_MASK) != 0; +    lhs->l = (flags >> DESC_L_SHIFT) & 1; +    lhs->g = (flags & DESC_G_MASK) != 0; +    lhs->avl = (flags & DESC_AVL_MASK) != 0; +    lhs->unusable = 0; +    lhs->padding = 0; +} + +static void get_seg(SegmentCache *lhs, const struct kvm_segment *rhs) +{ +    lhs->selector = rhs->selector; +    lhs->base = rhs->base; +    lhs->limit = rhs->limit; +    lhs->flags = (rhs->type << DESC_TYPE_SHIFT) | +                 (rhs->present * DESC_P_MASK) | +                 (rhs->dpl << DESC_DPL_SHIFT) | +                 (rhs->db << DESC_B_SHIFT) | +                 (rhs->s * DESC_S_MASK) | +                 (rhs->l << DESC_L_SHIFT) | +                 (rhs->g * DESC_G_MASK) | +                 (rhs->avl * DESC_AVL_MASK); +} + +static void kvm_getput_reg(__u64 *kvm_reg, target_ulong *qemu_reg, int set) +{ +    if (set) { +        *kvm_reg = *qemu_reg; +    } else { +        *qemu_reg = *kvm_reg; +    } +} + +static int kvm_getput_regs(X86CPU *cpu, int set) +{ +    CPUX86State *env = &cpu->env; +    struct kvm_regs regs; +    int ret = 0; + +    if (!set) { +        ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_REGS, ®s); +        if (ret < 0) { +            return ret; +        } +    } + +    kvm_getput_reg(®s.rax, &env->regs[R_EAX], set); +    kvm_getput_reg(®s.rbx, &env->regs[R_EBX], set); +    kvm_getput_reg(®s.rcx, &env->regs[R_ECX], set); +    kvm_getput_reg(®s.rdx, &env->regs[R_EDX], set); +    kvm_getput_reg(®s.rsi, &env->regs[R_ESI], set); +    kvm_getput_reg(®s.rdi, &env->regs[R_EDI], set); +    kvm_getput_reg(®s.rsp, &env->regs[R_ESP], set); +    kvm_getput_reg(®s.rbp, &env->regs[R_EBP], set); +#ifdef TARGET_X86_64 +    kvm_getput_reg(®s.r8, &env->regs[8], set); +    kvm_getput_reg(®s.r9, &env->regs[9], set); +    kvm_getput_reg(®s.r10, &env->regs[10], set); +    kvm_getput_reg(®s.r11, &env->regs[11], set); +    kvm_getput_reg(®s.r12, &env->regs[12], set); +    kvm_getput_reg(®s.r13, &env->regs[13], set); +    kvm_getput_reg(®s.r14, &env->regs[14], set); +    kvm_getput_reg(®s.r15, &env->regs[15], set); +#endif + +    kvm_getput_reg(®s.rflags, &env->eflags, set); +    kvm_getput_reg(®s.rip, &env->eip, set); + +    if (set) { +        ret = kvm_vcpu_ioctl(CPU(cpu), KVM_SET_REGS, ®s); +    } + +    return ret; +} + +static int kvm_put_fpu(X86CPU *cpu) +{ +    CPUX86State *env = &cpu->env; +    struct kvm_fpu fpu; +    int i; + +    memset(&fpu, 0, sizeof fpu); +    fpu.fsw = env->fpus & ~(7 << 11); +    fpu.fsw |= (env->fpstt & 7) << 11; +    fpu.fcw = env->fpuc; +    fpu.last_opcode = env->fpop; +    fpu.last_ip = env->fpip; +    fpu.last_dp = env->fpdp; +    for (i = 0; i < 8; ++i) { +        fpu.ftwx |= (!env->fptags[i]) << i; +    } +    memcpy(fpu.fpr, env->fpregs, sizeof env->fpregs); +    for (i = 0; i < CPU_NB_REGS; i++) { +        stq_p(&fpu.xmm[i][0], env->xmm_regs[i].XMM_Q(0)); +        stq_p(&fpu.xmm[i][8], env->xmm_regs[i].XMM_Q(1)); +    } +    fpu.mxcsr = env->mxcsr; + +    return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_FPU, &fpu); +} + +#define XSAVE_FCW_FSW     0 +#define XSAVE_FTW_FOP     1 +#define XSAVE_CWD_RIP     2 +#define XSAVE_CWD_RDP     4 +#define XSAVE_MXCSR       6 +#define XSAVE_ST_SPACE    8 +#define XSAVE_XMM_SPACE   40 +#define XSAVE_XSTATE_BV   128 +#define XSAVE_YMMH_SPACE  144 +#define XSAVE_BNDREGS     240 +#define XSAVE_BNDCSR      256 +#define XSAVE_OPMASK      272 +#define XSAVE_ZMM_Hi256   288 +#define XSAVE_Hi16_ZMM    416 + +static int kvm_put_xsave(X86CPU *cpu) +{ +    CPUX86State *env = &cpu->env; +    struct kvm_xsave* xsave = env->kvm_xsave_buf; +    uint16_t cwd, swd, twd; +    uint8_t *xmm, *ymmh, *zmmh; +    int i, r; + +    if (!kvm_has_xsave()) { +        return kvm_put_fpu(cpu); +    } + +    memset(xsave, 0, sizeof(struct kvm_xsave)); +    twd = 0; +    swd = env->fpus & ~(7 << 11); +    swd |= (env->fpstt & 7) << 11; +    cwd = env->fpuc; +    for (i = 0; i < 8; ++i) { +        twd |= (!env->fptags[i]) << i; +    } +    xsave->region[XSAVE_FCW_FSW] = (uint32_t)(swd << 16) + cwd; +    xsave->region[XSAVE_FTW_FOP] = (uint32_t)(env->fpop << 16) + twd; +    memcpy(&xsave->region[XSAVE_CWD_RIP], &env->fpip, sizeof(env->fpip)); +    memcpy(&xsave->region[XSAVE_CWD_RDP], &env->fpdp, sizeof(env->fpdp)); +    memcpy(&xsave->region[XSAVE_ST_SPACE], env->fpregs, +            sizeof env->fpregs); +    xsave->region[XSAVE_MXCSR] = env->mxcsr; +    *(uint64_t *)&xsave->region[XSAVE_XSTATE_BV] = env->xstate_bv; +    memcpy(&xsave->region[XSAVE_BNDREGS], env->bnd_regs, +            sizeof env->bnd_regs); +    memcpy(&xsave->region[XSAVE_BNDCSR], &env->bndcs_regs, +            sizeof(env->bndcs_regs)); +    memcpy(&xsave->region[XSAVE_OPMASK], env->opmask_regs, +            sizeof env->opmask_regs); + +    xmm = (uint8_t *)&xsave->region[XSAVE_XMM_SPACE]; +    ymmh = (uint8_t *)&xsave->region[XSAVE_YMMH_SPACE]; +    zmmh = (uint8_t *)&xsave->region[XSAVE_ZMM_Hi256]; +    for (i = 0; i < CPU_NB_REGS; i++, xmm += 16, ymmh += 16, zmmh += 32) { +        stq_p(xmm,     env->xmm_regs[i].XMM_Q(0)); +        stq_p(xmm+8,   env->xmm_regs[i].XMM_Q(1)); +        stq_p(ymmh,    env->xmm_regs[i].XMM_Q(2)); +        stq_p(ymmh+8,  env->xmm_regs[i].XMM_Q(3)); +        stq_p(zmmh,    env->xmm_regs[i].XMM_Q(4)); +        stq_p(zmmh+8,  env->xmm_regs[i].XMM_Q(5)); +        stq_p(zmmh+16, env->xmm_regs[i].XMM_Q(6)); +        stq_p(zmmh+24, env->xmm_regs[i].XMM_Q(7)); +    } + +#ifdef TARGET_X86_64 +    memcpy(&xsave->region[XSAVE_Hi16_ZMM], &env->xmm_regs[16], +            16 * sizeof env->xmm_regs[16]); +#endif +    r = kvm_vcpu_ioctl(CPU(cpu), KVM_SET_XSAVE, xsave); +    return r; +} + +static int kvm_put_xcrs(X86CPU *cpu) +{ +    CPUX86State *env = &cpu->env; +    struct kvm_xcrs xcrs = {}; + +    if (!kvm_has_xcrs()) { +        return 0; +    } + +    xcrs.nr_xcrs = 1; +    xcrs.flags = 0; +    xcrs.xcrs[0].xcr = 0; +    xcrs.xcrs[0].value = env->xcr0; +    return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_XCRS, &xcrs); +} + +static int kvm_put_sregs(X86CPU *cpu) +{ +    CPUX86State *env = &cpu->env; +    struct kvm_sregs sregs; + +    memset(sregs.interrupt_bitmap, 0, sizeof(sregs.interrupt_bitmap)); +    if (env->interrupt_injected >= 0) { +        sregs.interrupt_bitmap[env->interrupt_injected / 64] |= +                (uint64_t)1 << (env->interrupt_injected % 64); +    } + +    if ((env->eflags & VM_MASK)) { +        set_v8086_seg(&sregs.cs, &env->segs[R_CS]); +        set_v8086_seg(&sregs.ds, &env->segs[R_DS]); +        set_v8086_seg(&sregs.es, &env->segs[R_ES]); +        set_v8086_seg(&sregs.fs, &env->segs[R_FS]); +        set_v8086_seg(&sregs.gs, &env->segs[R_GS]); +        set_v8086_seg(&sregs.ss, &env->segs[R_SS]); +    } else { +        set_seg(&sregs.cs, &env->segs[R_CS]); +        set_seg(&sregs.ds, &env->segs[R_DS]); +        set_seg(&sregs.es, &env->segs[R_ES]); +        set_seg(&sregs.fs, &env->segs[R_FS]); +        set_seg(&sregs.gs, &env->segs[R_GS]); +        set_seg(&sregs.ss, &env->segs[R_SS]); +    } + +    set_seg(&sregs.tr, &env->tr); +    set_seg(&sregs.ldt, &env->ldt); + +    sregs.idt.limit = env->idt.limit; +    sregs.idt.base = env->idt.base; +    memset(sregs.idt.padding, 0, sizeof sregs.idt.padding); +    sregs.gdt.limit = env->gdt.limit; +    sregs.gdt.base = env->gdt.base; +    memset(sregs.gdt.padding, 0, sizeof sregs.gdt.padding); + +    sregs.cr0 = env->cr[0]; +    sregs.cr2 = env->cr[2]; +    sregs.cr3 = env->cr[3]; +    sregs.cr4 = env->cr[4]; + +    sregs.cr8 = cpu_get_apic_tpr(cpu->apic_state); +    sregs.apic_base = cpu_get_apic_base(cpu->apic_state); + +    sregs.efer = env->efer; + +    return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_SREGS, &sregs); +} + +static void kvm_msr_entry_set(struct kvm_msr_entry *entry, +                              uint32_t index, uint64_t value) +{ +    entry->index = index; +    entry->reserved = 0; +    entry->data = value; +} + +static int kvm_put_tscdeadline_msr(X86CPU *cpu) +{ +    CPUX86State *env = &cpu->env; +    struct { +        struct kvm_msrs info; +        struct kvm_msr_entry entries[1]; +    } msr_data; +    struct kvm_msr_entry *msrs = msr_data.entries; + +    if (!has_msr_tsc_deadline) { +        return 0; +    } + +    kvm_msr_entry_set(&msrs[0], MSR_IA32_TSCDEADLINE, env->tsc_deadline); + +    msr_data.info = (struct kvm_msrs) { +        .nmsrs = 1, +    }; + +    return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_MSRS, &msr_data); +} + +/* + * Provide a separate write service for the feature control MSR in order to + * kick the VCPU out of VMXON or even guest mode on reset. This has to be done + * before writing any other state because forcibly leaving nested mode + * invalidates the VCPU state. + */ +static int kvm_put_msr_feature_control(X86CPU *cpu) +{ +    struct { +        struct kvm_msrs info; +        struct kvm_msr_entry entry; +    } msr_data; + +    kvm_msr_entry_set(&msr_data.entry, MSR_IA32_FEATURE_CONTROL, +                      cpu->env.msr_ia32_feature_control); + +    msr_data.info = (struct kvm_msrs) { +        .nmsrs = 1, +    }; + +    return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_MSRS, &msr_data); +} + +static int kvm_put_msrs(X86CPU *cpu, int level) +{ +    CPUX86State *env = &cpu->env; +    struct { +        struct kvm_msrs info; +        struct kvm_msr_entry entries[150]; +    } msr_data; +    struct kvm_msr_entry *msrs = msr_data.entries; +    int n = 0, i; + +    kvm_msr_entry_set(&msrs[n++], MSR_IA32_SYSENTER_CS, env->sysenter_cs); +    kvm_msr_entry_set(&msrs[n++], MSR_IA32_SYSENTER_ESP, env->sysenter_esp); +    kvm_msr_entry_set(&msrs[n++], MSR_IA32_SYSENTER_EIP, env->sysenter_eip); +    kvm_msr_entry_set(&msrs[n++], MSR_PAT, env->pat); +    if (has_msr_star) { +        kvm_msr_entry_set(&msrs[n++], MSR_STAR, env->star); +    } +    if (has_msr_hsave_pa) { +        kvm_msr_entry_set(&msrs[n++], MSR_VM_HSAVE_PA, env->vm_hsave); +    } +    if (has_msr_tsc_adjust) { +        kvm_msr_entry_set(&msrs[n++], MSR_TSC_ADJUST, env->tsc_adjust); +    } +    if (has_msr_misc_enable) { +        kvm_msr_entry_set(&msrs[n++], MSR_IA32_MISC_ENABLE, +                          env->msr_ia32_misc_enable); +    } +    if (has_msr_smbase) { +        kvm_msr_entry_set(&msrs[n++], MSR_IA32_SMBASE, env->smbase); +    } +    if (has_msr_bndcfgs) { +        kvm_msr_entry_set(&msrs[n++], MSR_IA32_BNDCFGS, env->msr_bndcfgs); +    } +    if (has_msr_xss) { +        kvm_msr_entry_set(&msrs[n++], MSR_IA32_XSS, env->xss); +    } +#ifdef TARGET_X86_64 +    if (lm_capable_kernel) { +        kvm_msr_entry_set(&msrs[n++], MSR_CSTAR, env->cstar); +        kvm_msr_entry_set(&msrs[n++], MSR_KERNELGSBASE, env->kernelgsbase); +        kvm_msr_entry_set(&msrs[n++], MSR_FMASK, env->fmask); +        kvm_msr_entry_set(&msrs[n++], MSR_LSTAR, env->lstar); +    } +#endif +    /* +     * The following MSRs have side effects on the guest or are too heavy +     * for normal writeback. Limit them to reset or full state updates. +     */ +    if (level >= KVM_PUT_RESET_STATE) { +        kvm_msr_entry_set(&msrs[n++], MSR_IA32_TSC, env->tsc); +        kvm_msr_entry_set(&msrs[n++], MSR_KVM_SYSTEM_TIME, +                          env->system_time_msr); +        kvm_msr_entry_set(&msrs[n++], MSR_KVM_WALL_CLOCK, env->wall_clock_msr); +        if (has_msr_async_pf_en) { +            kvm_msr_entry_set(&msrs[n++], MSR_KVM_ASYNC_PF_EN, +                              env->async_pf_en_msr); +        } +        if (has_msr_pv_eoi_en) { +            kvm_msr_entry_set(&msrs[n++], MSR_KVM_PV_EOI_EN, +                              env->pv_eoi_en_msr); +        } +        if (has_msr_kvm_steal_time) { +            kvm_msr_entry_set(&msrs[n++], MSR_KVM_STEAL_TIME, +                              env->steal_time_msr); +        } +        if (has_msr_architectural_pmu) { +            /* Stop the counter.  */ +            kvm_msr_entry_set(&msrs[n++], MSR_CORE_PERF_FIXED_CTR_CTRL, 0); +            kvm_msr_entry_set(&msrs[n++], MSR_CORE_PERF_GLOBAL_CTRL, 0); + +            /* Set the counter values.  */ +            for (i = 0; i < MAX_FIXED_COUNTERS; i++) { +                kvm_msr_entry_set(&msrs[n++], MSR_CORE_PERF_FIXED_CTR0 + i, +                                  env->msr_fixed_counters[i]); +            } +            for (i = 0; i < num_architectural_pmu_counters; i++) { +                kvm_msr_entry_set(&msrs[n++], MSR_P6_PERFCTR0 + i, +                                  env->msr_gp_counters[i]); +                kvm_msr_entry_set(&msrs[n++], MSR_P6_EVNTSEL0 + i, +                                  env->msr_gp_evtsel[i]); +            } +            kvm_msr_entry_set(&msrs[n++], MSR_CORE_PERF_GLOBAL_STATUS, +                              env->msr_global_status); +            kvm_msr_entry_set(&msrs[n++], MSR_CORE_PERF_GLOBAL_OVF_CTRL, +                              env->msr_global_ovf_ctrl); + +            /* Now start the PMU.  */ +            kvm_msr_entry_set(&msrs[n++], MSR_CORE_PERF_FIXED_CTR_CTRL, +                              env->msr_fixed_ctr_ctrl); +            kvm_msr_entry_set(&msrs[n++], MSR_CORE_PERF_GLOBAL_CTRL, +                              env->msr_global_ctrl); +        } +        if (has_msr_hv_hypercall) { +            kvm_msr_entry_set(&msrs[n++], HV_X64_MSR_GUEST_OS_ID, +                              env->msr_hv_guest_os_id); +            kvm_msr_entry_set(&msrs[n++], HV_X64_MSR_HYPERCALL, +                              env->msr_hv_hypercall); +        } +        if (has_msr_hv_vapic) { +            kvm_msr_entry_set(&msrs[n++], HV_X64_MSR_APIC_ASSIST_PAGE, +                              env->msr_hv_vapic); +        } +        if (has_msr_hv_tsc) { +            kvm_msr_entry_set(&msrs[n++], HV_X64_MSR_REFERENCE_TSC, +                              env->msr_hv_tsc); +        } +        if (has_msr_mtrr) { +            kvm_msr_entry_set(&msrs[n++], MSR_MTRRdefType, env->mtrr_deftype); +            kvm_msr_entry_set(&msrs[n++], +                              MSR_MTRRfix64K_00000, env->mtrr_fixed[0]); +            kvm_msr_entry_set(&msrs[n++], +                              MSR_MTRRfix16K_80000, env->mtrr_fixed[1]); +            kvm_msr_entry_set(&msrs[n++], +                              MSR_MTRRfix16K_A0000, env->mtrr_fixed[2]); +            kvm_msr_entry_set(&msrs[n++], +                              MSR_MTRRfix4K_C0000, env->mtrr_fixed[3]); +            kvm_msr_entry_set(&msrs[n++], +                              MSR_MTRRfix4K_C8000, env->mtrr_fixed[4]); +            kvm_msr_entry_set(&msrs[n++], +                              MSR_MTRRfix4K_D0000, env->mtrr_fixed[5]); +            kvm_msr_entry_set(&msrs[n++], +                              MSR_MTRRfix4K_D8000, env->mtrr_fixed[6]); +            kvm_msr_entry_set(&msrs[n++], +                              MSR_MTRRfix4K_E0000, env->mtrr_fixed[7]); +            kvm_msr_entry_set(&msrs[n++], +                              MSR_MTRRfix4K_E8000, env->mtrr_fixed[8]); +            kvm_msr_entry_set(&msrs[n++], +                              MSR_MTRRfix4K_F0000, env->mtrr_fixed[9]); +            kvm_msr_entry_set(&msrs[n++], +                              MSR_MTRRfix4K_F8000, env->mtrr_fixed[10]); +            for (i = 0; i < MSR_MTRRcap_VCNT; i++) { +                kvm_msr_entry_set(&msrs[n++], +                                  MSR_MTRRphysBase(i), env->mtrr_var[i].base); +                kvm_msr_entry_set(&msrs[n++], +                                  MSR_MTRRphysMask(i), env->mtrr_var[i].mask); +            } +        } + +        /* Note: MSR_IA32_FEATURE_CONTROL is written separately, see +         *       kvm_put_msr_feature_control. */ +    } +    if (env->mcg_cap) { +        int i; + +        kvm_msr_entry_set(&msrs[n++], MSR_MCG_STATUS, env->mcg_status); +        kvm_msr_entry_set(&msrs[n++], MSR_MCG_CTL, env->mcg_ctl); +        for (i = 0; i < (env->mcg_cap & 0xff) * 4; i++) { +            kvm_msr_entry_set(&msrs[n++], MSR_MC0_CTL + i, env->mce_banks[i]); +        } +    } + +    msr_data.info = (struct kvm_msrs) { +        .nmsrs = n, +    }; + +    return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_MSRS, &msr_data); + +} + + +static int kvm_get_fpu(X86CPU *cpu) +{ +    CPUX86State *env = &cpu->env; +    struct kvm_fpu fpu; +    int i, ret; + +    ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_FPU, &fpu); +    if (ret < 0) { +        return ret; +    } + +    env->fpstt = (fpu.fsw >> 11) & 7; +    env->fpus = fpu.fsw; +    env->fpuc = fpu.fcw; +    env->fpop = fpu.last_opcode; +    env->fpip = fpu.last_ip; +    env->fpdp = fpu.last_dp; +    for (i = 0; i < 8; ++i) { +        env->fptags[i] = !((fpu.ftwx >> i) & 1); +    } +    memcpy(env->fpregs, fpu.fpr, sizeof env->fpregs); +    for (i = 0; i < CPU_NB_REGS; i++) { +        env->xmm_regs[i].XMM_Q(0) = ldq_p(&fpu.xmm[i][0]); +        env->xmm_regs[i].XMM_Q(1) = ldq_p(&fpu.xmm[i][8]); +    } +    env->mxcsr = fpu.mxcsr; + +    return 0; +} + +static int kvm_get_xsave(X86CPU *cpu) +{ +    CPUX86State *env = &cpu->env; +    struct kvm_xsave* xsave = env->kvm_xsave_buf; +    int ret, i; +    const uint8_t *xmm, *ymmh, *zmmh; +    uint16_t cwd, swd, twd; + +    if (!kvm_has_xsave()) { +        return kvm_get_fpu(cpu); +    } + +    ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_XSAVE, xsave); +    if (ret < 0) { +        return ret; +    } + +    cwd = (uint16_t)xsave->region[XSAVE_FCW_FSW]; +    swd = (uint16_t)(xsave->region[XSAVE_FCW_FSW] >> 16); +    twd = (uint16_t)xsave->region[XSAVE_FTW_FOP]; +    env->fpop = (uint16_t)(xsave->region[XSAVE_FTW_FOP] >> 16); +    env->fpstt = (swd >> 11) & 7; +    env->fpus = swd; +    env->fpuc = cwd; +    for (i = 0; i < 8; ++i) { +        env->fptags[i] = !((twd >> i) & 1); +    } +    memcpy(&env->fpip, &xsave->region[XSAVE_CWD_RIP], sizeof(env->fpip)); +    memcpy(&env->fpdp, &xsave->region[XSAVE_CWD_RDP], sizeof(env->fpdp)); +    env->mxcsr = xsave->region[XSAVE_MXCSR]; +    memcpy(env->fpregs, &xsave->region[XSAVE_ST_SPACE], +            sizeof env->fpregs); +    env->xstate_bv = *(uint64_t *)&xsave->region[XSAVE_XSTATE_BV]; +    memcpy(env->bnd_regs, &xsave->region[XSAVE_BNDREGS], +            sizeof env->bnd_regs); +    memcpy(&env->bndcs_regs, &xsave->region[XSAVE_BNDCSR], +            sizeof(env->bndcs_regs)); +    memcpy(env->opmask_regs, &xsave->region[XSAVE_OPMASK], +            sizeof env->opmask_regs); + +    xmm = (const uint8_t *)&xsave->region[XSAVE_XMM_SPACE]; +    ymmh = (const uint8_t *)&xsave->region[XSAVE_YMMH_SPACE]; +    zmmh = (const uint8_t *)&xsave->region[XSAVE_ZMM_Hi256]; +    for (i = 0; i < CPU_NB_REGS; i++, xmm += 16, ymmh += 16, zmmh += 32) { +        env->xmm_regs[i].XMM_Q(0) = ldq_p(xmm); +        env->xmm_regs[i].XMM_Q(1) = ldq_p(xmm+8); +        env->xmm_regs[i].XMM_Q(2) = ldq_p(ymmh); +        env->xmm_regs[i].XMM_Q(3) = ldq_p(ymmh+8); +        env->xmm_regs[i].XMM_Q(4) = ldq_p(zmmh); +        env->xmm_regs[i].XMM_Q(5) = ldq_p(zmmh+8); +        env->xmm_regs[i].XMM_Q(6) = ldq_p(zmmh+16); +        env->xmm_regs[i].XMM_Q(7) = ldq_p(zmmh+24); +    } + +#ifdef TARGET_X86_64 +    memcpy(&env->xmm_regs[16], &xsave->region[XSAVE_Hi16_ZMM], +           16 * sizeof env->xmm_regs[16]); +#endif +    return 0; +} + +static int kvm_get_xcrs(X86CPU *cpu) +{ +    CPUX86State *env = &cpu->env; +    int i, ret; +    struct kvm_xcrs xcrs; + +    if (!kvm_has_xcrs()) { +        return 0; +    } + +    ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_XCRS, &xcrs); +    if (ret < 0) { +        return ret; +    } + +    for (i = 0; i < xcrs.nr_xcrs; i++) { +        /* Only support xcr0 now */ +        if (xcrs.xcrs[i].xcr == 0) { +            env->xcr0 = xcrs.xcrs[i].value; +            break; +        } +    } +    return 0; +} + +static int kvm_get_sregs(X86CPU *cpu) +{ +    CPUX86State *env = &cpu->env; +    struct kvm_sregs sregs; +    uint32_t hflags; +    int bit, i, ret; + +    ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_SREGS, &sregs); +    if (ret < 0) { +        return ret; +    } + +    /* There can only be one pending IRQ set in the bitmap at a time, so try +       to find it and save its number instead (-1 for none). */ +    env->interrupt_injected = -1; +    for (i = 0; i < ARRAY_SIZE(sregs.interrupt_bitmap); i++) { +        if (sregs.interrupt_bitmap[i]) { +            bit = ctz64(sregs.interrupt_bitmap[i]); +            env->interrupt_injected = i * 64 + bit; +            break; +        } +    } + +    get_seg(&env->segs[R_CS], &sregs.cs); +    get_seg(&env->segs[R_DS], &sregs.ds); +    get_seg(&env->segs[R_ES], &sregs.es); +    get_seg(&env->segs[R_FS], &sregs.fs); +    get_seg(&env->segs[R_GS], &sregs.gs); +    get_seg(&env->segs[R_SS], &sregs.ss); + +    get_seg(&env->tr, &sregs.tr); +    get_seg(&env->ldt, &sregs.ldt); + +    env->idt.limit = sregs.idt.limit; +    env->idt.base = sregs.idt.base; +    env->gdt.limit = sregs.gdt.limit; +    env->gdt.base = sregs.gdt.base; + +    env->cr[0] = sregs.cr0; +    env->cr[2] = sregs.cr2; +    env->cr[3] = sregs.cr3; +    env->cr[4] = sregs.cr4; + +    env->efer = sregs.efer; + +    /* changes to apic base and cr8/tpr are read back via kvm_arch_post_run */ + +#define HFLAG_COPY_MASK \ +    ~( HF_CPL_MASK | HF_PE_MASK | HF_MP_MASK | HF_EM_MASK | \ +       HF_TS_MASK | HF_TF_MASK | HF_VM_MASK | HF_IOPL_MASK | \ +       HF_OSFXSR_MASK | HF_LMA_MASK | HF_CS32_MASK | \ +       HF_SS32_MASK | HF_CS64_MASK | HF_ADDSEG_MASK) + +    hflags = (env->segs[R_SS].flags >> DESC_DPL_SHIFT) & HF_CPL_MASK; +    hflags |= (env->cr[0] & CR0_PE_MASK) << (HF_PE_SHIFT - CR0_PE_SHIFT); +    hflags |= (env->cr[0] << (HF_MP_SHIFT - CR0_MP_SHIFT)) & +                (HF_MP_MASK | HF_EM_MASK | HF_TS_MASK); +    hflags |= (env->eflags & (HF_TF_MASK | HF_VM_MASK | HF_IOPL_MASK)); +    hflags |= (env->cr[4] & CR4_OSFXSR_MASK) << +                (HF_OSFXSR_SHIFT - CR4_OSFXSR_SHIFT); + +    if (env->efer & MSR_EFER_LMA) { +        hflags |= HF_LMA_MASK; +    } + +    if ((hflags & HF_LMA_MASK) && (env->segs[R_CS].flags & DESC_L_MASK)) { +        hflags |= HF_CS32_MASK | HF_SS32_MASK | HF_CS64_MASK; +    } else { +        hflags |= (env->segs[R_CS].flags & DESC_B_MASK) >> +                    (DESC_B_SHIFT - HF_CS32_SHIFT); +        hflags |= (env->segs[R_SS].flags & DESC_B_MASK) >> +                    (DESC_B_SHIFT - HF_SS32_SHIFT); +        if (!(env->cr[0] & CR0_PE_MASK) || (env->eflags & VM_MASK) || +            !(hflags & HF_CS32_MASK)) { +            hflags |= HF_ADDSEG_MASK; +        } else { +            hflags |= ((env->segs[R_DS].base | env->segs[R_ES].base | +                        env->segs[R_SS].base) != 0) << HF_ADDSEG_SHIFT; +        } +    } +    env->hflags = (env->hflags & HFLAG_COPY_MASK) | hflags; + +    return 0; +} + +static int kvm_get_msrs(X86CPU *cpu) +{ +    CPUX86State *env = &cpu->env; +    struct { +        struct kvm_msrs info; +        struct kvm_msr_entry entries[150]; +    } msr_data; +    struct kvm_msr_entry *msrs = msr_data.entries; +    int ret, i, n; + +    n = 0; +    msrs[n++].index = MSR_IA32_SYSENTER_CS; +    msrs[n++].index = MSR_IA32_SYSENTER_ESP; +    msrs[n++].index = MSR_IA32_SYSENTER_EIP; +    msrs[n++].index = MSR_PAT; +    if (has_msr_star) { +        msrs[n++].index = MSR_STAR; +    } +    if (has_msr_hsave_pa) { +        msrs[n++].index = MSR_VM_HSAVE_PA; +    } +    if (has_msr_tsc_adjust) { +        msrs[n++].index = MSR_TSC_ADJUST; +    } +    if (has_msr_tsc_deadline) { +        msrs[n++].index = MSR_IA32_TSCDEADLINE; +    } +    if (has_msr_misc_enable) { +        msrs[n++].index = MSR_IA32_MISC_ENABLE; +    } +    if (has_msr_smbase) { +        msrs[n++].index = MSR_IA32_SMBASE; +    } +    if (has_msr_feature_control) { +        msrs[n++].index = MSR_IA32_FEATURE_CONTROL; +    } +    if (has_msr_bndcfgs) { +        msrs[n++].index = MSR_IA32_BNDCFGS; +    } +    if (has_msr_xss) { +        msrs[n++].index = MSR_IA32_XSS; +    } + + +    if (!env->tsc_valid) { +        msrs[n++].index = MSR_IA32_TSC; +        env->tsc_valid = !runstate_is_running(); +    } + +#ifdef TARGET_X86_64 +    if (lm_capable_kernel) { +        msrs[n++].index = MSR_CSTAR; +        msrs[n++].index = MSR_KERNELGSBASE; +        msrs[n++].index = MSR_FMASK; +        msrs[n++].index = MSR_LSTAR; +    } +#endif +    msrs[n++].index = MSR_KVM_SYSTEM_TIME; +    msrs[n++].index = MSR_KVM_WALL_CLOCK; +    if (has_msr_async_pf_en) { +        msrs[n++].index = MSR_KVM_ASYNC_PF_EN; +    } +    if (has_msr_pv_eoi_en) { +        msrs[n++].index = MSR_KVM_PV_EOI_EN; +    } +    if (has_msr_kvm_steal_time) { +        msrs[n++].index = MSR_KVM_STEAL_TIME; +    } +    if (has_msr_architectural_pmu) { +        msrs[n++].index = MSR_CORE_PERF_FIXED_CTR_CTRL; +        msrs[n++].index = MSR_CORE_PERF_GLOBAL_CTRL; +        msrs[n++].index = MSR_CORE_PERF_GLOBAL_STATUS; +        msrs[n++].index = MSR_CORE_PERF_GLOBAL_OVF_CTRL; +        for (i = 0; i < MAX_FIXED_COUNTERS; i++) { +            msrs[n++].index = MSR_CORE_PERF_FIXED_CTR0 + i; +        } +        for (i = 0; i < num_architectural_pmu_counters; i++) { +            msrs[n++].index = MSR_P6_PERFCTR0 + i; +            msrs[n++].index = MSR_P6_EVNTSEL0 + i; +        } +    } + +    if (env->mcg_cap) { +        msrs[n++].index = MSR_MCG_STATUS; +        msrs[n++].index = MSR_MCG_CTL; +        for (i = 0; i < (env->mcg_cap & 0xff) * 4; i++) { +            msrs[n++].index = MSR_MC0_CTL + i; +        } +    } + +    if (has_msr_hv_hypercall) { +        msrs[n++].index = HV_X64_MSR_HYPERCALL; +        msrs[n++].index = HV_X64_MSR_GUEST_OS_ID; +    } +    if (has_msr_hv_vapic) { +        msrs[n++].index = HV_X64_MSR_APIC_ASSIST_PAGE; +    } +    if (has_msr_hv_tsc) { +        msrs[n++].index = HV_X64_MSR_REFERENCE_TSC; +    } +    if (has_msr_mtrr) { +        msrs[n++].index = MSR_MTRRdefType; +        msrs[n++].index = MSR_MTRRfix64K_00000; +        msrs[n++].index = MSR_MTRRfix16K_80000; +        msrs[n++].index = MSR_MTRRfix16K_A0000; +        msrs[n++].index = MSR_MTRRfix4K_C0000; +        msrs[n++].index = MSR_MTRRfix4K_C8000; +        msrs[n++].index = MSR_MTRRfix4K_D0000; +        msrs[n++].index = MSR_MTRRfix4K_D8000; +        msrs[n++].index = MSR_MTRRfix4K_E0000; +        msrs[n++].index = MSR_MTRRfix4K_E8000; +        msrs[n++].index = MSR_MTRRfix4K_F0000; +        msrs[n++].index = MSR_MTRRfix4K_F8000; +        for (i = 0; i < MSR_MTRRcap_VCNT; i++) { +            msrs[n++].index = MSR_MTRRphysBase(i); +            msrs[n++].index = MSR_MTRRphysMask(i); +        } +    } + +    msr_data.info = (struct kvm_msrs) { +        .nmsrs = n, +    }; + +    ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_MSRS, &msr_data); +    if (ret < 0) { +        return ret; +    } + +    for (i = 0; i < ret; i++) { +        uint32_t index = msrs[i].index; +        switch (index) { +        case MSR_IA32_SYSENTER_CS: +            env->sysenter_cs = msrs[i].data; +            break; +        case MSR_IA32_SYSENTER_ESP: +            env->sysenter_esp = msrs[i].data; +            break; +        case MSR_IA32_SYSENTER_EIP: +            env->sysenter_eip = msrs[i].data; +            break; +        case MSR_PAT: +            env->pat = msrs[i].data; +            break; +        case MSR_STAR: +            env->star = msrs[i].data; +            break; +#ifdef TARGET_X86_64 +        case MSR_CSTAR: +            env->cstar = msrs[i].data; +            break; +        case MSR_KERNELGSBASE: +            env->kernelgsbase = msrs[i].data; +            break; +        case MSR_FMASK: +            env->fmask = msrs[i].data; +            break; +        case MSR_LSTAR: +            env->lstar = msrs[i].data; +            break; +#endif +        case MSR_IA32_TSC: +            env->tsc = msrs[i].data; +            break; +        case MSR_TSC_ADJUST: +            env->tsc_adjust = msrs[i].data; +            break; +        case MSR_IA32_TSCDEADLINE: +            env->tsc_deadline = msrs[i].data; +            break; +        case MSR_VM_HSAVE_PA: +            env->vm_hsave = msrs[i].data; +            break; +        case MSR_KVM_SYSTEM_TIME: +            env->system_time_msr = msrs[i].data; +            break; +        case MSR_KVM_WALL_CLOCK: +            env->wall_clock_msr = msrs[i].data; +            break; +        case MSR_MCG_STATUS: +            env->mcg_status = msrs[i].data; +            break; +        case MSR_MCG_CTL: +            env->mcg_ctl = msrs[i].data; +            break; +        case MSR_IA32_MISC_ENABLE: +            env->msr_ia32_misc_enable = msrs[i].data; +            break; +        case MSR_IA32_SMBASE: +            env->smbase = msrs[i].data; +            break; +        case MSR_IA32_FEATURE_CONTROL: +            env->msr_ia32_feature_control = msrs[i].data; +            break; +        case MSR_IA32_BNDCFGS: +            env->msr_bndcfgs = msrs[i].data; +            break; +        case MSR_IA32_XSS: +            env->xss = msrs[i].data; +            break; +        default: +            if (msrs[i].index >= MSR_MC0_CTL && +                msrs[i].index < MSR_MC0_CTL + (env->mcg_cap & 0xff) * 4) { +                env->mce_banks[msrs[i].index - MSR_MC0_CTL] = msrs[i].data; +            } +            break; +        case MSR_KVM_ASYNC_PF_EN: +            env->async_pf_en_msr = msrs[i].data; +            break; +        case MSR_KVM_PV_EOI_EN: +            env->pv_eoi_en_msr = msrs[i].data; +            break; +        case MSR_KVM_STEAL_TIME: +            env->steal_time_msr = msrs[i].data; +            break; +        case MSR_CORE_PERF_FIXED_CTR_CTRL: +            env->msr_fixed_ctr_ctrl = msrs[i].data; +            break; +        case MSR_CORE_PERF_GLOBAL_CTRL: +            env->msr_global_ctrl = msrs[i].data; +            break; +        case MSR_CORE_PERF_GLOBAL_STATUS: +            env->msr_global_status = msrs[i].data; +            break; +        case MSR_CORE_PERF_GLOBAL_OVF_CTRL: +            env->msr_global_ovf_ctrl = msrs[i].data; +            break; +        case MSR_CORE_PERF_FIXED_CTR0 ... MSR_CORE_PERF_FIXED_CTR0 + MAX_FIXED_COUNTERS - 1: +            env->msr_fixed_counters[index - MSR_CORE_PERF_FIXED_CTR0] = msrs[i].data; +            break; +        case MSR_P6_PERFCTR0 ... MSR_P6_PERFCTR0 + MAX_GP_COUNTERS - 1: +            env->msr_gp_counters[index - MSR_P6_PERFCTR0] = msrs[i].data; +            break; +        case MSR_P6_EVNTSEL0 ... MSR_P6_EVNTSEL0 + MAX_GP_COUNTERS - 1: +            env->msr_gp_evtsel[index - MSR_P6_EVNTSEL0] = msrs[i].data; +            break; +        case HV_X64_MSR_HYPERCALL: +            env->msr_hv_hypercall = msrs[i].data; +            break; +        case HV_X64_MSR_GUEST_OS_ID: +            env->msr_hv_guest_os_id = msrs[i].data; +            break; +        case HV_X64_MSR_APIC_ASSIST_PAGE: +            env->msr_hv_vapic = msrs[i].data; +            break; +        case HV_X64_MSR_REFERENCE_TSC: +            env->msr_hv_tsc = msrs[i].data; +            break; +        case MSR_MTRRdefType: +            env->mtrr_deftype = msrs[i].data; +            break; +        case MSR_MTRRfix64K_00000: +            env->mtrr_fixed[0] = msrs[i].data; +            break; +        case MSR_MTRRfix16K_80000: +            env->mtrr_fixed[1] = msrs[i].data; +            break; +        case MSR_MTRRfix16K_A0000: +            env->mtrr_fixed[2] = msrs[i].data; +            break; +        case MSR_MTRRfix4K_C0000: +            env->mtrr_fixed[3] = msrs[i].data; +            break; +        case MSR_MTRRfix4K_C8000: +            env->mtrr_fixed[4] = msrs[i].data; +            break; +        case MSR_MTRRfix4K_D0000: +            env->mtrr_fixed[5] = msrs[i].data; +            break; +        case MSR_MTRRfix4K_D8000: +            env->mtrr_fixed[6] = msrs[i].data; +            break; +        case MSR_MTRRfix4K_E0000: +            env->mtrr_fixed[7] = msrs[i].data; +            break; +        case MSR_MTRRfix4K_E8000: +            env->mtrr_fixed[8] = msrs[i].data; +            break; +        case MSR_MTRRfix4K_F0000: +            env->mtrr_fixed[9] = msrs[i].data; +            break; +        case MSR_MTRRfix4K_F8000: +            env->mtrr_fixed[10] = msrs[i].data; +            break; +        case MSR_MTRRphysBase(0) ... MSR_MTRRphysMask(MSR_MTRRcap_VCNT - 1): +            if (index & 1) { +                env->mtrr_var[MSR_MTRRphysIndex(index)].mask = msrs[i].data; +            } else { +                env->mtrr_var[MSR_MTRRphysIndex(index)].base = msrs[i].data; +            } +            break; +        } +    } + +    return 0; +} + +static int kvm_put_mp_state(X86CPU *cpu) +{ +    struct kvm_mp_state mp_state = { .mp_state = cpu->env.mp_state }; + +    return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_MP_STATE, &mp_state); +} + +static int kvm_get_mp_state(X86CPU *cpu) +{ +    CPUState *cs = CPU(cpu); +    CPUX86State *env = &cpu->env; +    struct kvm_mp_state mp_state; +    int ret; + +    ret = kvm_vcpu_ioctl(cs, KVM_GET_MP_STATE, &mp_state); +    if (ret < 0) { +        return ret; +    } +    env->mp_state = mp_state.mp_state; +    if (kvm_irqchip_in_kernel()) { +        cs->halted = (mp_state.mp_state == KVM_MP_STATE_HALTED); +    } +    return 0; +} + +static int kvm_get_apic(X86CPU *cpu) +{ +    DeviceState *apic = cpu->apic_state; +    struct kvm_lapic_state kapic; +    int ret; + +    if (apic && kvm_irqchip_in_kernel()) { +        ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_LAPIC, &kapic); +        if (ret < 0) { +            return ret; +        } + +        kvm_get_apic_state(apic, &kapic); +    } +    return 0; +} + +static int kvm_put_apic(X86CPU *cpu) +{ +    DeviceState *apic = cpu->apic_state; +    struct kvm_lapic_state kapic; + +    if (apic && kvm_irqchip_in_kernel()) { +        kvm_put_apic_state(apic, &kapic); + +        return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_LAPIC, &kapic); +    } +    return 0; +} + +static int kvm_put_vcpu_events(X86CPU *cpu, int level) +{ +    CPUState *cs = CPU(cpu); +    CPUX86State *env = &cpu->env; +    struct kvm_vcpu_events events = {}; + +    if (!kvm_has_vcpu_events()) { +        return 0; +    } + +    events.exception.injected = (env->exception_injected >= 0); +    events.exception.nr = env->exception_injected; +    events.exception.has_error_code = env->has_error_code; +    events.exception.error_code = env->error_code; +    events.exception.pad = 0; + +    events.interrupt.injected = (env->interrupt_injected >= 0); +    events.interrupt.nr = env->interrupt_injected; +    events.interrupt.soft = env->soft_interrupt; + +    events.nmi.injected = env->nmi_injected; +    events.nmi.pending = env->nmi_pending; +    events.nmi.masked = !!(env->hflags2 & HF2_NMI_MASK); +    events.nmi.pad = 0; + +    events.sipi_vector = env->sipi_vector; + +    if (has_msr_smbase) { +        events.smi.smm = !!(env->hflags & HF_SMM_MASK); +        events.smi.smm_inside_nmi = !!(env->hflags2 & HF2_SMM_INSIDE_NMI_MASK); +        if (kvm_irqchip_in_kernel()) { +            /* As soon as these are moved to the kernel, remove them +             * from cs->interrupt_request. +             */ +            events.smi.pending = cs->interrupt_request & CPU_INTERRUPT_SMI; +            events.smi.latched_init = cs->interrupt_request & CPU_INTERRUPT_INIT; +            cs->interrupt_request &= ~(CPU_INTERRUPT_INIT | CPU_INTERRUPT_SMI); +        } else { +            /* Keep these in cs->interrupt_request.  */ +            events.smi.pending = 0; +            events.smi.latched_init = 0; +        } +        events.flags |= KVM_VCPUEVENT_VALID_SMM; +    } + +    events.flags = 0; +    if (level >= KVM_PUT_RESET_STATE) { +        events.flags |= +            KVM_VCPUEVENT_VALID_NMI_PENDING | KVM_VCPUEVENT_VALID_SIPI_VECTOR; +    } + +    return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_VCPU_EVENTS, &events); +} + +static int kvm_get_vcpu_events(X86CPU *cpu) +{ +    CPUX86State *env = &cpu->env; +    struct kvm_vcpu_events events; +    int ret; + +    if (!kvm_has_vcpu_events()) { +        return 0; +    } + +    memset(&events, 0, sizeof(events)); +    ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_VCPU_EVENTS, &events); +    if (ret < 0) { +       return ret; +    } +    env->exception_injected = +       events.exception.injected ? events.exception.nr : -1; +    env->has_error_code = events.exception.has_error_code; +    env->error_code = events.exception.error_code; + +    env->interrupt_injected = +        events.interrupt.injected ? events.interrupt.nr : -1; +    env->soft_interrupt = events.interrupt.soft; + +    env->nmi_injected = events.nmi.injected; +    env->nmi_pending = events.nmi.pending; +    if (events.nmi.masked) { +        env->hflags2 |= HF2_NMI_MASK; +    } else { +        env->hflags2 &= ~HF2_NMI_MASK; +    } + +    if (events.flags & KVM_VCPUEVENT_VALID_SMM) { +        if (events.smi.smm) { +            env->hflags |= HF_SMM_MASK; +        } else { +            env->hflags &= ~HF_SMM_MASK; +        } +        if (events.smi.pending) { +            cpu_interrupt(CPU(cpu), CPU_INTERRUPT_SMI); +        } else { +            cpu_reset_interrupt(CPU(cpu), CPU_INTERRUPT_SMI); +        } +        if (events.smi.smm_inside_nmi) { +            env->hflags2 |= HF2_SMM_INSIDE_NMI_MASK; +        } else { +            env->hflags2 &= ~HF2_SMM_INSIDE_NMI_MASK; +        } +        if (events.smi.latched_init) { +            cpu_interrupt(CPU(cpu), CPU_INTERRUPT_INIT); +        } else { +            cpu_reset_interrupt(CPU(cpu), CPU_INTERRUPT_INIT); +        } +    } + +    env->sipi_vector = events.sipi_vector; + +    return 0; +} + +static int kvm_guest_debug_workarounds(X86CPU *cpu) +{ +    CPUState *cs = CPU(cpu); +    CPUX86State *env = &cpu->env; +    int ret = 0; +    unsigned long reinject_trap = 0; + +    if (!kvm_has_vcpu_events()) { +        if (env->exception_injected == 1) { +            reinject_trap = KVM_GUESTDBG_INJECT_DB; +        } else if (env->exception_injected == 3) { +            reinject_trap = KVM_GUESTDBG_INJECT_BP; +        } +        env->exception_injected = -1; +    } + +    /* +     * Kernels before KVM_CAP_X86_ROBUST_SINGLESTEP overwrote flags.TF +     * injected via SET_GUEST_DEBUG while updating GP regs. Work around this +     * by updating the debug state once again if single-stepping is on. +     * Another reason to call kvm_update_guest_debug here is a pending debug +     * trap raise by the guest. On kernels without SET_VCPU_EVENTS we have to +     * reinject them via SET_GUEST_DEBUG. +     */ +    if (reinject_trap || +        (!kvm_has_robust_singlestep() && cs->singlestep_enabled)) { +        ret = kvm_update_guest_debug(cs, reinject_trap); +    } +    return ret; +} + +static int kvm_put_debugregs(X86CPU *cpu) +{ +    CPUX86State *env = &cpu->env; +    struct kvm_debugregs dbgregs; +    int i; + +    if (!kvm_has_debugregs()) { +        return 0; +    } + +    for (i = 0; i < 4; i++) { +        dbgregs.db[i] = env->dr[i]; +    } +    dbgregs.dr6 = env->dr[6]; +    dbgregs.dr7 = env->dr[7]; +    dbgregs.flags = 0; + +    return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_DEBUGREGS, &dbgregs); +} + +static int kvm_get_debugregs(X86CPU *cpu) +{ +    CPUX86State *env = &cpu->env; +    struct kvm_debugregs dbgregs; +    int i, ret; + +    if (!kvm_has_debugregs()) { +        return 0; +    } + +    ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_DEBUGREGS, &dbgregs); +    if (ret < 0) { +        return ret; +    } +    for (i = 0; i < 4; i++) { +        env->dr[i] = dbgregs.db[i]; +    } +    env->dr[4] = env->dr[6] = dbgregs.dr6; +    env->dr[5] = env->dr[7] = dbgregs.dr7; + +    return 0; +} + +int kvm_arch_put_registers(CPUState *cpu, int level) +{ +    X86CPU *x86_cpu = X86_CPU(cpu); +    int ret; + +    assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu)); + +    if (level >= KVM_PUT_RESET_STATE && has_msr_feature_control) { +        ret = kvm_put_msr_feature_control(x86_cpu); +        if (ret < 0) { +            return ret; +        } +    } + +    ret = kvm_getput_regs(x86_cpu, 1); +    if (ret < 0) { +        return ret; +    } +    ret = kvm_put_xsave(x86_cpu); +    if (ret < 0) { +        return ret; +    } +    ret = kvm_put_xcrs(x86_cpu); +    if (ret < 0) { +        return ret; +    } +    ret = kvm_put_sregs(x86_cpu); +    if (ret < 0) { +        return ret; +    } +    /* must be before kvm_put_msrs */ +    ret = kvm_inject_mce_oldstyle(x86_cpu); +    if (ret < 0) { +        return ret; +    } +    ret = kvm_put_msrs(x86_cpu, level); +    if (ret < 0) { +        return ret; +    } +    if (level >= KVM_PUT_RESET_STATE) { +        ret = kvm_put_mp_state(x86_cpu); +        if (ret < 0) { +            return ret; +        } +        ret = kvm_put_apic(x86_cpu); +        if (ret < 0) { +            return ret; +        } +    } + +    ret = kvm_put_tscdeadline_msr(x86_cpu); +    if (ret < 0) { +        return ret; +    } + +    ret = kvm_put_vcpu_events(x86_cpu, level); +    if (ret < 0) { +        return ret; +    } +    ret = kvm_put_debugregs(x86_cpu); +    if (ret < 0) { +        return ret; +    } +    /* must be last */ +    ret = kvm_guest_debug_workarounds(x86_cpu); +    if (ret < 0) { +        return ret; +    } +    return 0; +} + +int kvm_arch_get_registers(CPUState *cs) +{ +    X86CPU *cpu = X86_CPU(cs); +    int ret; + +    assert(cpu_is_stopped(cs) || qemu_cpu_is_self(cs)); + +    ret = kvm_getput_regs(cpu, 0); +    if (ret < 0) { +        return ret; +    } +    ret = kvm_get_xsave(cpu); +    if (ret < 0) { +        return ret; +    } +    ret = kvm_get_xcrs(cpu); +    if (ret < 0) { +        return ret; +    } +    ret = kvm_get_sregs(cpu); +    if (ret < 0) { +        return ret; +    } +    ret = kvm_get_msrs(cpu); +    if (ret < 0) { +        return ret; +    } +    ret = kvm_get_mp_state(cpu); +    if (ret < 0) { +        return ret; +    } +    ret = kvm_get_apic(cpu); +    if (ret < 0) { +        return ret; +    } +    ret = kvm_get_vcpu_events(cpu); +    if (ret < 0) { +        return ret; +    } +    ret = kvm_get_debugregs(cpu); +    if (ret < 0) { +        return ret; +    } +    return 0; +} + +void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) +{ +    X86CPU *x86_cpu = X86_CPU(cpu); +    CPUX86State *env = &x86_cpu->env; +    int ret; + +    /* Inject NMI */ +    if (cpu->interrupt_request & (CPU_INTERRUPT_NMI | CPU_INTERRUPT_SMI)) { +        if (cpu->interrupt_request & CPU_INTERRUPT_NMI) { +            qemu_mutex_lock_iothread(); +            cpu->interrupt_request &= ~CPU_INTERRUPT_NMI; +            qemu_mutex_unlock_iothread(); +            DPRINTF("injected NMI\n"); +            ret = kvm_vcpu_ioctl(cpu, KVM_NMI); +            if (ret < 0) { +                fprintf(stderr, "KVM: injection failed, NMI lost (%s)\n", +                        strerror(-ret)); +            } +        } +        if (cpu->interrupt_request & CPU_INTERRUPT_SMI) { +            qemu_mutex_lock_iothread(); +            cpu->interrupt_request &= ~CPU_INTERRUPT_SMI; +            qemu_mutex_unlock_iothread(); +            DPRINTF("injected SMI\n"); +            ret = kvm_vcpu_ioctl(cpu, KVM_SMI); +            if (ret < 0) { +                fprintf(stderr, "KVM: injection failed, SMI lost (%s)\n", +                        strerror(-ret)); +            } +        } +    } + +    if (!kvm_irqchip_in_kernel()) { +        qemu_mutex_lock_iothread(); +    } + +    /* Force the VCPU out of its inner loop to process any INIT requests +     * or (for userspace APIC, but it is cheap to combine the checks here) +     * pending TPR access reports. +     */ +    if (cpu->interrupt_request & (CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) { +        if ((cpu->interrupt_request & CPU_INTERRUPT_INIT) && +            !(env->hflags & HF_SMM_MASK)) { +            cpu->exit_request = 1; +        } +        if (cpu->interrupt_request & CPU_INTERRUPT_TPR) { +            cpu->exit_request = 1; +        } +    } + +    if (!kvm_irqchip_in_kernel()) { +        /* Try to inject an interrupt if the guest can accept it */ +        if (run->ready_for_interrupt_injection && +            (cpu->interrupt_request & CPU_INTERRUPT_HARD) && +            (env->eflags & IF_MASK)) { +            int irq; + +            cpu->interrupt_request &= ~CPU_INTERRUPT_HARD; +            irq = cpu_get_pic_interrupt(env); +            if (irq >= 0) { +                struct kvm_interrupt intr; + +                intr.irq = irq; +                DPRINTF("injected interrupt %d\n", irq); +                ret = kvm_vcpu_ioctl(cpu, KVM_INTERRUPT, &intr); +                if (ret < 0) { +                    fprintf(stderr, +                            "KVM: injection failed, interrupt lost (%s)\n", +                            strerror(-ret)); +                } +            } +        } + +        /* If we have an interrupt but the guest is not ready to receive an +         * interrupt, request an interrupt window exit.  This will +         * cause a return to userspace as soon as the guest is ready to +         * receive interrupts. */ +        if ((cpu->interrupt_request & CPU_INTERRUPT_HARD)) { +            run->request_interrupt_window = 1; +        } else { +            run->request_interrupt_window = 0; +        } + +        DPRINTF("setting tpr\n"); +        run->cr8 = cpu_get_apic_tpr(x86_cpu->apic_state); + +        qemu_mutex_unlock_iothread(); +    } +} + +MemTxAttrs kvm_arch_post_run(CPUState *cpu, struct kvm_run *run) +{ +    X86CPU *x86_cpu = X86_CPU(cpu); +    CPUX86State *env = &x86_cpu->env; + +    if (run->flags & KVM_RUN_X86_SMM) { +        env->hflags |= HF_SMM_MASK; +    } else { +        env->hflags &= HF_SMM_MASK; +    } +    if (run->if_flag) { +        env->eflags |= IF_MASK; +    } else { +        env->eflags &= ~IF_MASK; +    } + +    /* We need to protect the apic state against concurrent accesses from +     * different threads in case the userspace irqchip is used. */ +    if (!kvm_irqchip_in_kernel()) { +        qemu_mutex_lock_iothread(); +    } +    cpu_set_apic_tpr(x86_cpu->apic_state, run->cr8); +    cpu_set_apic_base(x86_cpu->apic_state, run->apic_base); +    if (!kvm_irqchip_in_kernel()) { +        qemu_mutex_unlock_iothread(); +    } +    return cpu_get_mem_attrs(env); +} + +int kvm_arch_process_async_events(CPUState *cs) +{ +    X86CPU *cpu = X86_CPU(cs); +    CPUX86State *env = &cpu->env; + +    if (cs->interrupt_request & CPU_INTERRUPT_MCE) { +        /* We must not raise CPU_INTERRUPT_MCE if it's not supported. */ +        assert(env->mcg_cap); + +        cs->interrupt_request &= ~CPU_INTERRUPT_MCE; + +        kvm_cpu_synchronize_state(cs); + +        if (env->exception_injected == EXCP08_DBLE) { +            /* this means triple fault */ +            qemu_system_reset_request(); +            cs->exit_request = 1; +            return 0; +        } +        env->exception_injected = EXCP12_MCHK; +        env->has_error_code = 0; + +        cs->halted = 0; +        if (kvm_irqchip_in_kernel() && env->mp_state == KVM_MP_STATE_HALTED) { +            env->mp_state = KVM_MP_STATE_RUNNABLE; +        } +    } + +    if ((cs->interrupt_request & CPU_INTERRUPT_INIT) && +        !(env->hflags & HF_SMM_MASK)) { +        kvm_cpu_synchronize_state(cs); +        do_cpu_init(cpu); +    } + +    if (kvm_irqchip_in_kernel()) { +        return 0; +    } + +    if (cs->interrupt_request & CPU_INTERRUPT_POLL) { +        cs->interrupt_request &= ~CPU_INTERRUPT_POLL; +        apic_poll_irq(cpu->apic_state); +    } +    if (((cs->interrupt_request & CPU_INTERRUPT_HARD) && +         (env->eflags & IF_MASK)) || +        (cs->interrupt_request & CPU_INTERRUPT_NMI)) { +        cs->halted = 0; +    } +    if (cs->interrupt_request & CPU_INTERRUPT_SIPI) { +        kvm_cpu_synchronize_state(cs); +        do_cpu_sipi(cpu); +    } +    if (cs->interrupt_request & CPU_INTERRUPT_TPR) { +        cs->interrupt_request &= ~CPU_INTERRUPT_TPR; +        kvm_cpu_synchronize_state(cs); +        apic_handle_tpr_access_report(cpu->apic_state, env->eip, +                                      env->tpr_access_type); +    } + +    return cs->halted; +} + +static int kvm_handle_halt(X86CPU *cpu) +{ +    CPUState *cs = CPU(cpu); +    CPUX86State *env = &cpu->env; + +    if (!((cs->interrupt_request & CPU_INTERRUPT_HARD) && +          (env->eflags & IF_MASK)) && +        !(cs->interrupt_request & CPU_INTERRUPT_NMI)) { +        cs->halted = 1; +        return EXCP_HLT; +    } + +    return 0; +} + +static int kvm_handle_tpr_access(X86CPU *cpu) +{ +    CPUState *cs = CPU(cpu); +    struct kvm_run *run = cs->kvm_run; + +    apic_handle_tpr_access_report(cpu->apic_state, run->tpr_access.rip, +                                  run->tpr_access.is_write ? TPR_ACCESS_WRITE +                                                           : TPR_ACCESS_READ); +    return 1; +} + +int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) +{ +    static const uint8_t int3 = 0xcc; + +    if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 1, 0) || +        cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&int3, 1, 1)) { +        return -EINVAL; +    } +    return 0; +} + +int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) +{ +    uint8_t int3; + +    if (cpu_memory_rw_debug(cs, bp->pc, &int3, 1, 0) || int3 != 0xcc || +        cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 1, 1)) { +        return -EINVAL; +    } +    return 0; +} + +static struct { +    target_ulong addr; +    int len; +    int type; +} hw_breakpoint[4]; + +static int nb_hw_breakpoint; + +static int find_hw_breakpoint(target_ulong addr, int len, int type) +{ +    int n; + +    for (n = 0; n < nb_hw_breakpoint; n++) { +        if (hw_breakpoint[n].addr == addr && hw_breakpoint[n].type == type && +            (hw_breakpoint[n].len == len || len == -1)) { +            return n; +        } +    } +    return -1; +} + +int kvm_arch_insert_hw_breakpoint(target_ulong addr, +                                  target_ulong len, int type) +{ +    switch (type) { +    case GDB_BREAKPOINT_HW: +        len = 1; +        break; +    case GDB_WATCHPOINT_WRITE: +    case GDB_WATCHPOINT_ACCESS: +        switch (len) { +        case 1: +            break; +        case 2: +        case 4: +        case 8: +            if (addr & (len - 1)) { +                return -EINVAL; +            } +            break; +        default: +            return -EINVAL; +        } +        break; +    default: +        return -ENOSYS; +    } + +    if (nb_hw_breakpoint == 4) { +        return -ENOBUFS; +    } +    if (find_hw_breakpoint(addr, len, type) >= 0) { +        return -EEXIST; +    } +    hw_breakpoint[nb_hw_breakpoint].addr = addr; +    hw_breakpoint[nb_hw_breakpoint].len = len; +    hw_breakpoint[nb_hw_breakpoint].type = type; +    nb_hw_breakpoint++; + +    return 0; +} + +int kvm_arch_remove_hw_breakpoint(target_ulong addr, +                                  target_ulong len, int type) +{ +    int n; + +    n = find_hw_breakpoint(addr, (type == GDB_BREAKPOINT_HW) ? 1 : len, type); +    if (n < 0) { +        return -ENOENT; +    } +    nb_hw_breakpoint--; +    hw_breakpoint[n] = hw_breakpoint[nb_hw_breakpoint]; + +    return 0; +} + +void kvm_arch_remove_all_hw_breakpoints(void) +{ +    nb_hw_breakpoint = 0; +} + +static CPUWatchpoint hw_watchpoint; + +static int kvm_handle_debug(X86CPU *cpu, +                            struct kvm_debug_exit_arch *arch_info) +{ +    CPUState *cs = CPU(cpu); +    CPUX86State *env = &cpu->env; +    int ret = 0; +    int n; + +    if (arch_info->exception == 1) { +        if (arch_info->dr6 & (1 << 14)) { +            if (cs->singlestep_enabled) { +                ret = EXCP_DEBUG; +            } +        } else { +            for (n = 0; n < 4; n++) { +                if (arch_info->dr6 & (1 << n)) { +                    switch ((arch_info->dr7 >> (16 + n*4)) & 0x3) { +                    case 0x0: +                        ret = EXCP_DEBUG; +                        break; +                    case 0x1: +                        ret = EXCP_DEBUG; +                        cs->watchpoint_hit = &hw_watchpoint; +                        hw_watchpoint.vaddr = hw_breakpoint[n].addr; +                        hw_watchpoint.flags = BP_MEM_WRITE; +                        break; +                    case 0x3: +                        ret = EXCP_DEBUG; +                        cs->watchpoint_hit = &hw_watchpoint; +                        hw_watchpoint.vaddr = hw_breakpoint[n].addr; +                        hw_watchpoint.flags = BP_MEM_ACCESS; +                        break; +                    } +                } +            } +        } +    } else if (kvm_find_sw_breakpoint(cs, arch_info->pc)) { +        ret = EXCP_DEBUG; +    } +    if (ret == 0) { +        cpu_synchronize_state(cs); +        assert(env->exception_injected == -1); + +        /* pass to guest */ +        env->exception_injected = arch_info->exception; +        env->has_error_code = 0; +    } + +    return ret; +} + +void kvm_arch_update_guest_debug(CPUState *cpu, struct kvm_guest_debug *dbg) +{ +    const uint8_t type_code[] = { +        [GDB_BREAKPOINT_HW] = 0x0, +        [GDB_WATCHPOINT_WRITE] = 0x1, +        [GDB_WATCHPOINT_ACCESS] = 0x3 +    }; +    const uint8_t len_code[] = { +        [1] = 0x0, [2] = 0x1, [4] = 0x3, [8] = 0x2 +    }; +    int n; + +    if (kvm_sw_breakpoints_active(cpu)) { +        dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP; +    } +    if (nb_hw_breakpoint > 0) { +        dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP; +        dbg->arch.debugreg[7] = 0x0600; +        for (n = 0; n < nb_hw_breakpoint; n++) { +            dbg->arch.debugreg[n] = hw_breakpoint[n].addr; +            dbg->arch.debugreg[7] |= (2 << (n * 2)) | +                (type_code[hw_breakpoint[n].type] << (16 + n*4)) | +                ((uint32_t)len_code[hw_breakpoint[n].len] << (18 + n*4)); +        } +    } +} + +static bool host_supports_vmx(void) +{ +    uint32_t ecx, unused; + +    host_cpuid(1, 0, &unused, &unused, &ecx, &unused); +    return ecx & CPUID_EXT_VMX; +} + +#define VMX_INVALID_GUEST_STATE 0x80000021 + +int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) +{ +    X86CPU *cpu = X86_CPU(cs); +    uint64_t code; +    int ret; + +    switch (run->exit_reason) { +    case KVM_EXIT_HLT: +        DPRINTF("handle_hlt\n"); +        qemu_mutex_lock_iothread(); +        ret = kvm_handle_halt(cpu); +        qemu_mutex_unlock_iothread(); +        break; +    case KVM_EXIT_SET_TPR: +        ret = 0; +        break; +    case KVM_EXIT_TPR_ACCESS: +        qemu_mutex_lock_iothread(); +        ret = kvm_handle_tpr_access(cpu); +        qemu_mutex_unlock_iothread(); +        break; +    case KVM_EXIT_FAIL_ENTRY: +        code = run->fail_entry.hardware_entry_failure_reason; +        fprintf(stderr, "KVM: entry failed, hardware error 0x%" PRIx64 "\n", +                code); +        if (host_supports_vmx() && code == VMX_INVALID_GUEST_STATE) { +            fprintf(stderr, +                    "\nIf you're running a guest on an Intel machine without " +                        "unrestricted mode\n" +                    "support, the failure can be most likely due to the guest " +                        "entering an invalid\n" +                    "state for Intel VT. For example, the guest maybe running " +                        "in big real mode\n" +                    "which is not supported on less recent Intel processors." +                        "\n\n"); +        } +        ret = -1; +        break; +    case KVM_EXIT_EXCEPTION: +        fprintf(stderr, "KVM: exception %d exit (error code 0x%x)\n", +                run->ex.exception, run->ex.error_code); +        ret = -1; +        break; +    case KVM_EXIT_DEBUG: +        DPRINTF("kvm_exit_debug\n"); +        qemu_mutex_lock_iothread(); +        ret = kvm_handle_debug(cpu, &run->debug.arch); +        qemu_mutex_unlock_iothread(); +        break; +    default: +        fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason); +        ret = -1; +        break; +    } + +    return ret; +} + +bool kvm_arch_stop_on_emulation_error(CPUState *cs) +{ +    X86CPU *cpu = X86_CPU(cs); +    CPUX86State *env = &cpu->env; + +    kvm_cpu_synchronize_state(cs); +    return !(env->cr[0] & CR0_PE_MASK) || +           ((env->segs[R_CS].selector  & 3) != 3); +} + +void kvm_arch_init_irq_routing(KVMState *s) +{ +    if (!kvm_check_extension(s, KVM_CAP_IRQ_ROUTING)) { +        /* If kernel can't do irq routing, interrupt source +         * override 0->2 cannot be set up as required by HPET. +         * So we have to disable it. +         */ +        no_hpet = 1; +    } +    /* We know at this point that we're using the in-kernel +     * irqchip, so we can use irqfds, and on x86 we know +     * we can use msi via irqfd and GSI routing. +     */ +    kvm_msi_via_irqfd_allowed = true; +    kvm_gsi_routing_allowed = true; +} + +/* Classic KVM device assignment interface. Will remain x86 only. */ +int kvm_device_pci_assign(KVMState *s, PCIHostDeviceAddress *dev_addr, +                          uint32_t flags, uint32_t *dev_id) +{ +    struct kvm_assigned_pci_dev dev_data = { +        .segnr = dev_addr->domain, +        .busnr = dev_addr->bus, +        .devfn = PCI_DEVFN(dev_addr->slot, dev_addr->function), +        .flags = flags, +    }; +    int ret; + +    dev_data.assigned_dev_id = +        (dev_addr->domain << 16) | (dev_addr->bus << 8) | dev_data.devfn; + +    ret = kvm_vm_ioctl(s, KVM_ASSIGN_PCI_DEVICE, &dev_data); +    if (ret < 0) { +        return ret; +    } + +    *dev_id = dev_data.assigned_dev_id; + +    return 0; +} + +int kvm_device_pci_deassign(KVMState *s, uint32_t dev_id) +{ +    struct kvm_assigned_pci_dev dev_data = { +        .assigned_dev_id = dev_id, +    }; + +    return kvm_vm_ioctl(s, KVM_DEASSIGN_PCI_DEVICE, &dev_data); +} + +static int kvm_assign_irq_internal(KVMState *s, uint32_t dev_id, +                                   uint32_t irq_type, uint32_t guest_irq) +{ +    struct kvm_assigned_irq assigned_irq = { +        .assigned_dev_id = dev_id, +        .guest_irq = guest_irq, +        .flags = irq_type, +    }; + +    if (kvm_check_extension(s, KVM_CAP_ASSIGN_DEV_IRQ)) { +        return kvm_vm_ioctl(s, KVM_ASSIGN_DEV_IRQ, &assigned_irq); +    } else { +        return kvm_vm_ioctl(s, KVM_ASSIGN_IRQ, &assigned_irq); +    } +} + +int kvm_device_intx_assign(KVMState *s, uint32_t dev_id, bool use_host_msi, +                           uint32_t guest_irq) +{ +    uint32_t irq_type = KVM_DEV_IRQ_GUEST_INTX | +        (use_host_msi ? KVM_DEV_IRQ_HOST_MSI : KVM_DEV_IRQ_HOST_INTX); + +    return kvm_assign_irq_internal(s, dev_id, irq_type, guest_irq); +} + +int kvm_device_intx_set_mask(KVMState *s, uint32_t dev_id, bool masked) +{ +    struct kvm_assigned_pci_dev dev_data = { +        .assigned_dev_id = dev_id, +        .flags = masked ? KVM_DEV_ASSIGN_MASK_INTX : 0, +    }; + +    return kvm_vm_ioctl(s, KVM_ASSIGN_SET_INTX_MASK, &dev_data); +} + +static int kvm_deassign_irq_internal(KVMState *s, uint32_t dev_id, +                                     uint32_t type) +{ +    struct kvm_assigned_irq assigned_irq = { +        .assigned_dev_id = dev_id, +        .flags = type, +    }; + +    return kvm_vm_ioctl(s, KVM_DEASSIGN_DEV_IRQ, &assigned_irq); +} + +int kvm_device_intx_deassign(KVMState *s, uint32_t dev_id, bool use_host_msi) +{ +    return kvm_deassign_irq_internal(s, dev_id, KVM_DEV_IRQ_GUEST_INTX | +        (use_host_msi ? KVM_DEV_IRQ_HOST_MSI : KVM_DEV_IRQ_HOST_INTX)); +} + +int kvm_device_msi_assign(KVMState *s, uint32_t dev_id, int virq) +{ +    return kvm_assign_irq_internal(s, dev_id, KVM_DEV_IRQ_HOST_MSI | +                                              KVM_DEV_IRQ_GUEST_MSI, virq); +} + +int kvm_device_msi_deassign(KVMState *s, uint32_t dev_id) +{ +    return kvm_deassign_irq_internal(s, dev_id, KVM_DEV_IRQ_GUEST_MSI | +                                                KVM_DEV_IRQ_HOST_MSI); +} + +bool kvm_device_msix_supported(KVMState *s) +{ +    /* The kernel lacks a corresponding KVM_CAP, so we probe by calling +     * KVM_ASSIGN_SET_MSIX_NR with an invalid parameter. */ +    return kvm_vm_ioctl(s, KVM_ASSIGN_SET_MSIX_NR, NULL) == -EFAULT; +} + +int kvm_device_msix_init_vectors(KVMState *s, uint32_t dev_id, +                                 uint32_t nr_vectors) +{ +    struct kvm_assigned_msix_nr msix_nr = { +        .assigned_dev_id = dev_id, +        .entry_nr = nr_vectors, +    }; + +    return kvm_vm_ioctl(s, KVM_ASSIGN_SET_MSIX_NR, &msix_nr); +} + +int kvm_device_msix_set_vector(KVMState *s, uint32_t dev_id, uint32_t vector, +                               int virq) +{ +    struct kvm_assigned_msix_entry msix_entry = { +        .assigned_dev_id = dev_id, +        .gsi = virq, +        .entry = vector, +    }; + +    return kvm_vm_ioctl(s, KVM_ASSIGN_SET_MSIX_ENTRY, &msix_entry); +} + +int kvm_device_msix_assign(KVMState *s, uint32_t dev_id) +{ +    return kvm_assign_irq_internal(s, dev_id, KVM_DEV_IRQ_HOST_MSIX | +                                              KVM_DEV_IRQ_GUEST_MSIX, 0); +} + +int kvm_device_msix_deassign(KVMState *s, uint32_t dev_id) +{ +    return kvm_deassign_irq_internal(s, dev_id, KVM_DEV_IRQ_GUEST_MSIX | +                                                KVM_DEV_IRQ_HOST_MSIX); +} + +int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, +                             uint64_t address, uint32_t data) +{ +    return 0; +} + +int kvm_arch_msi_data_to_gsi(uint32_t data) +{ +    abort(); +} diff --git a/target-i386/kvm_i386.h b/target-i386/kvm_i386.h new file mode 100644 index 00000000..e557e94f --- /dev/null +++ b/target-i386/kvm_i386.h @@ -0,0 +1,41 @@ +/* + * QEMU KVM support -- x86 specific functions. + * + * Copyright (c) 2012 Linaro Limited + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef QEMU_KVM_I386_H +#define QEMU_KVM_I386_H + +#include "sysemu/kvm.h" + +bool kvm_allows_irq0_override(void); +bool kvm_has_smm(void); +void kvm_arch_reset_vcpu(X86CPU *cs); +void kvm_arch_do_init_vcpu(X86CPU *cs); + +int kvm_device_pci_assign(KVMState *s, PCIHostDeviceAddress *dev_addr, +                          uint32_t flags, uint32_t *dev_id); +int kvm_device_pci_deassign(KVMState *s, uint32_t dev_id); + +int kvm_device_intx_assign(KVMState *s, uint32_t dev_id, +                           bool use_host_msi, uint32_t guest_irq); +int kvm_device_intx_set_mask(KVMState *s, uint32_t dev_id, bool masked); +int kvm_device_intx_deassign(KVMState *s, uint32_t dev_id, bool use_host_msi); + +int kvm_device_msi_assign(KVMState *s, uint32_t dev_id, int virq); +int kvm_device_msi_deassign(KVMState *s, uint32_t dev_id); + +bool kvm_device_msix_supported(KVMState *s); +int kvm_device_msix_init_vectors(KVMState *s, uint32_t dev_id, +                                 uint32_t nr_vectors); +int kvm_device_msix_set_vector(KVMState *s, uint32_t dev_id, uint32_t vector, +                               int virq); +int kvm_device_msix_assign(KVMState *s, uint32_t dev_id); +int kvm_device_msix_deassign(KVMState *s, uint32_t dev_id); + +#endif diff --git a/target-i386/machine.c b/target-i386/machine.c new file mode 100644 index 00000000..a0df64b5 --- /dev/null +++ b/target-i386/machine.c @@ -0,0 +1,849 @@ +#include "hw/hw.h" +#include "hw/boards.h" +#include "hw/i386/pc.h" +#include "hw/isa/isa.h" + +#include "cpu.h" +#include "sysemu/kvm.h" + +static const VMStateDescription vmstate_segment = { +    .name = "segment", +    .version_id = 1, +    .minimum_version_id = 1, +    .fields = (VMStateField[]) { +        VMSTATE_UINT32(selector, SegmentCache), +        VMSTATE_UINTTL(base, SegmentCache), +        VMSTATE_UINT32(limit, SegmentCache), +        VMSTATE_UINT32(flags, SegmentCache), +        VMSTATE_END_OF_LIST() +    } +}; + +#define VMSTATE_SEGMENT(_field, _state) {                            \ +    .name       = (stringify(_field)),                               \ +    .size       = sizeof(SegmentCache),                              \ +    .vmsd       = &vmstate_segment,                                  \ +    .flags      = VMS_STRUCT,                                        \ +    .offset     = offsetof(_state, _field)                           \ +            + type_check(SegmentCache,typeof_field(_state, _field))  \ +} + +#define VMSTATE_SEGMENT_ARRAY(_field, _state, _n)                    \ +    VMSTATE_STRUCT_ARRAY(_field, _state, _n, 0, vmstate_segment, SegmentCache) + +static const VMStateDescription vmstate_xmm_reg = { +    .name = "xmm_reg", +    .version_id = 1, +    .minimum_version_id = 1, +    .fields = (VMStateField[]) { +        VMSTATE_UINT64(XMM_Q(0), XMMReg), +        VMSTATE_UINT64(XMM_Q(1), XMMReg), +        VMSTATE_END_OF_LIST() +    } +}; + +#define VMSTATE_XMM_REGS(_field, _state, _start)                         \ +    VMSTATE_STRUCT_SUB_ARRAY(_field, _state, _start, CPU_NB_REGS, 0,     \ +                             vmstate_xmm_reg, XMMReg) + +/* YMMH format is the same as XMM, but for bits 128-255 */ +static const VMStateDescription vmstate_ymmh_reg = { +    .name = "ymmh_reg", +    .version_id = 1, +    .minimum_version_id = 1, +    .fields = (VMStateField[]) { +        VMSTATE_UINT64(XMM_Q(2), XMMReg), +        VMSTATE_UINT64(XMM_Q(3), XMMReg), +        VMSTATE_END_OF_LIST() +    } +}; + +#define VMSTATE_YMMH_REGS_VARS(_field, _state, _start, _v)               \ +    VMSTATE_STRUCT_SUB_ARRAY(_field, _state, _start, CPU_NB_REGS, _v,    \ +                             vmstate_ymmh_reg, XMMReg) + +static const VMStateDescription vmstate_zmmh_reg = { +    .name = "zmmh_reg", +    .version_id = 1, +    .minimum_version_id = 1, +    .fields = (VMStateField[]) { +        VMSTATE_UINT64(XMM_Q(4), XMMReg), +        VMSTATE_UINT64(XMM_Q(5), XMMReg), +        VMSTATE_UINT64(XMM_Q(6), XMMReg), +        VMSTATE_UINT64(XMM_Q(7), XMMReg), +        VMSTATE_END_OF_LIST() +    } +}; + +#define VMSTATE_ZMMH_REGS_VARS(_field, _state, _start)                   \ +    VMSTATE_STRUCT_SUB_ARRAY(_field, _state, _start, CPU_NB_REGS, 0,     \ +                             vmstate_zmmh_reg, XMMReg) + +#ifdef TARGET_X86_64 +static const VMStateDescription vmstate_hi16_zmm_reg = { +    .name = "hi16_zmm_reg", +    .version_id = 1, +    .minimum_version_id = 1, +    .fields = (VMStateField[]) { +        VMSTATE_UINT64(XMM_Q(0), XMMReg), +        VMSTATE_UINT64(XMM_Q(1), XMMReg), +        VMSTATE_UINT64(XMM_Q(2), XMMReg), +        VMSTATE_UINT64(XMM_Q(3), XMMReg), +        VMSTATE_UINT64(XMM_Q(4), XMMReg), +        VMSTATE_UINT64(XMM_Q(5), XMMReg), +        VMSTATE_UINT64(XMM_Q(6), XMMReg), +        VMSTATE_UINT64(XMM_Q(7), XMMReg), +        VMSTATE_END_OF_LIST() +    } +}; + +#define VMSTATE_Hi16_ZMM_REGS_VARS(_field, _state, _start)               \ +    VMSTATE_STRUCT_SUB_ARRAY(_field, _state, _start, CPU_NB_REGS, 0,     \ +                             vmstate_hi16_zmm_reg, XMMReg) +#endif + +static const VMStateDescription vmstate_bnd_regs = { +    .name = "bnd_regs", +    .version_id = 1, +    .minimum_version_id = 1, +    .fields = (VMStateField[]) { +        VMSTATE_UINT64(lb, BNDReg), +        VMSTATE_UINT64(ub, BNDReg), +        VMSTATE_END_OF_LIST() +    } +}; + +#define VMSTATE_BND_REGS(_field, _state, _n)          \ +    VMSTATE_STRUCT_ARRAY(_field, _state, _n, 0, vmstate_bnd_regs, BNDReg) + +static const VMStateDescription vmstate_mtrr_var = { +    .name = "mtrr_var", +    .version_id = 1, +    .minimum_version_id = 1, +    .fields = (VMStateField[]) { +        VMSTATE_UINT64(base, MTRRVar), +        VMSTATE_UINT64(mask, MTRRVar), +        VMSTATE_END_OF_LIST() +    } +}; + +#define VMSTATE_MTRR_VARS(_field, _state, _n, _v)                    \ +    VMSTATE_STRUCT_ARRAY(_field, _state, _n, _v, vmstate_mtrr_var, MTRRVar) + +static void put_fpreg_error(QEMUFile *f, void *opaque, size_t size) +{ +    fprintf(stderr, "call put_fpreg() with invalid arguments\n"); +    exit(0); +} + +/* XXX: add that in a FPU generic layer */ +union x86_longdouble { +    uint64_t mant; +    uint16_t exp; +}; + +#define MANTD1(fp)	(fp & ((1LL << 52) - 1)) +#define EXPBIAS1 1023 +#define EXPD1(fp)	((fp >> 52) & 0x7FF) +#define SIGND1(fp)	((fp >> 32) & 0x80000000) + +static void fp64_to_fp80(union x86_longdouble *p, uint64_t temp) +{ +    int e; +    /* mantissa */ +    p->mant = (MANTD1(temp) << 11) | (1LL << 63); +    /* exponent + sign */ +    e = EXPD1(temp) - EXPBIAS1 + 16383; +    e |= SIGND1(temp) >> 16; +    p->exp = e; +} + +static int get_fpreg(QEMUFile *f, void *opaque, size_t size) +{ +    FPReg *fp_reg = opaque; +    uint64_t mant; +    uint16_t exp; + +    qemu_get_be64s(f, &mant); +    qemu_get_be16s(f, &exp); +    fp_reg->d = cpu_set_fp80(mant, exp); +    return 0; +} + +static void put_fpreg(QEMUFile *f, void *opaque, size_t size) +{ +    FPReg *fp_reg = opaque; +    uint64_t mant; +    uint16_t exp; +    /* we save the real CPU data (in case of MMX usage only 'mant' +       contains the MMX register */ +    cpu_get_fp80(&mant, &exp, fp_reg->d); +    qemu_put_be64s(f, &mant); +    qemu_put_be16s(f, &exp); +} + +static const VMStateInfo vmstate_fpreg = { +    .name = "fpreg", +    .get  = get_fpreg, +    .put  = put_fpreg, +}; + +static int get_fpreg_1_mmx(QEMUFile *f, void *opaque, size_t size) +{ +    union x86_longdouble *p = opaque; +    uint64_t mant; + +    qemu_get_be64s(f, &mant); +    p->mant = mant; +    p->exp = 0xffff; +    return 0; +} + +static const VMStateInfo vmstate_fpreg_1_mmx = { +    .name = "fpreg_1_mmx", +    .get  = get_fpreg_1_mmx, +    .put  = put_fpreg_error, +}; + +static int get_fpreg_1_no_mmx(QEMUFile *f, void *opaque, size_t size) +{ +    union x86_longdouble *p = opaque; +    uint64_t mant; + +    qemu_get_be64s(f, &mant); +    fp64_to_fp80(p, mant); +    return 0; +} + +static const VMStateInfo vmstate_fpreg_1_no_mmx = { +    .name = "fpreg_1_no_mmx", +    .get  = get_fpreg_1_no_mmx, +    .put  = put_fpreg_error, +}; + +static bool fpregs_is_0(void *opaque, int version_id) +{ +    X86CPU *cpu = opaque; +    CPUX86State *env = &cpu->env; + +    return (env->fpregs_format_vmstate == 0); +} + +static bool fpregs_is_1_mmx(void *opaque, int version_id) +{ +    X86CPU *cpu = opaque; +    CPUX86State *env = &cpu->env; +    int guess_mmx; + +    guess_mmx = ((env->fptag_vmstate == 0xff) && +                 (env->fpus_vmstate & 0x3800) == 0); +    return (guess_mmx && (env->fpregs_format_vmstate == 1)); +} + +static bool fpregs_is_1_no_mmx(void *opaque, int version_id) +{ +    X86CPU *cpu = opaque; +    CPUX86State *env = &cpu->env; +    int guess_mmx; + +    guess_mmx = ((env->fptag_vmstate == 0xff) && +                 (env->fpus_vmstate & 0x3800) == 0); +    return (!guess_mmx && (env->fpregs_format_vmstate == 1)); +} + +#define VMSTATE_FP_REGS(_field, _state, _n)                               \ +    VMSTATE_ARRAY_TEST(_field, _state, _n, fpregs_is_0, vmstate_fpreg, FPReg), \ +    VMSTATE_ARRAY_TEST(_field, _state, _n, fpregs_is_1_mmx, vmstate_fpreg_1_mmx, FPReg), \ +    VMSTATE_ARRAY_TEST(_field, _state, _n, fpregs_is_1_no_mmx, vmstate_fpreg_1_no_mmx, FPReg) + +static bool version_is_5(void *opaque, int version_id) +{ +    return version_id == 5; +} + +#ifdef TARGET_X86_64 +static bool less_than_7(void *opaque, int version_id) +{ +    return version_id < 7; +} + +static int get_uint64_as_uint32(QEMUFile *f, void *pv, size_t size) +{ +    uint64_t *v = pv; +    *v = qemu_get_be32(f); +    return 0; +} + +static void put_uint64_as_uint32(QEMUFile *f, void *pv, size_t size) +{ +    uint64_t *v = pv; +    qemu_put_be32(f, *v); +} + +static const VMStateInfo vmstate_hack_uint64_as_uint32 = { +    .name = "uint64_as_uint32", +    .get  = get_uint64_as_uint32, +    .put  = put_uint64_as_uint32, +}; + +#define VMSTATE_HACK_UINT32(_f, _s, _t)                                  \ +    VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_hack_uint64_as_uint32, uint64_t) +#endif + +static void cpu_pre_save(void *opaque) +{ +    X86CPU *cpu = opaque; +    CPUX86State *env = &cpu->env; +    int i; + +    /* FPU */ +    env->fpus_vmstate = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; +    env->fptag_vmstate = 0; +    for(i = 0; i < 8; i++) { +        env->fptag_vmstate |= ((!env->fptags[i]) << i); +    } + +    env->fpregs_format_vmstate = 0; + +    /* +     * Real mode guest segments register DPL should be zero. +     * Older KVM version were setting it wrongly. +     * Fixing it will allow live migration to host with unrestricted guest +     * support (otherwise the migration will fail with invalid guest state +     * error). +     */ +    if (!(env->cr[0] & CR0_PE_MASK) && +        (env->segs[R_CS].flags >> DESC_DPL_SHIFT & 3) != 0) { +        env->segs[R_CS].flags &= ~(env->segs[R_CS].flags & DESC_DPL_MASK); +        env->segs[R_DS].flags &= ~(env->segs[R_DS].flags & DESC_DPL_MASK); +        env->segs[R_ES].flags &= ~(env->segs[R_ES].flags & DESC_DPL_MASK); +        env->segs[R_FS].flags &= ~(env->segs[R_FS].flags & DESC_DPL_MASK); +        env->segs[R_GS].flags &= ~(env->segs[R_GS].flags & DESC_DPL_MASK); +        env->segs[R_SS].flags &= ~(env->segs[R_SS].flags & DESC_DPL_MASK); +    } + +} + +static int cpu_post_load(void *opaque, int version_id) +{ +    X86CPU *cpu = opaque; +    CPUState *cs = CPU(cpu); +    CPUX86State *env = &cpu->env; +    int i; + +    /* +     * Real mode guest segments register DPL should be zero. +     * Older KVM version were setting it wrongly. +     * Fixing it will allow live migration from such host that don't have +     * restricted guest support to a host with unrestricted guest support +     * (otherwise the migration will fail with invalid guest state +     * error). +     */ +    if (!(env->cr[0] & CR0_PE_MASK) && +        (env->segs[R_CS].flags >> DESC_DPL_SHIFT & 3) != 0) { +        env->segs[R_CS].flags &= ~(env->segs[R_CS].flags & DESC_DPL_MASK); +        env->segs[R_DS].flags &= ~(env->segs[R_DS].flags & DESC_DPL_MASK); +        env->segs[R_ES].flags &= ~(env->segs[R_ES].flags & DESC_DPL_MASK); +        env->segs[R_FS].flags &= ~(env->segs[R_FS].flags & DESC_DPL_MASK); +        env->segs[R_GS].flags &= ~(env->segs[R_GS].flags & DESC_DPL_MASK); +        env->segs[R_SS].flags &= ~(env->segs[R_SS].flags & DESC_DPL_MASK); +    } + +    /* Older versions of QEMU incorrectly used CS.DPL as the CPL when +     * running under KVM.  This is wrong for conforming code segments. +     * Luckily, in our implementation the CPL field of hflags is redundant +     * and we can get the right value from the SS descriptor privilege level. +     */ +    env->hflags &= ~HF_CPL_MASK; +    env->hflags |= (env->segs[R_SS].flags >> DESC_DPL_SHIFT) & HF_CPL_MASK; + +    env->fpstt = (env->fpus_vmstate >> 11) & 7; +    env->fpus = env->fpus_vmstate & ~0x3800; +    env->fptag_vmstate ^= 0xff; +    for(i = 0; i < 8; i++) { +        env->fptags[i] = (env->fptag_vmstate >> i) & 1; +    } +    update_fp_status(env); + +    cpu_breakpoint_remove_all(cs, BP_CPU); +    cpu_watchpoint_remove_all(cs, BP_CPU); +    for (i = 0; i < DR7_MAX_BP; i++) { +        hw_breakpoint_insert(env, i); +    } +    tlb_flush(cs, 1); + +    if (tcg_enabled()) { +        cpu_smm_update(cpu); +    } +    return 0; +} + +static bool async_pf_msr_needed(void *opaque) +{ +    X86CPU *cpu = opaque; + +    return cpu->env.async_pf_en_msr != 0; +} + +static bool pv_eoi_msr_needed(void *opaque) +{ +    X86CPU *cpu = opaque; + +    return cpu->env.pv_eoi_en_msr != 0; +} + +static bool steal_time_msr_needed(void *opaque) +{ +    X86CPU *cpu = opaque; + +    return cpu->env.steal_time_msr != 0; +} + +static const VMStateDescription vmstate_steal_time_msr = { +    .name = "cpu/steal_time_msr", +    .version_id = 1, +    .minimum_version_id = 1, +    .needed = steal_time_msr_needed, +    .fields = (VMStateField[]) { +        VMSTATE_UINT64(env.steal_time_msr, X86CPU), +        VMSTATE_END_OF_LIST() +    } +}; + +static const VMStateDescription vmstate_async_pf_msr = { +    .name = "cpu/async_pf_msr", +    .version_id = 1, +    .minimum_version_id = 1, +    .needed = async_pf_msr_needed, +    .fields = (VMStateField[]) { +        VMSTATE_UINT64(env.async_pf_en_msr, X86CPU), +        VMSTATE_END_OF_LIST() +    } +}; + +static const VMStateDescription vmstate_pv_eoi_msr = { +    .name = "cpu/async_pv_eoi_msr", +    .version_id = 1, +    .minimum_version_id = 1, +    .needed = pv_eoi_msr_needed, +    .fields = (VMStateField[]) { +        VMSTATE_UINT64(env.pv_eoi_en_msr, X86CPU), +        VMSTATE_END_OF_LIST() +    } +}; + +static bool fpop_ip_dp_needed(void *opaque) +{ +    X86CPU *cpu = opaque; +    CPUX86State *env = &cpu->env; + +    return env->fpop != 0 || env->fpip != 0 || env->fpdp != 0; +} + +static const VMStateDescription vmstate_fpop_ip_dp = { +    .name = "cpu/fpop_ip_dp", +    .version_id = 1, +    .minimum_version_id = 1, +    .needed = fpop_ip_dp_needed, +    .fields = (VMStateField[]) { +        VMSTATE_UINT16(env.fpop, X86CPU), +        VMSTATE_UINT64(env.fpip, X86CPU), +        VMSTATE_UINT64(env.fpdp, X86CPU), +        VMSTATE_END_OF_LIST() +    } +}; + +static bool tsc_adjust_needed(void *opaque) +{ +    X86CPU *cpu = opaque; +    CPUX86State *env = &cpu->env; + +    return env->tsc_adjust != 0; +} + +static const VMStateDescription vmstate_msr_tsc_adjust = { +    .name = "cpu/msr_tsc_adjust", +    .version_id = 1, +    .minimum_version_id = 1, +    .needed = tsc_adjust_needed, +    .fields = (VMStateField[]) { +        VMSTATE_UINT64(env.tsc_adjust, X86CPU), +        VMSTATE_END_OF_LIST() +    } +}; + +static bool tscdeadline_needed(void *opaque) +{ +    X86CPU *cpu = opaque; +    CPUX86State *env = &cpu->env; + +    return env->tsc_deadline != 0; +} + +static const VMStateDescription vmstate_msr_tscdeadline = { +    .name = "cpu/msr_tscdeadline", +    .version_id = 1, +    .minimum_version_id = 1, +    .needed = tscdeadline_needed, +    .fields = (VMStateField[]) { +        VMSTATE_UINT64(env.tsc_deadline, X86CPU), +        VMSTATE_END_OF_LIST() +    } +}; + +static bool misc_enable_needed(void *opaque) +{ +    X86CPU *cpu = opaque; +    CPUX86State *env = &cpu->env; + +    return env->msr_ia32_misc_enable != MSR_IA32_MISC_ENABLE_DEFAULT; +} + +static bool feature_control_needed(void *opaque) +{ +    X86CPU *cpu = opaque; +    CPUX86State *env = &cpu->env; + +    return env->msr_ia32_feature_control != 0; +} + +static const VMStateDescription vmstate_msr_ia32_misc_enable = { +    .name = "cpu/msr_ia32_misc_enable", +    .version_id = 1, +    .minimum_version_id = 1, +    .needed = misc_enable_needed, +    .fields = (VMStateField[]) { +        VMSTATE_UINT64(env.msr_ia32_misc_enable, X86CPU), +        VMSTATE_END_OF_LIST() +    } +}; + +static const VMStateDescription vmstate_msr_ia32_feature_control = { +    .name = "cpu/msr_ia32_feature_control", +    .version_id = 1, +    .minimum_version_id = 1, +    .needed = feature_control_needed, +    .fields = (VMStateField[]) { +        VMSTATE_UINT64(env.msr_ia32_feature_control, X86CPU), +        VMSTATE_END_OF_LIST() +    } +}; + +static bool pmu_enable_needed(void *opaque) +{ +    X86CPU *cpu = opaque; +    CPUX86State *env = &cpu->env; +    int i; + +    if (env->msr_fixed_ctr_ctrl || env->msr_global_ctrl || +        env->msr_global_status || env->msr_global_ovf_ctrl) { +        return true; +    } +    for (i = 0; i < MAX_FIXED_COUNTERS; i++) { +        if (env->msr_fixed_counters[i]) { +            return true; +        } +    } +    for (i = 0; i < MAX_GP_COUNTERS; i++) { +        if (env->msr_gp_counters[i] || env->msr_gp_evtsel[i]) { +            return true; +        } +    } + +    return false; +} + +static const VMStateDescription vmstate_msr_architectural_pmu = { +    .name = "cpu/msr_architectural_pmu", +    .version_id = 1, +    .minimum_version_id = 1, +    .needed = pmu_enable_needed, +    .fields = (VMStateField[]) { +        VMSTATE_UINT64(env.msr_fixed_ctr_ctrl, X86CPU), +        VMSTATE_UINT64(env.msr_global_ctrl, X86CPU), +        VMSTATE_UINT64(env.msr_global_status, X86CPU), +        VMSTATE_UINT64(env.msr_global_ovf_ctrl, X86CPU), +        VMSTATE_UINT64_ARRAY(env.msr_fixed_counters, X86CPU, MAX_FIXED_COUNTERS), +        VMSTATE_UINT64_ARRAY(env.msr_gp_counters, X86CPU, MAX_GP_COUNTERS), +        VMSTATE_UINT64_ARRAY(env.msr_gp_evtsel, X86CPU, MAX_GP_COUNTERS), +        VMSTATE_END_OF_LIST() +    } +}; + +static bool mpx_needed(void *opaque) +{ +    X86CPU *cpu = opaque; +    CPUX86State *env = &cpu->env; +    unsigned int i; + +    for (i = 0; i < 4; i++) { +        if (env->bnd_regs[i].lb || env->bnd_regs[i].ub) { +            return true; +        } +    } + +    if (env->bndcs_regs.cfgu || env->bndcs_regs.sts) { +        return true; +    } + +    return !!env->msr_bndcfgs; +} + +static const VMStateDescription vmstate_mpx = { +    .name = "cpu/mpx", +    .version_id = 1, +    .minimum_version_id = 1, +    .needed = mpx_needed, +    .fields = (VMStateField[]) { +        VMSTATE_BND_REGS(env.bnd_regs, X86CPU, 4), +        VMSTATE_UINT64(env.bndcs_regs.cfgu, X86CPU), +        VMSTATE_UINT64(env.bndcs_regs.sts, X86CPU), +        VMSTATE_UINT64(env.msr_bndcfgs, X86CPU), +        VMSTATE_END_OF_LIST() +    } +}; + +static bool hyperv_hypercall_enable_needed(void *opaque) +{ +    X86CPU *cpu = opaque; +    CPUX86State *env = &cpu->env; + +    return env->msr_hv_hypercall != 0 || env->msr_hv_guest_os_id != 0; +} + +static const VMStateDescription vmstate_msr_hypercall_hypercall = { +    .name = "cpu/msr_hyperv_hypercall", +    .version_id = 1, +    .minimum_version_id = 1, +    .needed = hyperv_hypercall_enable_needed, +    .fields = (VMStateField[]) { +        VMSTATE_UINT64(env.msr_hv_guest_os_id, X86CPU), +        VMSTATE_UINT64(env.msr_hv_hypercall, X86CPU), +        VMSTATE_END_OF_LIST() +    } +}; + +static bool hyperv_vapic_enable_needed(void *opaque) +{ +    X86CPU *cpu = opaque; +    CPUX86State *env = &cpu->env; + +    return env->msr_hv_vapic != 0; +} + +static const VMStateDescription vmstate_msr_hyperv_vapic = { +    .name = "cpu/msr_hyperv_vapic", +    .version_id = 1, +    .minimum_version_id = 1, +    .needed = hyperv_vapic_enable_needed, +    .fields = (VMStateField[]) { +        VMSTATE_UINT64(env.msr_hv_vapic, X86CPU), +        VMSTATE_END_OF_LIST() +    } +}; + +static bool hyperv_time_enable_needed(void *opaque) +{ +    X86CPU *cpu = opaque; +    CPUX86State *env = &cpu->env; + +    return env->msr_hv_tsc != 0; +} + +static const VMStateDescription vmstate_msr_hyperv_time = { +    .name = "cpu/msr_hyperv_time", +    .version_id = 1, +    .minimum_version_id = 1, +    .needed = hyperv_time_enable_needed, +    .fields = (VMStateField[]) { +        VMSTATE_UINT64(env.msr_hv_tsc, X86CPU), +        VMSTATE_END_OF_LIST() +    } +}; + +static bool avx512_needed(void *opaque) +{ +    X86CPU *cpu = opaque; +    CPUX86State *env = &cpu->env; +    unsigned int i; + +    for (i = 0; i < NB_OPMASK_REGS; i++) { +        if (env->opmask_regs[i]) { +            return true; +        } +    } + +    for (i = 0; i < CPU_NB_REGS; i++) { +#define ENV_XMM(reg, field) (env->xmm_regs[reg].XMM_Q(field)) +        if (ENV_XMM(i, 4) || ENV_XMM(i, 6) || +            ENV_XMM(i, 5) || ENV_XMM(i, 7)) { +            return true; +        } +#ifdef TARGET_X86_64 +        if (ENV_XMM(i+16, 0) || ENV_XMM(i+16, 1) || +            ENV_XMM(i+16, 2) || ENV_XMM(i+16, 3) || +            ENV_XMM(i+16, 4) || ENV_XMM(i+16, 5) || +            ENV_XMM(i+16, 6) || ENV_XMM(i+16, 7)) { +            return true; +        } +#endif +    } + +    return false; +} + +static const VMStateDescription vmstate_avx512 = { +    .name = "cpu/avx512", +    .version_id = 1, +    .minimum_version_id = 1, +    .needed = avx512_needed, +    .fields = (VMStateField[]) { +        VMSTATE_UINT64_ARRAY(env.opmask_regs, X86CPU, NB_OPMASK_REGS), +        VMSTATE_ZMMH_REGS_VARS(env.xmm_regs, X86CPU, 0), +#ifdef TARGET_X86_64 +        VMSTATE_Hi16_ZMM_REGS_VARS(env.xmm_regs, X86CPU, 16), +#endif +        VMSTATE_END_OF_LIST() +    } +}; + +static bool xss_needed(void *opaque) +{ +    X86CPU *cpu = opaque; +    CPUX86State *env = &cpu->env; + +    return env->xss != 0; +} + +static const VMStateDescription vmstate_xss = { +    .name = "cpu/xss", +    .version_id = 1, +    .minimum_version_id = 1, +    .needed = xss_needed, +    .fields = (VMStateField[]) { +        VMSTATE_UINT64(env.xss, X86CPU), +        VMSTATE_END_OF_LIST() +    } +}; + +VMStateDescription vmstate_x86_cpu = { +    .name = "cpu", +    .version_id = 12, +    .minimum_version_id = 3, +    .pre_save = cpu_pre_save, +    .post_load = cpu_post_load, +    .fields = (VMStateField[]) { +        VMSTATE_UINTTL_ARRAY(env.regs, X86CPU, CPU_NB_REGS), +        VMSTATE_UINTTL(env.eip, X86CPU), +        VMSTATE_UINTTL(env.eflags, X86CPU), +        VMSTATE_UINT32(env.hflags, X86CPU), +        /* FPU */ +        VMSTATE_UINT16(env.fpuc, X86CPU), +        VMSTATE_UINT16(env.fpus_vmstate, X86CPU), +        VMSTATE_UINT16(env.fptag_vmstate, X86CPU), +        VMSTATE_UINT16(env.fpregs_format_vmstate, X86CPU), +        VMSTATE_FP_REGS(env.fpregs, X86CPU, 8), + +        VMSTATE_SEGMENT_ARRAY(env.segs, X86CPU, 6), +        VMSTATE_SEGMENT(env.ldt, X86CPU), +        VMSTATE_SEGMENT(env.tr, X86CPU), +        VMSTATE_SEGMENT(env.gdt, X86CPU), +        VMSTATE_SEGMENT(env.idt, X86CPU), + +        VMSTATE_UINT32(env.sysenter_cs, X86CPU), +#ifdef TARGET_X86_64 +        /* Hack: In v7 size changed from 32 to 64 bits on x86_64 */ +        VMSTATE_HACK_UINT32(env.sysenter_esp, X86CPU, less_than_7), +        VMSTATE_HACK_UINT32(env.sysenter_eip, X86CPU, less_than_7), +        VMSTATE_UINTTL_V(env.sysenter_esp, X86CPU, 7), +        VMSTATE_UINTTL_V(env.sysenter_eip, X86CPU, 7), +#else +        VMSTATE_UINTTL(env.sysenter_esp, X86CPU), +        VMSTATE_UINTTL(env.sysenter_eip, X86CPU), +#endif + +        VMSTATE_UINTTL(env.cr[0], X86CPU), +        VMSTATE_UINTTL(env.cr[2], X86CPU), +        VMSTATE_UINTTL(env.cr[3], X86CPU), +        VMSTATE_UINTTL(env.cr[4], X86CPU), +        VMSTATE_UINTTL_ARRAY(env.dr, X86CPU, 8), +        /* MMU */ +        VMSTATE_INT32(env.a20_mask, X86CPU), +        /* XMM */ +        VMSTATE_UINT32(env.mxcsr, X86CPU), +        VMSTATE_XMM_REGS(env.xmm_regs, X86CPU, 0), + +#ifdef TARGET_X86_64 +        VMSTATE_UINT64(env.efer, X86CPU), +        VMSTATE_UINT64(env.star, X86CPU), +        VMSTATE_UINT64(env.lstar, X86CPU), +        VMSTATE_UINT64(env.cstar, X86CPU), +        VMSTATE_UINT64(env.fmask, X86CPU), +        VMSTATE_UINT64(env.kernelgsbase, X86CPU), +#endif +        VMSTATE_UINT32_V(env.smbase, X86CPU, 4), + +        VMSTATE_UINT64_V(env.pat, X86CPU, 5), +        VMSTATE_UINT32_V(env.hflags2, X86CPU, 5), + +        VMSTATE_UINT32_TEST(parent_obj.halted, X86CPU, version_is_5), +        VMSTATE_UINT64_V(env.vm_hsave, X86CPU, 5), +        VMSTATE_UINT64_V(env.vm_vmcb, X86CPU, 5), +        VMSTATE_UINT64_V(env.tsc_offset, X86CPU, 5), +        VMSTATE_UINT64_V(env.intercept, X86CPU, 5), +        VMSTATE_UINT16_V(env.intercept_cr_read, X86CPU, 5), +        VMSTATE_UINT16_V(env.intercept_cr_write, X86CPU, 5), +        VMSTATE_UINT16_V(env.intercept_dr_read, X86CPU, 5), +        VMSTATE_UINT16_V(env.intercept_dr_write, X86CPU, 5), +        VMSTATE_UINT32_V(env.intercept_exceptions, X86CPU, 5), +        VMSTATE_UINT8_V(env.v_tpr, X86CPU, 5), +        /* MTRRs */ +        VMSTATE_UINT64_ARRAY_V(env.mtrr_fixed, X86CPU, 11, 8), +        VMSTATE_UINT64_V(env.mtrr_deftype, X86CPU, 8), +        VMSTATE_MTRR_VARS(env.mtrr_var, X86CPU, MSR_MTRRcap_VCNT, 8), +        /* KVM-related states */ +        VMSTATE_INT32_V(env.interrupt_injected, X86CPU, 9), +        VMSTATE_UINT32_V(env.mp_state, X86CPU, 9), +        VMSTATE_UINT64_V(env.tsc, X86CPU, 9), +        VMSTATE_INT32_V(env.exception_injected, X86CPU, 11), +        VMSTATE_UINT8_V(env.soft_interrupt, X86CPU, 11), +        VMSTATE_UINT8_V(env.nmi_injected, X86CPU, 11), +        VMSTATE_UINT8_V(env.nmi_pending, X86CPU, 11), +        VMSTATE_UINT8_V(env.has_error_code, X86CPU, 11), +        VMSTATE_UINT32_V(env.sipi_vector, X86CPU, 11), +        /* MCE */ +        VMSTATE_UINT64_V(env.mcg_cap, X86CPU, 10), +        VMSTATE_UINT64_V(env.mcg_status, X86CPU, 10), +        VMSTATE_UINT64_V(env.mcg_ctl, X86CPU, 10), +        VMSTATE_UINT64_ARRAY_V(env.mce_banks, X86CPU, MCE_BANKS_DEF * 4, 10), +        /* rdtscp */ +        VMSTATE_UINT64_V(env.tsc_aux, X86CPU, 11), +        /* KVM pvclock msr */ +        VMSTATE_UINT64_V(env.system_time_msr, X86CPU, 11), +        VMSTATE_UINT64_V(env.wall_clock_msr, X86CPU, 11), +        /* XSAVE related fields */ +        VMSTATE_UINT64_V(env.xcr0, X86CPU, 12), +        VMSTATE_UINT64_V(env.xstate_bv, X86CPU, 12), +        VMSTATE_YMMH_REGS_VARS(env.xmm_regs, X86CPU, 0, 12), +        VMSTATE_END_OF_LIST() +        /* The above list is not sorted /wrt version numbers, watch out! */ +    }, +    .subsections = (const VMStateDescription*[]) { +        &vmstate_async_pf_msr, +        &vmstate_pv_eoi_msr, +        &vmstate_steal_time_msr, +        &vmstate_fpop_ip_dp, +        &vmstate_msr_tsc_adjust, +        &vmstate_msr_tscdeadline, +        &vmstate_msr_ia32_misc_enable, +        &vmstate_msr_ia32_feature_control, +        &vmstate_msr_architectural_pmu, +        &vmstate_mpx, +        &vmstate_msr_hypercall_hypercall, +        &vmstate_msr_hyperv_vapic, +        &vmstate_msr_hyperv_time, +        &vmstate_avx512, +        &vmstate_xss, +        NULL +    } +}; diff --git a/target-i386/mem_helper.c b/target-i386/mem_helper.c new file mode 100644 index 00000000..1aec8a5f --- /dev/null +++ b/target-i386/mem_helper.c @@ -0,0 +1,132 @@ +/* + *  x86 memory access helpers + * + *  Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "cpu.h" +#include "exec/helper-proto.h" +#include "exec/cpu_ldst.h" + +/* broken thread support */ + +static spinlock_t global_cpu_lock = SPIN_LOCK_UNLOCKED; + +void helper_lock(void) +{ +    spin_lock(&global_cpu_lock); +} + +void helper_unlock(void) +{ +    spin_unlock(&global_cpu_lock); +} + +void helper_cmpxchg8b(CPUX86State *env, target_ulong a0) +{ +    uint64_t d; +    int eflags; + +    eflags = cpu_cc_compute_all(env, CC_OP); +    d = cpu_ldq_data(env, a0); +    if (d == (((uint64_t)env->regs[R_EDX] << 32) | (uint32_t)env->regs[R_EAX])) { +        cpu_stq_data(env, a0, ((uint64_t)env->regs[R_ECX] << 32) | (uint32_t)env->regs[R_EBX]); +        eflags |= CC_Z; +    } else { +        /* always do the store */ +        cpu_stq_data(env, a0, d); +        env->regs[R_EDX] = (uint32_t)(d >> 32); +        env->regs[R_EAX] = (uint32_t)d; +        eflags &= ~CC_Z; +    } +    CC_SRC = eflags; +} + +#ifdef TARGET_X86_64 +void helper_cmpxchg16b(CPUX86State *env, target_ulong a0) +{ +    uint64_t d0, d1; +    int eflags; + +    if ((a0 & 0xf) != 0) { +        raise_exception(env, EXCP0D_GPF); +    } +    eflags = cpu_cc_compute_all(env, CC_OP); +    d0 = cpu_ldq_data(env, a0); +    d1 = cpu_ldq_data(env, a0 + 8); +    if (d0 == env->regs[R_EAX] && d1 == env->regs[R_EDX]) { +        cpu_stq_data(env, a0, env->regs[R_EBX]); +        cpu_stq_data(env, a0 + 8, env->regs[R_ECX]); +        eflags |= CC_Z; +    } else { +        /* always do the store */ +        cpu_stq_data(env, a0, d0); +        cpu_stq_data(env, a0 + 8, d1); +        env->regs[R_EDX] = d1; +        env->regs[R_EAX] = d0; +        eflags &= ~CC_Z; +    } +    CC_SRC = eflags; +} +#endif + +void helper_boundw(CPUX86State *env, target_ulong a0, int v) +{ +    int low, high; + +    low = cpu_ldsw_data(env, a0); +    high = cpu_ldsw_data(env, a0 + 2); +    v = (int16_t)v; +    if (v < low || v > high) { +        raise_exception(env, EXCP05_BOUND); +    } +} + +void helper_boundl(CPUX86State *env, target_ulong a0, int v) +{ +    int low, high; + +    low = cpu_ldl_data(env, a0); +    high = cpu_ldl_data(env, a0 + 4); +    if (v < low || v > high) { +        raise_exception(env, EXCP05_BOUND); +    } +} + +#if !defined(CONFIG_USER_ONLY) +/* try to fill the TLB and return an exception if error. If retaddr is + * NULL, it means that the function was called in C code (i.e. not + * from generated code or from helper.c) + */ +/* XXX: fix it to restore all registers */ +void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx, +              uintptr_t retaddr) +{ +    int ret; + +    ret = x86_cpu_handle_mmu_fault(cs, addr, is_write, mmu_idx); +    if (ret) { +        X86CPU *cpu = X86_CPU(cs); +        CPUX86State *env = &cpu->env; + +        if (retaddr) { +            /* now we have a real cpu fault */ +            cpu_restore_state(cs, retaddr); +        } +        raise_exception_err(env, cs->exception_index, env->error_code); +    } +} +#endif diff --git a/target-i386/misc_helper.c b/target-i386/misc_helper.c new file mode 100644 index 00000000..52c5d65e --- /dev/null +++ b/target-i386/misc_helper.c @@ -0,0 +1,635 @@ +/* + *  x86 misc helpers + * + *  Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "cpu.h" +#include "exec/helper-proto.h" +#include "exec/cpu_ldst.h" +#include "exec/address-spaces.h" + +void helper_outb(CPUX86State *env, uint32_t port, uint32_t data) +{ +#ifdef CONFIG_USER_ONLY +    fprintf(stderr, "outb: port=0x%04x, data=%02x\n", port, data); +#else +    address_space_stb(&address_space_io, port, data, +                      cpu_get_mem_attrs(env), NULL); +#endif +} + +target_ulong helper_inb(CPUX86State *env, uint32_t port) +{ +#ifdef CONFIG_USER_ONLY +    fprintf(stderr, "inb: port=0x%04x\n", port); +    return 0; +#else +    return address_space_ldub(&address_space_io, port, +                              cpu_get_mem_attrs(env), NULL); +#endif +} + +void helper_outw(CPUX86State *env, uint32_t port, uint32_t data) +{ +#ifdef CONFIG_USER_ONLY +    fprintf(stderr, "outw: port=0x%04x, data=%04x\n", port, data); +#else +    address_space_stw(&address_space_io, port, data, +                      cpu_get_mem_attrs(env), NULL); +#endif +} + +target_ulong helper_inw(CPUX86State *env, uint32_t port) +{ +#ifdef CONFIG_USER_ONLY +    fprintf(stderr, "inw: port=0x%04x\n", port); +    return 0; +#else +    return address_space_lduw(&address_space_io, port, +                              cpu_get_mem_attrs(env), NULL); +#endif +} + +void helper_outl(CPUX86State *env, uint32_t port, uint32_t data) +{ +#ifdef CONFIG_USER_ONLY +    fprintf(stderr, "outw: port=0x%04x, data=%08x\n", port, data); +#else +    address_space_stl(&address_space_io, port, data, +                      cpu_get_mem_attrs(env), NULL); +#endif +} + +target_ulong helper_inl(CPUX86State *env, uint32_t port) +{ +#ifdef CONFIG_USER_ONLY +    fprintf(stderr, "inl: port=0x%04x\n", port); +    return 0; +#else +    return address_space_ldl(&address_space_io, port, +                             cpu_get_mem_attrs(env), NULL); +#endif +} + +void helper_into(CPUX86State *env, int next_eip_addend) +{ +    int eflags; + +    eflags = cpu_cc_compute_all(env, CC_OP); +    if (eflags & CC_O) { +        raise_interrupt(env, EXCP04_INTO, 1, 0, next_eip_addend); +    } +} + +void helper_single_step(CPUX86State *env) +{ +#ifndef CONFIG_USER_ONLY +    check_hw_breakpoints(env, true); +    env->dr[6] |= DR6_BS; +#endif +    raise_exception(env, EXCP01_DB); +} + +void helper_cpuid(CPUX86State *env) +{ +    uint32_t eax, ebx, ecx, edx; + +    cpu_svm_check_intercept_param(env, SVM_EXIT_CPUID, 0); + +    cpu_x86_cpuid(env, (uint32_t)env->regs[R_EAX], (uint32_t)env->regs[R_ECX], +                  &eax, &ebx, &ecx, &edx); +    env->regs[R_EAX] = eax; +    env->regs[R_EBX] = ebx; +    env->regs[R_ECX] = ecx; +    env->regs[R_EDX] = edx; +} + +#if defined(CONFIG_USER_ONLY) +target_ulong helper_read_crN(CPUX86State *env, int reg) +{ +    return 0; +} + +void helper_write_crN(CPUX86State *env, int reg, target_ulong t0) +{ +} + +void helper_movl_drN_T0(CPUX86State *env, int reg, target_ulong t0) +{ +} +#else +target_ulong helper_read_crN(CPUX86State *env, int reg) +{ +    target_ulong val; + +    cpu_svm_check_intercept_param(env, SVM_EXIT_READ_CR0 + reg, 0); +    switch (reg) { +    default: +        val = env->cr[reg]; +        break; +    case 8: +        if (!(env->hflags2 & HF2_VINTR_MASK)) { +            val = cpu_get_apic_tpr(x86_env_get_cpu(env)->apic_state); +        } else { +            val = env->v_tpr; +        } +        break; +    } +    return val; +} + +void helper_write_crN(CPUX86State *env, int reg, target_ulong t0) +{ +    cpu_svm_check_intercept_param(env, SVM_EXIT_WRITE_CR0 + reg, 0); +    switch (reg) { +    case 0: +        cpu_x86_update_cr0(env, t0); +        break; +    case 3: +        cpu_x86_update_cr3(env, t0); +        break; +    case 4: +        cpu_x86_update_cr4(env, t0); +        break; +    case 8: +        if (!(env->hflags2 & HF2_VINTR_MASK)) { +            cpu_set_apic_tpr(x86_env_get_cpu(env)->apic_state, t0); +        } +        env->v_tpr = t0 & 0x0f; +        break; +    default: +        env->cr[reg] = t0; +        break; +    } +} + +void helper_movl_drN_T0(CPUX86State *env, int reg, target_ulong t0) +{ +    int i; + +    if (reg < 4) { +        hw_breakpoint_remove(env, reg); +        env->dr[reg] = t0; +        hw_breakpoint_insert(env, reg); +    } else if (reg == 7) { +        for (i = 0; i < DR7_MAX_BP; i++) { +            hw_breakpoint_remove(env, i); +        } +        env->dr[7] = t0; +        for (i = 0; i < DR7_MAX_BP; i++) { +            hw_breakpoint_insert(env, i); +        } +    } else { +        env->dr[reg] = t0; +    } +} +#endif + +void helper_lmsw(CPUX86State *env, target_ulong t0) +{ +    /* only 4 lower bits of CR0 are modified. PE cannot be set to zero +       if already set to one. */ +    t0 = (env->cr[0] & ~0xe) | (t0 & 0xf); +    helper_write_crN(env, 0, t0); +} + +void helper_invlpg(CPUX86State *env, target_ulong addr) +{ +    X86CPU *cpu = x86_env_get_cpu(env); + +    cpu_svm_check_intercept_param(env, SVM_EXIT_INVLPG, 0); +    tlb_flush_page(CPU(cpu), addr); +} + +void helper_rdtsc(CPUX86State *env) +{ +    uint64_t val; + +    if ((env->cr[4] & CR4_TSD_MASK) && ((env->hflags & HF_CPL_MASK) != 0)) { +        raise_exception(env, EXCP0D_GPF); +    } +    cpu_svm_check_intercept_param(env, SVM_EXIT_RDTSC, 0); + +    val = cpu_get_tsc(env) + env->tsc_offset; +    env->regs[R_EAX] = (uint32_t)(val); +    env->regs[R_EDX] = (uint32_t)(val >> 32); +} + +void helper_rdtscp(CPUX86State *env) +{ +    helper_rdtsc(env); +    env->regs[R_ECX] = (uint32_t)(env->tsc_aux); +} + +void helper_rdpmc(CPUX86State *env) +{ +    if ((env->cr[4] & CR4_PCE_MASK) && ((env->hflags & HF_CPL_MASK) != 0)) { +        raise_exception(env, EXCP0D_GPF); +    } +    cpu_svm_check_intercept_param(env, SVM_EXIT_RDPMC, 0); + +    /* currently unimplemented */ +    qemu_log_mask(LOG_UNIMP, "x86: unimplemented rdpmc\n"); +    raise_exception_err(env, EXCP06_ILLOP, 0); +} + +#if defined(CONFIG_USER_ONLY) +void helper_wrmsr(CPUX86State *env) +{ +} + +void helper_rdmsr(CPUX86State *env) +{ +} +#else +void helper_wrmsr(CPUX86State *env) +{ +    uint64_t val; + +    cpu_svm_check_intercept_param(env, SVM_EXIT_MSR, 1); + +    val = ((uint32_t)env->regs[R_EAX]) | +        ((uint64_t)((uint32_t)env->regs[R_EDX]) << 32); + +    switch ((uint32_t)env->regs[R_ECX]) { +    case MSR_IA32_SYSENTER_CS: +        env->sysenter_cs = val & 0xffff; +        break; +    case MSR_IA32_SYSENTER_ESP: +        env->sysenter_esp = val; +        break; +    case MSR_IA32_SYSENTER_EIP: +        env->sysenter_eip = val; +        break; +    case MSR_IA32_APICBASE: +        cpu_set_apic_base(x86_env_get_cpu(env)->apic_state, val); +        break; +    case MSR_EFER: +        { +            uint64_t update_mask; + +            update_mask = 0; +            if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_SYSCALL) { +                update_mask |= MSR_EFER_SCE; +            } +            if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) { +                update_mask |= MSR_EFER_LME; +            } +            if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_FFXSR) { +                update_mask |= MSR_EFER_FFXSR; +            } +            if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_NX) { +                update_mask |= MSR_EFER_NXE; +            } +            if (env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_SVM) { +                update_mask |= MSR_EFER_SVME; +            } +            if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_FFXSR) { +                update_mask |= MSR_EFER_FFXSR; +            } +            cpu_load_efer(env, (env->efer & ~update_mask) | +                          (val & update_mask)); +        } +        break; +    case MSR_STAR: +        env->star = val; +        break; +    case MSR_PAT: +        env->pat = val; +        break; +    case MSR_VM_HSAVE_PA: +        env->vm_hsave = val; +        break; +#ifdef TARGET_X86_64 +    case MSR_LSTAR: +        env->lstar = val; +        break; +    case MSR_CSTAR: +        env->cstar = val; +        break; +    case MSR_FMASK: +        env->fmask = val; +        break; +    case MSR_FSBASE: +        env->segs[R_FS].base = val; +        break; +    case MSR_GSBASE: +        env->segs[R_GS].base = val; +        break; +    case MSR_KERNELGSBASE: +        env->kernelgsbase = val; +        break; +#endif +    case MSR_MTRRphysBase(0): +    case MSR_MTRRphysBase(1): +    case MSR_MTRRphysBase(2): +    case MSR_MTRRphysBase(3): +    case MSR_MTRRphysBase(4): +    case MSR_MTRRphysBase(5): +    case MSR_MTRRphysBase(6): +    case MSR_MTRRphysBase(7): +        env->mtrr_var[((uint32_t)env->regs[R_ECX] - +                       MSR_MTRRphysBase(0)) / 2].base = val; +        break; +    case MSR_MTRRphysMask(0): +    case MSR_MTRRphysMask(1): +    case MSR_MTRRphysMask(2): +    case MSR_MTRRphysMask(3): +    case MSR_MTRRphysMask(4): +    case MSR_MTRRphysMask(5): +    case MSR_MTRRphysMask(6): +    case MSR_MTRRphysMask(7): +        env->mtrr_var[((uint32_t)env->regs[R_ECX] - +                       MSR_MTRRphysMask(0)) / 2].mask = val; +        break; +    case MSR_MTRRfix64K_00000: +        env->mtrr_fixed[(uint32_t)env->regs[R_ECX] - +                        MSR_MTRRfix64K_00000] = val; +        break; +    case MSR_MTRRfix16K_80000: +    case MSR_MTRRfix16K_A0000: +        env->mtrr_fixed[(uint32_t)env->regs[R_ECX] - +                        MSR_MTRRfix16K_80000 + 1] = val; +        break; +    case MSR_MTRRfix4K_C0000: +    case MSR_MTRRfix4K_C8000: +    case MSR_MTRRfix4K_D0000: +    case MSR_MTRRfix4K_D8000: +    case MSR_MTRRfix4K_E0000: +    case MSR_MTRRfix4K_E8000: +    case MSR_MTRRfix4K_F0000: +    case MSR_MTRRfix4K_F8000: +        env->mtrr_fixed[(uint32_t)env->regs[R_ECX] - +                        MSR_MTRRfix4K_C0000 + 3] = val; +        break; +    case MSR_MTRRdefType: +        env->mtrr_deftype = val; +        break; +    case MSR_MCG_STATUS: +        env->mcg_status = val; +        break; +    case MSR_MCG_CTL: +        if ((env->mcg_cap & MCG_CTL_P) +            && (val == 0 || val == ~(uint64_t)0)) { +            env->mcg_ctl = val; +        } +        break; +    case MSR_TSC_AUX: +        env->tsc_aux = val; +        break; +    case MSR_IA32_MISC_ENABLE: +        env->msr_ia32_misc_enable = val; +        break; +    default: +        if ((uint32_t)env->regs[R_ECX] >= MSR_MC0_CTL +            && (uint32_t)env->regs[R_ECX] < MSR_MC0_CTL + +            (4 * env->mcg_cap & 0xff)) { +            uint32_t offset = (uint32_t)env->regs[R_ECX] - MSR_MC0_CTL; +            if ((offset & 0x3) != 0 +                || (val == 0 || val == ~(uint64_t)0)) { +                env->mce_banks[offset] = val; +            } +            break; +        } +        /* XXX: exception? */ +        break; +    } +} + +void helper_rdmsr(CPUX86State *env) +{ +    uint64_t val; + +    cpu_svm_check_intercept_param(env, SVM_EXIT_MSR, 0); + +    switch ((uint32_t)env->regs[R_ECX]) { +    case MSR_IA32_SYSENTER_CS: +        val = env->sysenter_cs; +        break; +    case MSR_IA32_SYSENTER_ESP: +        val = env->sysenter_esp; +        break; +    case MSR_IA32_SYSENTER_EIP: +        val = env->sysenter_eip; +        break; +    case MSR_IA32_APICBASE: +        val = cpu_get_apic_base(x86_env_get_cpu(env)->apic_state); +        break; +    case MSR_EFER: +        val = env->efer; +        break; +    case MSR_STAR: +        val = env->star; +        break; +    case MSR_PAT: +        val = env->pat; +        break; +    case MSR_VM_HSAVE_PA: +        val = env->vm_hsave; +        break; +    case MSR_IA32_PERF_STATUS: +        /* tsc_increment_by_tick */ +        val = 1000ULL; +        /* CPU multiplier */ +        val |= (((uint64_t)4ULL) << 40); +        break; +#ifdef TARGET_X86_64 +    case MSR_LSTAR: +        val = env->lstar; +        break; +    case MSR_CSTAR: +        val = env->cstar; +        break; +    case MSR_FMASK: +        val = env->fmask; +        break; +    case MSR_FSBASE: +        val = env->segs[R_FS].base; +        break; +    case MSR_GSBASE: +        val = env->segs[R_GS].base; +        break; +    case MSR_KERNELGSBASE: +        val = env->kernelgsbase; +        break; +    case MSR_TSC_AUX: +        val = env->tsc_aux; +        break; +#endif +    case MSR_MTRRphysBase(0): +    case MSR_MTRRphysBase(1): +    case MSR_MTRRphysBase(2): +    case MSR_MTRRphysBase(3): +    case MSR_MTRRphysBase(4): +    case MSR_MTRRphysBase(5): +    case MSR_MTRRphysBase(6): +    case MSR_MTRRphysBase(7): +        val = env->mtrr_var[((uint32_t)env->regs[R_ECX] - +                             MSR_MTRRphysBase(0)) / 2].base; +        break; +    case MSR_MTRRphysMask(0): +    case MSR_MTRRphysMask(1): +    case MSR_MTRRphysMask(2): +    case MSR_MTRRphysMask(3): +    case MSR_MTRRphysMask(4): +    case MSR_MTRRphysMask(5): +    case MSR_MTRRphysMask(6): +    case MSR_MTRRphysMask(7): +        val = env->mtrr_var[((uint32_t)env->regs[R_ECX] - +                             MSR_MTRRphysMask(0)) / 2].mask; +        break; +    case MSR_MTRRfix64K_00000: +        val = env->mtrr_fixed[0]; +        break; +    case MSR_MTRRfix16K_80000: +    case MSR_MTRRfix16K_A0000: +        val = env->mtrr_fixed[(uint32_t)env->regs[R_ECX] - +                              MSR_MTRRfix16K_80000 + 1]; +        break; +    case MSR_MTRRfix4K_C0000: +    case MSR_MTRRfix4K_C8000: +    case MSR_MTRRfix4K_D0000: +    case MSR_MTRRfix4K_D8000: +    case MSR_MTRRfix4K_E0000: +    case MSR_MTRRfix4K_E8000: +    case MSR_MTRRfix4K_F0000: +    case MSR_MTRRfix4K_F8000: +        val = env->mtrr_fixed[(uint32_t)env->regs[R_ECX] - +                              MSR_MTRRfix4K_C0000 + 3]; +        break; +    case MSR_MTRRdefType: +        val = env->mtrr_deftype; +        break; +    case MSR_MTRRcap: +        if (env->features[FEAT_1_EDX] & CPUID_MTRR) { +            val = MSR_MTRRcap_VCNT | MSR_MTRRcap_FIXRANGE_SUPPORT | +                MSR_MTRRcap_WC_SUPPORTED; +        } else { +            /* XXX: exception? */ +            val = 0; +        } +        break; +    case MSR_MCG_CAP: +        val = env->mcg_cap; +        break; +    case MSR_MCG_CTL: +        if (env->mcg_cap & MCG_CTL_P) { +            val = env->mcg_ctl; +        } else { +            val = 0; +        } +        break; +    case MSR_MCG_STATUS: +        val = env->mcg_status; +        break; +    case MSR_IA32_MISC_ENABLE: +        val = env->msr_ia32_misc_enable; +        break; +    default: +        if ((uint32_t)env->regs[R_ECX] >= MSR_MC0_CTL +            && (uint32_t)env->regs[R_ECX] < MSR_MC0_CTL + +            (4 * env->mcg_cap & 0xff)) { +            uint32_t offset = (uint32_t)env->regs[R_ECX] - MSR_MC0_CTL; +            val = env->mce_banks[offset]; +            break; +        } +        /* XXX: exception? */ +        val = 0; +        break; +    } +    env->regs[R_EAX] = (uint32_t)(val); +    env->regs[R_EDX] = (uint32_t)(val >> 32); +} +#endif + +static void do_pause(X86CPU *cpu) +{ +    CPUState *cs = CPU(cpu); + +    /* Just let another CPU run.  */ +    cs->exception_index = EXCP_INTERRUPT; +    cpu_loop_exit(cs); +} + +static void do_hlt(X86CPU *cpu) +{ +    CPUState *cs = CPU(cpu); +    CPUX86State *env = &cpu->env; + +    env->hflags &= ~HF_INHIBIT_IRQ_MASK; /* needed if sti is just before */ +    cs->halted = 1; +    cs->exception_index = EXCP_HLT; +    cpu_loop_exit(cs); +} + +void helper_hlt(CPUX86State *env, int next_eip_addend) +{ +    X86CPU *cpu = x86_env_get_cpu(env); + +    cpu_svm_check_intercept_param(env, SVM_EXIT_HLT, 0); +    env->eip += next_eip_addend; + +    do_hlt(cpu); +} + +void helper_monitor(CPUX86State *env, target_ulong ptr) +{ +    if ((uint32_t)env->regs[R_ECX] != 0) { +        raise_exception(env, EXCP0D_GPF); +    } +    /* XXX: store address? */ +    cpu_svm_check_intercept_param(env, SVM_EXIT_MONITOR, 0); +} + +void helper_mwait(CPUX86State *env, int next_eip_addend) +{ +    CPUState *cs; +    X86CPU *cpu; + +    if ((uint32_t)env->regs[R_ECX] != 0) { +        raise_exception(env, EXCP0D_GPF); +    } +    cpu_svm_check_intercept_param(env, SVM_EXIT_MWAIT, 0); +    env->eip += next_eip_addend; + +    cpu = x86_env_get_cpu(env); +    cs = CPU(cpu); +    /* XXX: not complete but not completely erroneous */ +    if (cs->cpu_index != 0 || CPU_NEXT(cs) != NULL) { +        do_pause(cpu); +    } else { +        do_hlt(cpu); +    } +} + +void helper_pause(CPUX86State *env, int next_eip_addend) +{ +    X86CPU *cpu = x86_env_get_cpu(env); + +    cpu_svm_check_intercept_param(env, SVM_EXIT_PAUSE, 0); +    env->eip += next_eip_addend; + +    do_pause(cpu); +} + +void helper_debug(CPUX86State *env) +{ +    CPUState *cs = CPU(x86_env_get_cpu(env)); + +    cs->exception_index = EXCP_DEBUG; +    cpu_loop_exit(cs); +} diff --git a/target-i386/ops_sse.h b/target-i386/ops_sse.h new file mode 100644 index 00000000..a9e19052 --- /dev/null +++ b/target-i386/ops_sse.h @@ -0,0 +1,2296 @@ +/* + *  MMX/3DNow!/SSE/SSE2/SSE3/SSSE3/SSE4/PNI support + * + *  Copyright (c) 2005 Fabrice Bellard + *  Copyright (c) 2008 Intel Corporation  <andrew.zaborowski@intel.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "crypto/aes.h" + +#if SHIFT == 0 +#define Reg MMXReg +#define XMM_ONLY(...) +#define B(n) MMX_B(n) +#define W(n) MMX_W(n) +#define L(n) MMX_L(n) +#define Q(n) q +#define SUFFIX _mmx +#else +#define Reg XMMReg +#define XMM_ONLY(...) __VA_ARGS__ +#define B(n) XMM_B(n) +#define W(n) XMM_W(n) +#define L(n) XMM_L(n) +#define Q(n) XMM_Q(n) +#define SUFFIX _xmm +#endif + +void glue(helper_psrlw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    int shift; + +    if (s->Q(0) > 15) { +        d->Q(0) = 0; +#if SHIFT == 1 +        d->Q(1) = 0; +#endif +    } else { +        shift = s->B(0); +        d->W(0) >>= shift; +        d->W(1) >>= shift; +        d->W(2) >>= shift; +        d->W(3) >>= shift; +#if SHIFT == 1 +        d->W(4) >>= shift; +        d->W(5) >>= shift; +        d->W(6) >>= shift; +        d->W(7) >>= shift; +#endif +    } +} + +void glue(helper_psraw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    int shift; + +    if (s->Q(0) > 15) { +        shift = 15; +    } else { +        shift = s->B(0); +    } +    d->W(0) = (int16_t)d->W(0) >> shift; +    d->W(1) = (int16_t)d->W(1) >> shift; +    d->W(2) = (int16_t)d->W(2) >> shift; +    d->W(3) = (int16_t)d->W(3) >> shift; +#if SHIFT == 1 +    d->W(4) = (int16_t)d->W(4) >> shift; +    d->W(5) = (int16_t)d->W(5) >> shift; +    d->W(6) = (int16_t)d->W(6) >> shift; +    d->W(7) = (int16_t)d->W(7) >> shift; +#endif +} + +void glue(helper_psllw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    int shift; + +    if (s->Q(0) > 15) { +        d->Q(0) = 0; +#if SHIFT == 1 +        d->Q(1) = 0; +#endif +    } else { +        shift = s->B(0); +        d->W(0) <<= shift; +        d->W(1) <<= shift; +        d->W(2) <<= shift; +        d->W(3) <<= shift; +#if SHIFT == 1 +        d->W(4) <<= shift; +        d->W(5) <<= shift; +        d->W(6) <<= shift; +        d->W(7) <<= shift; +#endif +    } +} + +void glue(helper_psrld, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    int shift; + +    if (s->Q(0) > 31) { +        d->Q(0) = 0; +#if SHIFT == 1 +        d->Q(1) = 0; +#endif +    } else { +        shift = s->B(0); +        d->L(0) >>= shift; +        d->L(1) >>= shift; +#if SHIFT == 1 +        d->L(2) >>= shift; +        d->L(3) >>= shift; +#endif +    } +} + +void glue(helper_psrad, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    int shift; + +    if (s->Q(0) > 31) { +        shift = 31; +    } else { +        shift = s->B(0); +    } +    d->L(0) = (int32_t)d->L(0) >> shift; +    d->L(1) = (int32_t)d->L(1) >> shift; +#if SHIFT == 1 +    d->L(2) = (int32_t)d->L(2) >> shift; +    d->L(3) = (int32_t)d->L(3) >> shift; +#endif +} + +void glue(helper_pslld, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    int shift; + +    if (s->Q(0) > 31) { +        d->Q(0) = 0; +#if SHIFT == 1 +        d->Q(1) = 0; +#endif +    } else { +        shift = s->B(0); +        d->L(0) <<= shift; +        d->L(1) <<= shift; +#if SHIFT == 1 +        d->L(2) <<= shift; +        d->L(3) <<= shift; +#endif +    } +} + +void glue(helper_psrlq, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    int shift; + +    if (s->Q(0) > 63) { +        d->Q(0) = 0; +#if SHIFT == 1 +        d->Q(1) = 0; +#endif +    } else { +        shift = s->B(0); +        d->Q(0) >>= shift; +#if SHIFT == 1 +        d->Q(1) >>= shift; +#endif +    } +} + +void glue(helper_psllq, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    int shift; + +    if (s->Q(0) > 63) { +        d->Q(0) = 0; +#if SHIFT == 1 +        d->Q(1) = 0; +#endif +    } else { +        shift = s->B(0); +        d->Q(0) <<= shift; +#if SHIFT == 1 +        d->Q(1) <<= shift; +#endif +    } +} + +#if SHIFT == 1 +void glue(helper_psrldq, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    int shift, i; + +    shift = s->L(0); +    if (shift > 16) { +        shift = 16; +    } +    for (i = 0; i < 16 - shift; i++) { +        d->B(i) = d->B(i + shift); +    } +    for (i = 16 - shift; i < 16; i++) { +        d->B(i) = 0; +    } +} + +void glue(helper_pslldq, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    int shift, i; + +    shift = s->L(0); +    if (shift > 16) { +        shift = 16; +    } +    for (i = 15; i >= shift; i--) { +        d->B(i) = d->B(i - shift); +    } +    for (i = 0; i < shift; i++) { +        d->B(i) = 0; +    } +} +#endif + +#define SSE_HELPER_B(name, F)                                   \ +    void glue(name, SUFFIX)(CPUX86State *env, Reg *d, Reg *s)   \ +    {                                                           \ +        d->B(0) = F(d->B(0), s->B(0));                          \ +        d->B(1) = F(d->B(1), s->B(1));                          \ +        d->B(2) = F(d->B(2), s->B(2));                          \ +        d->B(3) = F(d->B(3), s->B(3));                          \ +        d->B(4) = F(d->B(4), s->B(4));                          \ +        d->B(5) = F(d->B(5), s->B(5));                          \ +        d->B(6) = F(d->B(6), s->B(6));                          \ +        d->B(7) = F(d->B(7), s->B(7));                          \ +        XMM_ONLY(                                               \ +                 d->B(8) = F(d->B(8), s->B(8));                 \ +                 d->B(9) = F(d->B(9), s->B(9));                 \ +                 d->B(10) = F(d->B(10), s->B(10));              \ +                 d->B(11) = F(d->B(11), s->B(11));              \ +                 d->B(12) = F(d->B(12), s->B(12));              \ +                 d->B(13) = F(d->B(13), s->B(13));              \ +                 d->B(14) = F(d->B(14), s->B(14));              \ +                 d->B(15) = F(d->B(15), s->B(15));              \ +                                                        )       \ +            } + +#define SSE_HELPER_W(name, F)                                   \ +    void glue(name, SUFFIX)(CPUX86State *env, Reg *d, Reg *s)   \ +    {                                                           \ +        d->W(0) = F(d->W(0), s->W(0));                          \ +        d->W(1) = F(d->W(1), s->W(1));                          \ +        d->W(2) = F(d->W(2), s->W(2));                          \ +        d->W(3) = F(d->W(3), s->W(3));                          \ +        XMM_ONLY(                                               \ +                 d->W(4) = F(d->W(4), s->W(4));                 \ +                 d->W(5) = F(d->W(5), s->W(5));                 \ +                 d->W(6) = F(d->W(6), s->W(6));                 \ +                 d->W(7) = F(d->W(7), s->W(7));                 \ +                                                        )       \ +            } + +#define SSE_HELPER_L(name, F)                                   \ +    void glue(name, SUFFIX)(CPUX86State *env, Reg *d, Reg *s)   \ +    {                                                           \ +        d->L(0) = F(d->L(0), s->L(0));                          \ +        d->L(1) = F(d->L(1), s->L(1));                          \ +        XMM_ONLY(                                               \ +                 d->L(2) = F(d->L(2), s->L(2));                 \ +                 d->L(3) = F(d->L(3), s->L(3));                 \ +                                                        )       \ +            } + +#define SSE_HELPER_Q(name, F)                                   \ +    void glue(name, SUFFIX)(CPUX86State *env, Reg *d, Reg *s)   \ +    {                                                           \ +        d->Q(0) = F(d->Q(0), s->Q(0));                          \ +        XMM_ONLY(                                               \ +                 d->Q(1) = F(d->Q(1), s->Q(1));                 \ +                                                        )       \ +            } + +#if SHIFT == 0 +static inline int satub(int x) +{ +    if (x < 0) { +        return 0; +    } else if (x > 255) { +        return 255; +    } else { +        return x; +    } +} + +static inline int satuw(int x) +{ +    if (x < 0) { +        return 0; +    } else if (x > 65535) { +        return 65535; +    } else { +        return x; +    } +} + +static inline int satsb(int x) +{ +    if (x < -128) { +        return -128; +    } else if (x > 127) { +        return 127; +    } else { +        return x; +    } +} + +static inline int satsw(int x) +{ +    if (x < -32768) { +        return -32768; +    } else if (x > 32767) { +        return 32767; +    } else { +        return x; +    } +} + +#define FADD(a, b) ((a) + (b)) +#define FADDUB(a, b) satub((a) + (b)) +#define FADDUW(a, b) satuw((a) + (b)) +#define FADDSB(a, b) satsb((int8_t)(a) + (int8_t)(b)) +#define FADDSW(a, b) satsw((int16_t)(a) + (int16_t)(b)) + +#define FSUB(a, b) ((a) - (b)) +#define FSUBUB(a, b) satub((a) - (b)) +#define FSUBUW(a, b) satuw((a) - (b)) +#define FSUBSB(a, b) satsb((int8_t)(a) - (int8_t)(b)) +#define FSUBSW(a, b) satsw((int16_t)(a) - (int16_t)(b)) +#define FMINUB(a, b) ((a) < (b)) ? (a) : (b) +#define FMINSW(a, b) ((int16_t)(a) < (int16_t)(b)) ? (a) : (b) +#define FMAXUB(a, b) ((a) > (b)) ? (a) : (b) +#define FMAXSW(a, b) ((int16_t)(a) > (int16_t)(b)) ? (a) : (b) + +#define FAND(a, b) ((a) & (b)) +#define FANDN(a, b) ((~(a)) & (b)) +#define FOR(a, b) ((a) | (b)) +#define FXOR(a, b) ((a) ^ (b)) + +#define FCMPGTB(a, b) ((int8_t)(a) > (int8_t)(b) ? -1 : 0) +#define FCMPGTW(a, b) ((int16_t)(a) > (int16_t)(b) ? -1 : 0) +#define FCMPGTL(a, b) ((int32_t)(a) > (int32_t)(b) ? -1 : 0) +#define FCMPEQ(a, b) ((a) == (b) ? -1 : 0) + +#define FMULLW(a, b) ((a) * (b)) +#define FMULHRW(a, b) (((int16_t)(a) * (int16_t)(b) + 0x8000) >> 16) +#define FMULHUW(a, b) ((a) * (b) >> 16) +#define FMULHW(a, b) ((int16_t)(a) * (int16_t)(b) >> 16) + +#define FAVG(a, b) (((a) + (b) + 1) >> 1) +#endif + +SSE_HELPER_B(helper_paddb, FADD) +SSE_HELPER_W(helper_paddw, FADD) +SSE_HELPER_L(helper_paddl, FADD) +SSE_HELPER_Q(helper_paddq, FADD) + +SSE_HELPER_B(helper_psubb, FSUB) +SSE_HELPER_W(helper_psubw, FSUB) +SSE_HELPER_L(helper_psubl, FSUB) +SSE_HELPER_Q(helper_psubq, FSUB) + +SSE_HELPER_B(helper_paddusb, FADDUB) +SSE_HELPER_B(helper_paddsb, FADDSB) +SSE_HELPER_B(helper_psubusb, FSUBUB) +SSE_HELPER_B(helper_psubsb, FSUBSB) + +SSE_HELPER_W(helper_paddusw, FADDUW) +SSE_HELPER_W(helper_paddsw, FADDSW) +SSE_HELPER_W(helper_psubusw, FSUBUW) +SSE_HELPER_W(helper_psubsw, FSUBSW) + +SSE_HELPER_B(helper_pminub, FMINUB) +SSE_HELPER_B(helper_pmaxub, FMAXUB) + +SSE_HELPER_W(helper_pminsw, FMINSW) +SSE_HELPER_W(helper_pmaxsw, FMAXSW) + +SSE_HELPER_Q(helper_pand, FAND) +SSE_HELPER_Q(helper_pandn, FANDN) +SSE_HELPER_Q(helper_por, FOR) +SSE_HELPER_Q(helper_pxor, FXOR) + +SSE_HELPER_B(helper_pcmpgtb, FCMPGTB) +SSE_HELPER_W(helper_pcmpgtw, FCMPGTW) +SSE_HELPER_L(helper_pcmpgtl, FCMPGTL) + +SSE_HELPER_B(helper_pcmpeqb, FCMPEQ) +SSE_HELPER_W(helper_pcmpeqw, FCMPEQ) +SSE_HELPER_L(helper_pcmpeql, FCMPEQ) + +SSE_HELPER_W(helper_pmullw, FMULLW) +#if SHIFT == 0 +SSE_HELPER_W(helper_pmulhrw, FMULHRW) +#endif +SSE_HELPER_W(helper_pmulhuw, FMULHUW) +SSE_HELPER_W(helper_pmulhw, FMULHW) + +SSE_HELPER_B(helper_pavgb, FAVG) +SSE_HELPER_W(helper_pavgw, FAVG) + +void glue(helper_pmuludq, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    d->Q(0) = (uint64_t)s->L(0) * (uint64_t)d->L(0); +#if SHIFT == 1 +    d->Q(1) = (uint64_t)s->L(2) * (uint64_t)d->L(2); +#endif +} + +void glue(helper_pmaddwd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    int i; + +    for (i = 0; i < (2 << SHIFT); i++) { +        d->L(i) = (int16_t)s->W(2 * i) * (int16_t)d->W(2 * i) + +            (int16_t)s->W(2 * i + 1) * (int16_t)d->W(2 * i + 1); +    } +} + +#if SHIFT == 0 +static inline int abs1(int a) +{ +    if (a < 0) { +        return -a; +    } else { +        return a; +    } +} +#endif +void glue(helper_psadbw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    unsigned int val; + +    val = 0; +    val += abs1(d->B(0) - s->B(0)); +    val += abs1(d->B(1) - s->B(1)); +    val += abs1(d->B(2) - s->B(2)); +    val += abs1(d->B(3) - s->B(3)); +    val += abs1(d->B(4) - s->B(4)); +    val += abs1(d->B(5) - s->B(5)); +    val += abs1(d->B(6) - s->B(6)); +    val += abs1(d->B(7) - s->B(7)); +    d->Q(0) = val; +#if SHIFT == 1 +    val = 0; +    val += abs1(d->B(8) - s->B(8)); +    val += abs1(d->B(9) - s->B(9)); +    val += abs1(d->B(10) - s->B(10)); +    val += abs1(d->B(11) - s->B(11)); +    val += abs1(d->B(12) - s->B(12)); +    val += abs1(d->B(13) - s->B(13)); +    val += abs1(d->B(14) - s->B(14)); +    val += abs1(d->B(15) - s->B(15)); +    d->Q(1) = val; +#endif +} + +void glue(helper_maskmov, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, +                                  target_ulong a0) +{ +    int i; + +    for (i = 0; i < (8 << SHIFT); i++) { +        if (s->B(i) & 0x80) { +            cpu_stb_data(env, a0 + i, d->B(i)); +        } +    } +} + +void glue(helper_movl_mm_T0, SUFFIX)(Reg *d, uint32_t val) +{ +    d->L(0) = val; +    d->L(1) = 0; +#if SHIFT == 1 +    d->Q(1) = 0; +#endif +} + +#ifdef TARGET_X86_64 +void glue(helper_movq_mm_T0, SUFFIX)(Reg *d, uint64_t val) +{ +    d->Q(0) = val; +#if SHIFT == 1 +    d->Q(1) = 0; +#endif +} +#endif + +#if SHIFT == 0 +void glue(helper_pshufw, SUFFIX)(Reg *d, Reg *s, int order) +{ +    Reg r; + +    r.W(0) = s->W(order & 3); +    r.W(1) = s->W((order >> 2) & 3); +    r.W(2) = s->W((order >> 4) & 3); +    r.W(3) = s->W((order >> 6) & 3); +    *d = r; +} +#else +void helper_shufps(Reg *d, Reg *s, int order) +{ +    Reg r; + +    r.L(0) = d->L(order & 3); +    r.L(1) = d->L((order >> 2) & 3); +    r.L(2) = s->L((order >> 4) & 3); +    r.L(3) = s->L((order >> 6) & 3); +    *d = r; +} + +void helper_shufpd(Reg *d, Reg *s, int order) +{ +    Reg r; + +    r.Q(0) = d->Q(order & 1); +    r.Q(1) = s->Q((order >> 1) & 1); +    *d = r; +} + +void glue(helper_pshufd, SUFFIX)(Reg *d, Reg *s, int order) +{ +    Reg r; + +    r.L(0) = s->L(order & 3); +    r.L(1) = s->L((order >> 2) & 3); +    r.L(2) = s->L((order >> 4) & 3); +    r.L(3) = s->L((order >> 6) & 3); +    *d = r; +} + +void glue(helper_pshuflw, SUFFIX)(Reg *d, Reg *s, int order) +{ +    Reg r; + +    r.W(0) = s->W(order & 3); +    r.W(1) = s->W((order >> 2) & 3); +    r.W(2) = s->W((order >> 4) & 3); +    r.W(3) = s->W((order >> 6) & 3); +    r.Q(1) = s->Q(1); +    *d = r; +} + +void glue(helper_pshufhw, SUFFIX)(Reg *d, Reg *s, int order) +{ +    Reg r; + +    r.Q(0) = s->Q(0); +    r.W(4) = s->W(4 + (order & 3)); +    r.W(5) = s->W(4 + ((order >> 2) & 3)); +    r.W(6) = s->W(4 + ((order >> 4) & 3)); +    r.W(7) = s->W(4 + ((order >> 6) & 3)); +    *d = r; +} +#endif + +#if SHIFT == 1 +/* FPU ops */ +/* XXX: not accurate */ + +#define SSE_HELPER_S(name, F)                                           \ +    void helper_ ## name ## ps(CPUX86State *env, Reg *d, Reg *s)        \ +    {                                                                   \ +        d->XMM_S(0) = F(32, d->XMM_S(0), s->XMM_S(0));                  \ +        d->XMM_S(1) = F(32, d->XMM_S(1), s->XMM_S(1));                  \ +        d->XMM_S(2) = F(32, d->XMM_S(2), s->XMM_S(2));                  \ +        d->XMM_S(3) = F(32, d->XMM_S(3), s->XMM_S(3));                  \ +    }                                                                   \ +                                                                        \ +    void helper_ ## name ## ss(CPUX86State *env, Reg *d, Reg *s)        \ +    {                                                                   \ +        d->XMM_S(0) = F(32, d->XMM_S(0), s->XMM_S(0));                  \ +    }                                                                   \ +                                                                        \ +    void helper_ ## name ## pd(CPUX86State *env, Reg *d, Reg *s)        \ +    {                                                                   \ +        d->XMM_D(0) = F(64, d->XMM_D(0), s->XMM_D(0));                  \ +        d->XMM_D(1) = F(64, d->XMM_D(1), s->XMM_D(1));                  \ +    }                                                                   \ +                                                                        \ +    void helper_ ## name ## sd(CPUX86State *env, Reg *d, Reg *s)        \ +    {                                                                   \ +        d->XMM_D(0) = F(64, d->XMM_D(0), s->XMM_D(0));                  \ +    } + +#define FPU_ADD(size, a, b) float ## size ## _add(a, b, &env->sse_status) +#define FPU_SUB(size, a, b) float ## size ## _sub(a, b, &env->sse_status) +#define FPU_MUL(size, a, b) float ## size ## _mul(a, b, &env->sse_status) +#define FPU_DIV(size, a, b) float ## size ## _div(a, b, &env->sse_status) +#define FPU_SQRT(size, a, b) float ## size ## _sqrt(b, &env->sse_status) + +/* Note that the choice of comparison op here is important to get the + * special cases right: for min and max Intel specifies that (-0,0), + * (NaN, anything) and (anything, NaN) return the second argument. + */ +#define FPU_MIN(size, a, b)                                     \ +    (float ## size ## _lt(a, b, &env->sse_status) ? (a) : (b)) +#define FPU_MAX(size, a, b)                                     \ +    (float ## size ## _lt(b, a, &env->sse_status) ? (a) : (b)) + +SSE_HELPER_S(add, FPU_ADD) +SSE_HELPER_S(sub, FPU_SUB) +SSE_HELPER_S(mul, FPU_MUL) +SSE_HELPER_S(div, FPU_DIV) +SSE_HELPER_S(min, FPU_MIN) +SSE_HELPER_S(max, FPU_MAX) +SSE_HELPER_S(sqrt, FPU_SQRT) + + +/* float to float conversions */ +void helper_cvtps2pd(CPUX86State *env, Reg *d, Reg *s) +{ +    float32 s0, s1; + +    s0 = s->XMM_S(0); +    s1 = s->XMM_S(1); +    d->XMM_D(0) = float32_to_float64(s0, &env->sse_status); +    d->XMM_D(1) = float32_to_float64(s1, &env->sse_status); +} + +void helper_cvtpd2ps(CPUX86State *env, Reg *d, Reg *s) +{ +    d->XMM_S(0) = float64_to_float32(s->XMM_D(0), &env->sse_status); +    d->XMM_S(1) = float64_to_float32(s->XMM_D(1), &env->sse_status); +    d->Q(1) = 0; +} + +void helper_cvtss2sd(CPUX86State *env, Reg *d, Reg *s) +{ +    d->XMM_D(0) = float32_to_float64(s->XMM_S(0), &env->sse_status); +} + +void helper_cvtsd2ss(CPUX86State *env, Reg *d, Reg *s) +{ +    d->XMM_S(0) = float64_to_float32(s->XMM_D(0), &env->sse_status); +} + +/* integer to float */ +void helper_cvtdq2ps(CPUX86State *env, Reg *d, Reg *s) +{ +    d->XMM_S(0) = int32_to_float32(s->XMM_L(0), &env->sse_status); +    d->XMM_S(1) = int32_to_float32(s->XMM_L(1), &env->sse_status); +    d->XMM_S(2) = int32_to_float32(s->XMM_L(2), &env->sse_status); +    d->XMM_S(3) = int32_to_float32(s->XMM_L(3), &env->sse_status); +} + +void helper_cvtdq2pd(CPUX86State *env, Reg *d, Reg *s) +{ +    int32_t l0, l1; + +    l0 = (int32_t)s->XMM_L(0); +    l1 = (int32_t)s->XMM_L(1); +    d->XMM_D(0) = int32_to_float64(l0, &env->sse_status); +    d->XMM_D(1) = int32_to_float64(l1, &env->sse_status); +} + +void helper_cvtpi2ps(CPUX86State *env, XMMReg *d, MMXReg *s) +{ +    d->XMM_S(0) = int32_to_float32(s->MMX_L(0), &env->sse_status); +    d->XMM_S(1) = int32_to_float32(s->MMX_L(1), &env->sse_status); +} + +void helper_cvtpi2pd(CPUX86State *env, XMMReg *d, MMXReg *s) +{ +    d->XMM_D(0) = int32_to_float64(s->MMX_L(0), &env->sse_status); +    d->XMM_D(1) = int32_to_float64(s->MMX_L(1), &env->sse_status); +} + +void helper_cvtsi2ss(CPUX86State *env, XMMReg *d, uint32_t val) +{ +    d->XMM_S(0) = int32_to_float32(val, &env->sse_status); +} + +void helper_cvtsi2sd(CPUX86State *env, XMMReg *d, uint32_t val) +{ +    d->XMM_D(0) = int32_to_float64(val, &env->sse_status); +} + +#ifdef TARGET_X86_64 +void helper_cvtsq2ss(CPUX86State *env, XMMReg *d, uint64_t val) +{ +    d->XMM_S(0) = int64_to_float32(val, &env->sse_status); +} + +void helper_cvtsq2sd(CPUX86State *env, XMMReg *d, uint64_t val) +{ +    d->XMM_D(0) = int64_to_float64(val, &env->sse_status); +} +#endif + +/* float to integer */ +void helper_cvtps2dq(CPUX86State *env, XMMReg *d, XMMReg *s) +{ +    d->XMM_L(0) = float32_to_int32(s->XMM_S(0), &env->sse_status); +    d->XMM_L(1) = float32_to_int32(s->XMM_S(1), &env->sse_status); +    d->XMM_L(2) = float32_to_int32(s->XMM_S(2), &env->sse_status); +    d->XMM_L(3) = float32_to_int32(s->XMM_S(3), &env->sse_status); +} + +void helper_cvtpd2dq(CPUX86State *env, XMMReg *d, XMMReg *s) +{ +    d->XMM_L(0) = float64_to_int32(s->XMM_D(0), &env->sse_status); +    d->XMM_L(1) = float64_to_int32(s->XMM_D(1), &env->sse_status); +    d->XMM_Q(1) = 0; +} + +void helper_cvtps2pi(CPUX86State *env, MMXReg *d, XMMReg *s) +{ +    d->MMX_L(0) = float32_to_int32(s->XMM_S(0), &env->sse_status); +    d->MMX_L(1) = float32_to_int32(s->XMM_S(1), &env->sse_status); +} + +void helper_cvtpd2pi(CPUX86State *env, MMXReg *d, XMMReg *s) +{ +    d->MMX_L(0) = float64_to_int32(s->XMM_D(0), &env->sse_status); +    d->MMX_L(1) = float64_to_int32(s->XMM_D(1), &env->sse_status); +} + +int32_t helper_cvtss2si(CPUX86State *env, XMMReg *s) +{ +    return float32_to_int32(s->XMM_S(0), &env->sse_status); +} + +int32_t helper_cvtsd2si(CPUX86State *env, XMMReg *s) +{ +    return float64_to_int32(s->XMM_D(0), &env->sse_status); +} + +#ifdef TARGET_X86_64 +int64_t helper_cvtss2sq(CPUX86State *env, XMMReg *s) +{ +    return float32_to_int64(s->XMM_S(0), &env->sse_status); +} + +int64_t helper_cvtsd2sq(CPUX86State *env, XMMReg *s) +{ +    return float64_to_int64(s->XMM_D(0), &env->sse_status); +} +#endif + +/* float to integer truncated */ +void helper_cvttps2dq(CPUX86State *env, XMMReg *d, XMMReg *s) +{ +    d->XMM_L(0) = float32_to_int32_round_to_zero(s->XMM_S(0), &env->sse_status); +    d->XMM_L(1) = float32_to_int32_round_to_zero(s->XMM_S(1), &env->sse_status); +    d->XMM_L(2) = float32_to_int32_round_to_zero(s->XMM_S(2), &env->sse_status); +    d->XMM_L(3) = float32_to_int32_round_to_zero(s->XMM_S(3), &env->sse_status); +} + +void helper_cvttpd2dq(CPUX86State *env, XMMReg *d, XMMReg *s) +{ +    d->XMM_L(0) = float64_to_int32_round_to_zero(s->XMM_D(0), &env->sse_status); +    d->XMM_L(1) = float64_to_int32_round_to_zero(s->XMM_D(1), &env->sse_status); +    d->XMM_Q(1) = 0; +} + +void helper_cvttps2pi(CPUX86State *env, MMXReg *d, XMMReg *s) +{ +    d->MMX_L(0) = float32_to_int32_round_to_zero(s->XMM_S(0), &env->sse_status); +    d->MMX_L(1) = float32_to_int32_round_to_zero(s->XMM_S(1), &env->sse_status); +} + +void helper_cvttpd2pi(CPUX86State *env, MMXReg *d, XMMReg *s) +{ +    d->MMX_L(0) = float64_to_int32_round_to_zero(s->XMM_D(0), &env->sse_status); +    d->MMX_L(1) = float64_to_int32_round_to_zero(s->XMM_D(1), &env->sse_status); +} + +int32_t helper_cvttss2si(CPUX86State *env, XMMReg *s) +{ +    return float32_to_int32_round_to_zero(s->XMM_S(0), &env->sse_status); +} + +int32_t helper_cvttsd2si(CPUX86State *env, XMMReg *s) +{ +    return float64_to_int32_round_to_zero(s->XMM_D(0), &env->sse_status); +} + +#ifdef TARGET_X86_64 +int64_t helper_cvttss2sq(CPUX86State *env, XMMReg *s) +{ +    return float32_to_int64_round_to_zero(s->XMM_S(0), &env->sse_status); +} + +int64_t helper_cvttsd2sq(CPUX86State *env, XMMReg *s) +{ +    return float64_to_int64_round_to_zero(s->XMM_D(0), &env->sse_status); +} +#endif + +void helper_rsqrtps(CPUX86State *env, XMMReg *d, XMMReg *s) +{ +    d->XMM_S(0) = float32_div(float32_one, +                              float32_sqrt(s->XMM_S(0), &env->sse_status), +                              &env->sse_status); +    d->XMM_S(1) = float32_div(float32_one, +                              float32_sqrt(s->XMM_S(1), &env->sse_status), +                              &env->sse_status); +    d->XMM_S(2) = float32_div(float32_one, +                              float32_sqrt(s->XMM_S(2), &env->sse_status), +                              &env->sse_status); +    d->XMM_S(3) = float32_div(float32_one, +                              float32_sqrt(s->XMM_S(3), &env->sse_status), +                              &env->sse_status); +} + +void helper_rsqrtss(CPUX86State *env, XMMReg *d, XMMReg *s) +{ +    d->XMM_S(0) = float32_div(float32_one, +                              float32_sqrt(s->XMM_S(0), &env->sse_status), +                              &env->sse_status); +} + +void helper_rcpps(CPUX86State *env, XMMReg *d, XMMReg *s) +{ +    d->XMM_S(0) = float32_div(float32_one, s->XMM_S(0), &env->sse_status); +    d->XMM_S(1) = float32_div(float32_one, s->XMM_S(1), &env->sse_status); +    d->XMM_S(2) = float32_div(float32_one, s->XMM_S(2), &env->sse_status); +    d->XMM_S(3) = float32_div(float32_one, s->XMM_S(3), &env->sse_status); +} + +void helper_rcpss(CPUX86State *env, XMMReg *d, XMMReg *s) +{ +    d->XMM_S(0) = float32_div(float32_one, s->XMM_S(0), &env->sse_status); +} + +static inline uint64_t helper_extrq(uint64_t src, int shift, int len) +{ +    uint64_t mask; + +    if (len == 0) { +        mask = ~0LL; +    } else { +        mask = (1ULL << len) - 1; +    } +    return (src >> shift) & mask; +} + +void helper_extrq_r(CPUX86State *env, XMMReg *d, XMMReg *s) +{ +    d->XMM_Q(0) = helper_extrq(d->XMM_Q(0), s->XMM_B(1), s->XMM_B(0)); +} + +void helper_extrq_i(CPUX86State *env, XMMReg *d, int index, int length) +{ +    d->XMM_Q(0) = helper_extrq(d->XMM_Q(0), index, length); +} + +static inline uint64_t helper_insertq(uint64_t src, int shift, int len) +{ +    uint64_t mask; + +    if (len == 0) { +        mask = ~0ULL; +    } else { +        mask = (1ULL << len) - 1; +    } +    return (src & ~(mask << shift)) | ((src & mask) << shift); +} + +void helper_insertq_r(CPUX86State *env, XMMReg *d, XMMReg *s) +{ +    d->XMM_Q(0) = helper_insertq(s->XMM_Q(0), s->XMM_B(9), s->XMM_B(8)); +} + +void helper_insertq_i(CPUX86State *env, XMMReg *d, int index, int length) +{ +    d->XMM_Q(0) = helper_insertq(d->XMM_Q(0), index, length); +} + +void helper_haddps(CPUX86State *env, XMMReg *d, XMMReg *s) +{ +    XMMReg r; + +    r.XMM_S(0) = float32_add(d->XMM_S(0), d->XMM_S(1), &env->sse_status); +    r.XMM_S(1) = float32_add(d->XMM_S(2), d->XMM_S(3), &env->sse_status); +    r.XMM_S(2) = float32_add(s->XMM_S(0), s->XMM_S(1), &env->sse_status); +    r.XMM_S(3) = float32_add(s->XMM_S(2), s->XMM_S(3), &env->sse_status); +    *d = r; +} + +void helper_haddpd(CPUX86State *env, XMMReg *d, XMMReg *s) +{ +    XMMReg r; + +    r.XMM_D(0) = float64_add(d->XMM_D(0), d->XMM_D(1), &env->sse_status); +    r.XMM_D(1) = float64_add(s->XMM_D(0), s->XMM_D(1), &env->sse_status); +    *d = r; +} + +void helper_hsubps(CPUX86State *env, XMMReg *d, XMMReg *s) +{ +    XMMReg r; + +    r.XMM_S(0) = float32_sub(d->XMM_S(0), d->XMM_S(1), &env->sse_status); +    r.XMM_S(1) = float32_sub(d->XMM_S(2), d->XMM_S(3), &env->sse_status); +    r.XMM_S(2) = float32_sub(s->XMM_S(0), s->XMM_S(1), &env->sse_status); +    r.XMM_S(3) = float32_sub(s->XMM_S(2), s->XMM_S(3), &env->sse_status); +    *d = r; +} + +void helper_hsubpd(CPUX86State *env, XMMReg *d, XMMReg *s) +{ +    XMMReg r; + +    r.XMM_D(0) = float64_sub(d->XMM_D(0), d->XMM_D(1), &env->sse_status); +    r.XMM_D(1) = float64_sub(s->XMM_D(0), s->XMM_D(1), &env->sse_status); +    *d = r; +} + +void helper_addsubps(CPUX86State *env, XMMReg *d, XMMReg *s) +{ +    d->XMM_S(0) = float32_sub(d->XMM_S(0), s->XMM_S(0), &env->sse_status); +    d->XMM_S(1) = float32_add(d->XMM_S(1), s->XMM_S(1), &env->sse_status); +    d->XMM_S(2) = float32_sub(d->XMM_S(2), s->XMM_S(2), &env->sse_status); +    d->XMM_S(3) = float32_add(d->XMM_S(3), s->XMM_S(3), &env->sse_status); +} + +void helper_addsubpd(CPUX86State *env, XMMReg *d, XMMReg *s) +{ +    d->XMM_D(0) = float64_sub(d->XMM_D(0), s->XMM_D(0), &env->sse_status); +    d->XMM_D(1) = float64_add(d->XMM_D(1), s->XMM_D(1), &env->sse_status); +} + +/* XXX: unordered */ +#define SSE_HELPER_CMP(name, F)                                         \ +    void helper_ ## name ## ps(CPUX86State *env, Reg *d, Reg *s)        \ +    {                                                                   \ +        d->XMM_L(0) = F(32, d->XMM_S(0), s->XMM_S(0));                  \ +        d->XMM_L(1) = F(32, d->XMM_S(1), s->XMM_S(1));                  \ +        d->XMM_L(2) = F(32, d->XMM_S(2), s->XMM_S(2));                  \ +        d->XMM_L(3) = F(32, d->XMM_S(3), s->XMM_S(3));                  \ +    }                                                                   \ +                                                                        \ +    void helper_ ## name ## ss(CPUX86State *env, Reg *d, Reg *s)        \ +    {                                                                   \ +        d->XMM_L(0) = F(32, d->XMM_S(0), s->XMM_S(0));                  \ +    }                                                                   \ +                                                                        \ +    void helper_ ## name ## pd(CPUX86State *env, Reg *d, Reg *s)        \ +    {                                                                   \ +        d->XMM_Q(0) = F(64, d->XMM_D(0), s->XMM_D(0));                  \ +        d->XMM_Q(1) = F(64, d->XMM_D(1), s->XMM_D(1));                  \ +    }                                                                   \ +                                                                        \ +    void helper_ ## name ## sd(CPUX86State *env, Reg *d, Reg *s)        \ +    {                                                                   \ +        d->XMM_Q(0) = F(64, d->XMM_D(0), s->XMM_D(0));                  \ +    } + +#define FPU_CMPEQ(size, a, b)                                           \ +    (float ## size ## _eq_quiet(a, b, &env->sse_status) ? -1 : 0) +#define FPU_CMPLT(size, a, b)                                           \ +    (float ## size ## _lt(a, b, &env->sse_status) ? -1 : 0) +#define FPU_CMPLE(size, a, b)                                           \ +    (float ## size ## _le(a, b, &env->sse_status) ? -1 : 0) +#define FPU_CMPUNORD(size, a, b)                                        \ +    (float ## size ## _unordered_quiet(a, b, &env->sse_status) ? -1 : 0) +#define FPU_CMPNEQ(size, a, b)                                          \ +    (float ## size ## _eq_quiet(a, b, &env->sse_status) ? 0 : -1) +#define FPU_CMPNLT(size, a, b)                                          \ +    (float ## size ## _lt(a, b, &env->sse_status) ? 0 : -1) +#define FPU_CMPNLE(size, a, b)                                          \ +    (float ## size ## _le(a, b, &env->sse_status) ? 0 : -1) +#define FPU_CMPORD(size, a, b)                                          \ +    (float ## size ## _unordered_quiet(a, b, &env->sse_status) ? 0 : -1) + +SSE_HELPER_CMP(cmpeq, FPU_CMPEQ) +SSE_HELPER_CMP(cmplt, FPU_CMPLT) +SSE_HELPER_CMP(cmple, FPU_CMPLE) +SSE_HELPER_CMP(cmpunord, FPU_CMPUNORD) +SSE_HELPER_CMP(cmpneq, FPU_CMPNEQ) +SSE_HELPER_CMP(cmpnlt, FPU_CMPNLT) +SSE_HELPER_CMP(cmpnle, FPU_CMPNLE) +SSE_HELPER_CMP(cmpord, FPU_CMPORD) + +static const int comis_eflags[4] = {CC_C, CC_Z, 0, CC_Z | CC_P | CC_C}; + +void helper_ucomiss(CPUX86State *env, Reg *d, Reg *s) +{ +    int ret; +    float32 s0, s1; + +    s0 = d->XMM_S(0); +    s1 = s->XMM_S(0); +    ret = float32_compare_quiet(s0, s1, &env->sse_status); +    CC_SRC = comis_eflags[ret + 1]; +} + +void helper_comiss(CPUX86State *env, Reg *d, Reg *s) +{ +    int ret; +    float32 s0, s1; + +    s0 = d->XMM_S(0); +    s1 = s->XMM_S(0); +    ret = float32_compare(s0, s1, &env->sse_status); +    CC_SRC = comis_eflags[ret + 1]; +} + +void helper_ucomisd(CPUX86State *env, Reg *d, Reg *s) +{ +    int ret; +    float64 d0, d1; + +    d0 = d->XMM_D(0); +    d1 = s->XMM_D(0); +    ret = float64_compare_quiet(d0, d1, &env->sse_status); +    CC_SRC = comis_eflags[ret + 1]; +} + +void helper_comisd(CPUX86State *env, Reg *d, Reg *s) +{ +    int ret; +    float64 d0, d1; + +    d0 = d->XMM_D(0); +    d1 = s->XMM_D(0); +    ret = float64_compare(d0, d1, &env->sse_status); +    CC_SRC = comis_eflags[ret + 1]; +} + +uint32_t helper_movmskps(CPUX86State *env, Reg *s) +{ +    int b0, b1, b2, b3; + +    b0 = s->XMM_L(0) >> 31; +    b1 = s->XMM_L(1) >> 31; +    b2 = s->XMM_L(2) >> 31; +    b3 = s->XMM_L(3) >> 31; +    return b0 | (b1 << 1) | (b2 << 2) | (b3 << 3); +} + +uint32_t helper_movmskpd(CPUX86State *env, Reg *s) +{ +    int b0, b1; + +    b0 = s->XMM_L(1) >> 31; +    b1 = s->XMM_L(3) >> 31; +    return b0 | (b1 << 1); +} + +#endif + +uint32_t glue(helper_pmovmskb, SUFFIX)(CPUX86State *env, Reg *s) +{ +    uint32_t val; + +    val = 0; +    val |= (s->B(0) >> 7); +    val |= (s->B(1) >> 6) & 0x02; +    val |= (s->B(2) >> 5) & 0x04; +    val |= (s->B(3) >> 4) & 0x08; +    val |= (s->B(4) >> 3) & 0x10; +    val |= (s->B(5) >> 2) & 0x20; +    val |= (s->B(6) >> 1) & 0x40; +    val |= (s->B(7)) & 0x80; +#if SHIFT == 1 +    val |= (s->B(8) << 1) & 0x0100; +    val |= (s->B(9) << 2) & 0x0200; +    val |= (s->B(10) << 3) & 0x0400; +    val |= (s->B(11) << 4) & 0x0800; +    val |= (s->B(12) << 5) & 0x1000; +    val |= (s->B(13) << 6) & 0x2000; +    val |= (s->B(14) << 7) & 0x4000; +    val |= (s->B(15) << 8) & 0x8000; +#endif +    return val; +} + +void glue(helper_packsswb, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    Reg r; + +    r.B(0) = satsb((int16_t)d->W(0)); +    r.B(1) = satsb((int16_t)d->W(1)); +    r.B(2) = satsb((int16_t)d->W(2)); +    r.B(3) = satsb((int16_t)d->W(3)); +#if SHIFT == 1 +    r.B(4) = satsb((int16_t)d->W(4)); +    r.B(5) = satsb((int16_t)d->W(5)); +    r.B(6) = satsb((int16_t)d->W(6)); +    r.B(7) = satsb((int16_t)d->W(7)); +#endif +    r.B((4 << SHIFT) + 0) = satsb((int16_t)s->W(0)); +    r.B((4 << SHIFT) + 1) = satsb((int16_t)s->W(1)); +    r.B((4 << SHIFT) + 2) = satsb((int16_t)s->W(2)); +    r.B((4 << SHIFT) + 3) = satsb((int16_t)s->W(3)); +#if SHIFT == 1 +    r.B(12) = satsb((int16_t)s->W(4)); +    r.B(13) = satsb((int16_t)s->W(5)); +    r.B(14) = satsb((int16_t)s->W(6)); +    r.B(15) = satsb((int16_t)s->W(7)); +#endif +    *d = r; +} + +void glue(helper_packuswb, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    Reg r; + +    r.B(0) = satub((int16_t)d->W(0)); +    r.B(1) = satub((int16_t)d->W(1)); +    r.B(2) = satub((int16_t)d->W(2)); +    r.B(3) = satub((int16_t)d->W(3)); +#if SHIFT == 1 +    r.B(4) = satub((int16_t)d->W(4)); +    r.B(5) = satub((int16_t)d->W(5)); +    r.B(6) = satub((int16_t)d->W(6)); +    r.B(7) = satub((int16_t)d->W(7)); +#endif +    r.B((4 << SHIFT) + 0) = satub((int16_t)s->W(0)); +    r.B((4 << SHIFT) + 1) = satub((int16_t)s->W(1)); +    r.B((4 << SHIFT) + 2) = satub((int16_t)s->W(2)); +    r.B((4 << SHIFT) + 3) = satub((int16_t)s->W(3)); +#if SHIFT == 1 +    r.B(12) = satub((int16_t)s->W(4)); +    r.B(13) = satub((int16_t)s->W(5)); +    r.B(14) = satub((int16_t)s->W(6)); +    r.B(15) = satub((int16_t)s->W(7)); +#endif +    *d = r; +} + +void glue(helper_packssdw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    Reg r; + +    r.W(0) = satsw(d->L(0)); +    r.W(1) = satsw(d->L(1)); +#if SHIFT == 1 +    r.W(2) = satsw(d->L(2)); +    r.W(3) = satsw(d->L(3)); +#endif +    r.W((2 << SHIFT) + 0) = satsw(s->L(0)); +    r.W((2 << SHIFT) + 1) = satsw(s->L(1)); +#if SHIFT == 1 +    r.W(6) = satsw(s->L(2)); +    r.W(7) = satsw(s->L(3)); +#endif +    *d = r; +} + +#define UNPCK_OP(base_name, base)                                       \ +                                                                        \ +    void glue(helper_punpck ## base_name ## bw, SUFFIX)(CPUX86State *env,\ +                                                        Reg *d, Reg *s) \ +    {                                                                   \ +        Reg r;                                                          \ +                                                                        \ +        r.B(0) = d->B((base << (SHIFT + 2)) + 0);                       \ +        r.B(1) = s->B((base << (SHIFT + 2)) + 0);                       \ +        r.B(2) = d->B((base << (SHIFT + 2)) + 1);                       \ +        r.B(3) = s->B((base << (SHIFT + 2)) + 1);                       \ +        r.B(4) = d->B((base << (SHIFT + 2)) + 2);                       \ +        r.B(5) = s->B((base << (SHIFT + 2)) + 2);                       \ +        r.B(6) = d->B((base << (SHIFT + 2)) + 3);                       \ +        r.B(7) = s->B((base << (SHIFT + 2)) + 3);                       \ +        XMM_ONLY(                                                       \ +                 r.B(8) = d->B((base << (SHIFT + 2)) + 4);              \ +                 r.B(9) = s->B((base << (SHIFT + 2)) + 4);              \ +                 r.B(10) = d->B((base << (SHIFT + 2)) + 5);             \ +                 r.B(11) = s->B((base << (SHIFT + 2)) + 5);             \ +                 r.B(12) = d->B((base << (SHIFT + 2)) + 6);             \ +                 r.B(13) = s->B((base << (SHIFT + 2)) + 6);             \ +                 r.B(14) = d->B((base << (SHIFT + 2)) + 7);             \ +                 r.B(15) = s->B((base << (SHIFT + 2)) + 7);             \ +                                                                      ) \ +            *d = r;                                                     \ +    }                                                                   \ +                                                                        \ +    void glue(helper_punpck ## base_name ## wd, SUFFIX)(CPUX86State *env,\ +                                                        Reg *d, Reg *s) \ +    {                                                                   \ +        Reg r;                                                          \ +                                                                        \ +        r.W(0) = d->W((base << (SHIFT + 1)) + 0);                       \ +        r.W(1) = s->W((base << (SHIFT + 1)) + 0);                       \ +        r.W(2) = d->W((base << (SHIFT + 1)) + 1);                       \ +        r.W(3) = s->W((base << (SHIFT + 1)) + 1);                       \ +        XMM_ONLY(                                                       \ +                 r.W(4) = d->W((base << (SHIFT + 1)) + 2);              \ +                 r.W(5) = s->W((base << (SHIFT + 1)) + 2);              \ +                 r.W(6) = d->W((base << (SHIFT + 1)) + 3);              \ +                 r.W(7) = s->W((base << (SHIFT + 1)) + 3);              \ +                                                                      ) \ +            *d = r;                                                     \ +    }                                                                   \ +                                                                        \ +    void glue(helper_punpck ## base_name ## dq, SUFFIX)(CPUX86State *env,\ +                                                        Reg *d, Reg *s) \ +    {                                                                   \ +        Reg r;                                                          \ +                                                                        \ +        r.L(0) = d->L((base << SHIFT) + 0);                             \ +        r.L(1) = s->L((base << SHIFT) + 0);                             \ +        XMM_ONLY(                                                       \ +                 r.L(2) = d->L((base << SHIFT) + 1);                    \ +                 r.L(3) = s->L((base << SHIFT) + 1);                    \ +                                                                      ) \ +            *d = r;                                                     \ +    }                                                                   \ +                                                                        \ +    XMM_ONLY(                                                           \ +             void glue(helper_punpck ## base_name ## qdq, SUFFIX)(CPUX86State \ +                                                                  *env, \ +                                                                  Reg *d, \ +                                                                  Reg *s) \ +             {                                                          \ +                 Reg r;                                                 \ +                                                                        \ +                 r.Q(0) = d->Q(base);                                   \ +                 r.Q(1) = s->Q(base);                                   \ +                 *d = r;                                                \ +             }                                                          \ +                                                                        ) + +UNPCK_OP(l, 0) +UNPCK_OP(h, 1) + +/* 3DNow! float ops */ +#if SHIFT == 0 +void helper_pi2fd(CPUX86State *env, MMXReg *d, MMXReg *s) +{ +    d->MMX_S(0) = int32_to_float32(s->MMX_L(0), &env->mmx_status); +    d->MMX_S(1) = int32_to_float32(s->MMX_L(1), &env->mmx_status); +} + +void helper_pi2fw(CPUX86State *env, MMXReg *d, MMXReg *s) +{ +    d->MMX_S(0) = int32_to_float32((int16_t)s->MMX_W(0), &env->mmx_status); +    d->MMX_S(1) = int32_to_float32((int16_t)s->MMX_W(2), &env->mmx_status); +} + +void helper_pf2id(CPUX86State *env, MMXReg *d, MMXReg *s) +{ +    d->MMX_L(0) = float32_to_int32_round_to_zero(s->MMX_S(0), &env->mmx_status); +    d->MMX_L(1) = float32_to_int32_round_to_zero(s->MMX_S(1), &env->mmx_status); +} + +void helper_pf2iw(CPUX86State *env, MMXReg *d, MMXReg *s) +{ +    d->MMX_L(0) = satsw(float32_to_int32_round_to_zero(s->MMX_S(0), +                                                       &env->mmx_status)); +    d->MMX_L(1) = satsw(float32_to_int32_round_to_zero(s->MMX_S(1), +                                                       &env->mmx_status)); +} + +void helper_pfacc(CPUX86State *env, MMXReg *d, MMXReg *s) +{ +    MMXReg r; + +    r.MMX_S(0) = float32_add(d->MMX_S(0), d->MMX_S(1), &env->mmx_status); +    r.MMX_S(1) = float32_add(s->MMX_S(0), s->MMX_S(1), &env->mmx_status); +    *d = r; +} + +void helper_pfadd(CPUX86State *env, MMXReg *d, MMXReg *s) +{ +    d->MMX_S(0) = float32_add(d->MMX_S(0), s->MMX_S(0), &env->mmx_status); +    d->MMX_S(1) = float32_add(d->MMX_S(1), s->MMX_S(1), &env->mmx_status); +} + +void helper_pfcmpeq(CPUX86State *env, MMXReg *d, MMXReg *s) +{ +    d->MMX_L(0) = float32_eq_quiet(d->MMX_S(0), s->MMX_S(0), +                                   &env->mmx_status) ? -1 : 0; +    d->MMX_L(1) = float32_eq_quiet(d->MMX_S(1), s->MMX_S(1), +                                   &env->mmx_status) ? -1 : 0; +} + +void helper_pfcmpge(CPUX86State *env, MMXReg *d, MMXReg *s) +{ +    d->MMX_L(0) = float32_le(s->MMX_S(0), d->MMX_S(0), +                             &env->mmx_status) ? -1 : 0; +    d->MMX_L(1) = float32_le(s->MMX_S(1), d->MMX_S(1), +                             &env->mmx_status) ? -1 : 0; +} + +void helper_pfcmpgt(CPUX86State *env, MMXReg *d, MMXReg *s) +{ +    d->MMX_L(0) = float32_lt(s->MMX_S(0), d->MMX_S(0), +                             &env->mmx_status) ? -1 : 0; +    d->MMX_L(1) = float32_lt(s->MMX_S(1), d->MMX_S(1), +                             &env->mmx_status) ? -1 : 0; +} + +void helper_pfmax(CPUX86State *env, MMXReg *d, MMXReg *s) +{ +    if (float32_lt(d->MMX_S(0), s->MMX_S(0), &env->mmx_status)) { +        d->MMX_S(0) = s->MMX_S(0); +    } +    if (float32_lt(d->MMX_S(1), s->MMX_S(1), &env->mmx_status)) { +        d->MMX_S(1) = s->MMX_S(1); +    } +} + +void helper_pfmin(CPUX86State *env, MMXReg *d, MMXReg *s) +{ +    if (float32_lt(s->MMX_S(0), d->MMX_S(0), &env->mmx_status)) { +        d->MMX_S(0) = s->MMX_S(0); +    } +    if (float32_lt(s->MMX_S(1), d->MMX_S(1), &env->mmx_status)) { +        d->MMX_S(1) = s->MMX_S(1); +    } +} + +void helper_pfmul(CPUX86State *env, MMXReg *d, MMXReg *s) +{ +    d->MMX_S(0) = float32_mul(d->MMX_S(0), s->MMX_S(0), &env->mmx_status); +    d->MMX_S(1) = float32_mul(d->MMX_S(1), s->MMX_S(1), &env->mmx_status); +} + +void helper_pfnacc(CPUX86State *env, MMXReg *d, MMXReg *s) +{ +    MMXReg r; + +    r.MMX_S(0) = float32_sub(d->MMX_S(0), d->MMX_S(1), &env->mmx_status); +    r.MMX_S(1) = float32_sub(s->MMX_S(0), s->MMX_S(1), &env->mmx_status); +    *d = r; +} + +void helper_pfpnacc(CPUX86State *env, MMXReg *d, MMXReg *s) +{ +    MMXReg r; + +    r.MMX_S(0) = float32_sub(d->MMX_S(0), d->MMX_S(1), &env->mmx_status); +    r.MMX_S(1) = float32_add(s->MMX_S(0), s->MMX_S(1), &env->mmx_status); +    *d = r; +} + +void helper_pfrcp(CPUX86State *env, MMXReg *d, MMXReg *s) +{ +    d->MMX_S(0) = float32_div(float32_one, s->MMX_S(0), &env->mmx_status); +    d->MMX_S(1) = d->MMX_S(0); +} + +void helper_pfrsqrt(CPUX86State *env, MMXReg *d, MMXReg *s) +{ +    d->MMX_L(1) = s->MMX_L(0) & 0x7fffffff; +    d->MMX_S(1) = float32_div(float32_one, +                              float32_sqrt(d->MMX_S(1), &env->mmx_status), +                              &env->mmx_status); +    d->MMX_L(1) |= s->MMX_L(0) & 0x80000000; +    d->MMX_L(0) = d->MMX_L(1); +} + +void helper_pfsub(CPUX86State *env, MMXReg *d, MMXReg *s) +{ +    d->MMX_S(0) = float32_sub(d->MMX_S(0), s->MMX_S(0), &env->mmx_status); +    d->MMX_S(1) = float32_sub(d->MMX_S(1), s->MMX_S(1), &env->mmx_status); +} + +void helper_pfsubr(CPUX86State *env, MMXReg *d, MMXReg *s) +{ +    d->MMX_S(0) = float32_sub(s->MMX_S(0), d->MMX_S(0), &env->mmx_status); +    d->MMX_S(1) = float32_sub(s->MMX_S(1), d->MMX_S(1), &env->mmx_status); +} + +void helper_pswapd(CPUX86State *env, MMXReg *d, MMXReg *s) +{ +    MMXReg r; + +    r.MMX_L(0) = s->MMX_L(1); +    r.MMX_L(1) = s->MMX_L(0); +    *d = r; +} +#endif + +/* SSSE3 op helpers */ +void glue(helper_pshufb, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    int i; +    Reg r; + +    for (i = 0; i < (8 << SHIFT); i++) { +        r.B(i) = (s->B(i) & 0x80) ? 0 : (d->B(s->B(i) & ((8 << SHIFT) - 1))); +    } + +    *d = r; +} + +void glue(helper_phaddw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    d->W(0) = (int16_t)d->W(0) + (int16_t)d->W(1); +    d->W(1) = (int16_t)d->W(2) + (int16_t)d->W(3); +    XMM_ONLY(d->W(2) = (int16_t)d->W(4) + (int16_t)d->W(5)); +    XMM_ONLY(d->W(3) = (int16_t)d->W(6) + (int16_t)d->W(7)); +    d->W((2 << SHIFT) + 0) = (int16_t)s->W(0) + (int16_t)s->W(1); +    d->W((2 << SHIFT) + 1) = (int16_t)s->W(2) + (int16_t)s->W(3); +    XMM_ONLY(d->W(6) = (int16_t)s->W(4) + (int16_t)s->W(5)); +    XMM_ONLY(d->W(7) = (int16_t)s->W(6) + (int16_t)s->W(7)); +} + +void glue(helper_phaddd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    d->L(0) = (int32_t)d->L(0) + (int32_t)d->L(1); +    XMM_ONLY(d->L(1) = (int32_t)d->L(2) + (int32_t)d->L(3)); +    d->L((1 << SHIFT) + 0) = (int32_t)s->L(0) + (int32_t)s->L(1); +    XMM_ONLY(d->L(3) = (int32_t)s->L(2) + (int32_t)s->L(3)); +} + +void glue(helper_phaddsw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    d->W(0) = satsw((int16_t)d->W(0) + (int16_t)d->W(1)); +    d->W(1) = satsw((int16_t)d->W(2) + (int16_t)d->W(3)); +    XMM_ONLY(d->W(2) = satsw((int16_t)d->W(4) + (int16_t)d->W(5))); +    XMM_ONLY(d->W(3) = satsw((int16_t)d->W(6) + (int16_t)d->W(7))); +    d->W((2 << SHIFT) + 0) = satsw((int16_t)s->W(0) + (int16_t)s->W(1)); +    d->W((2 << SHIFT) + 1) = satsw((int16_t)s->W(2) + (int16_t)s->W(3)); +    XMM_ONLY(d->W(6) = satsw((int16_t)s->W(4) + (int16_t)s->W(5))); +    XMM_ONLY(d->W(7) = satsw((int16_t)s->W(6) + (int16_t)s->W(7))); +} + +void glue(helper_pmaddubsw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    d->W(0) = satsw((int8_t)s->B(0) * (uint8_t)d->B(0) + +                    (int8_t)s->B(1) * (uint8_t)d->B(1)); +    d->W(1) = satsw((int8_t)s->B(2) * (uint8_t)d->B(2) + +                    (int8_t)s->B(3) * (uint8_t)d->B(3)); +    d->W(2) = satsw((int8_t)s->B(4) * (uint8_t)d->B(4) + +                    (int8_t)s->B(5) * (uint8_t)d->B(5)); +    d->W(3) = satsw((int8_t)s->B(6) * (uint8_t)d->B(6) + +                    (int8_t)s->B(7) * (uint8_t)d->B(7)); +#if SHIFT == 1 +    d->W(4) = satsw((int8_t)s->B(8) * (uint8_t)d->B(8) + +                    (int8_t)s->B(9) * (uint8_t)d->B(9)); +    d->W(5) = satsw((int8_t)s->B(10) * (uint8_t)d->B(10) + +                    (int8_t)s->B(11) * (uint8_t)d->B(11)); +    d->W(6) = satsw((int8_t)s->B(12) * (uint8_t)d->B(12) + +                    (int8_t)s->B(13) * (uint8_t)d->B(13)); +    d->W(7) = satsw((int8_t)s->B(14) * (uint8_t)d->B(14) + +                    (int8_t)s->B(15) * (uint8_t)d->B(15)); +#endif +} + +void glue(helper_phsubw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    d->W(0) = (int16_t)d->W(0) - (int16_t)d->W(1); +    d->W(1) = (int16_t)d->W(2) - (int16_t)d->W(3); +    XMM_ONLY(d->W(2) = (int16_t)d->W(4) - (int16_t)d->W(5)); +    XMM_ONLY(d->W(3) = (int16_t)d->W(6) - (int16_t)d->W(7)); +    d->W((2 << SHIFT) + 0) = (int16_t)s->W(0) - (int16_t)s->W(1); +    d->W((2 << SHIFT) + 1) = (int16_t)s->W(2) - (int16_t)s->W(3); +    XMM_ONLY(d->W(6) = (int16_t)s->W(4) - (int16_t)s->W(5)); +    XMM_ONLY(d->W(7) = (int16_t)s->W(6) - (int16_t)s->W(7)); +} + +void glue(helper_phsubd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    d->L(0) = (int32_t)d->L(0) - (int32_t)d->L(1); +    XMM_ONLY(d->L(1) = (int32_t)d->L(2) - (int32_t)d->L(3)); +    d->L((1 << SHIFT) + 0) = (int32_t)s->L(0) - (int32_t)s->L(1); +    XMM_ONLY(d->L(3) = (int32_t)s->L(2) - (int32_t)s->L(3)); +} + +void glue(helper_phsubsw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    d->W(0) = satsw((int16_t)d->W(0) - (int16_t)d->W(1)); +    d->W(1) = satsw((int16_t)d->W(2) - (int16_t)d->W(3)); +    XMM_ONLY(d->W(2) = satsw((int16_t)d->W(4) - (int16_t)d->W(5))); +    XMM_ONLY(d->W(3) = satsw((int16_t)d->W(6) - (int16_t)d->W(7))); +    d->W((2 << SHIFT) + 0) = satsw((int16_t)s->W(0) - (int16_t)s->W(1)); +    d->W((2 << SHIFT) + 1) = satsw((int16_t)s->W(2) - (int16_t)s->W(3)); +    XMM_ONLY(d->W(6) = satsw((int16_t)s->W(4) - (int16_t)s->W(5))); +    XMM_ONLY(d->W(7) = satsw((int16_t)s->W(6) - (int16_t)s->W(7))); +} + +#define FABSB(_, x) (x > INT8_MAX  ? -(int8_t)x : x) +#define FABSW(_, x) (x > INT16_MAX ? -(int16_t)x : x) +#define FABSL(_, x) (x > INT32_MAX ? -(int32_t)x : x) +SSE_HELPER_B(helper_pabsb, FABSB) +SSE_HELPER_W(helper_pabsw, FABSW) +SSE_HELPER_L(helper_pabsd, FABSL) + +#define FMULHRSW(d, s) (((int16_t) d * (int16_t)s + 0x4000) >> 15) +SSE_HELPER_W(helper_pmulhrsw, FMULHRSW) + +#define FSIGNB(d, s) (s <= INT8_MAX  ? s ? d : 0 : -(int8_t)d) +#define FSIGNW(d, s) (s <= INT16_MAX ? s ? d : 0 : -(int16_t)d) +#define FSIGNL(d, s) (s <= INT32_MAX ? s ? d : 0 : -(int32_t)d) +SSE_HELPER_B(helper_psignb, FSIGNB) +SSE_HELPER_W(helper_psignw, FSIGNW) +SSE_HELPER_L(helper_psignd, FSIGNL) + +void glue(helper_palignr, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, +                                  int32_t shift) +{ +    Reg r; + +    /* XXX could be checked during translation */ +    if (shift >= (16 << SHIFT)) { +        r.Q(0) = 0; +        XMM_ONLY(r.Q(1) = 0); +    } else { +        shift <<= 3; +#define SHR(v, i) (i < 64 && i > -64 ? i > 0 ? v >> (i) : (v << -(i)) : 0) +#if SHIFT == 0 +        r.Q(0) = SHR(s->Q(0), shift - 0) | +            SHR(d->Q(0), shift -  64); +#else +        r.Q(0) = SHR(s->Q(0), shift - 0) | +            SHR(s->Q(1), shift -  64) | +            SHR(d->Q(0), shift - 128) | +            SHR(d->Q(1), shift - 192); +        r.Q(1) = SHR(s->Q(0), shift + 64) | +            SHR(s->Q(1), shift -   0) | +            SHR(d->Q(0), shift -  64) | +            SHR(d->Q(1), shift - 128); +#endif +#undef SHR +    } + +    *d = r; +} + +#define XMM0 (env->xmm_regs[0]) + +#if SHIFT == 1 +#define SSE_HELPER_V(name, elem, num, F)                                \ +    void glue(name, SUFFIX)(CPUX86State *env, Reg *d, Reg *s)           \ +    {                                                                   \ +        d->elem(0) = F(d->elem(0), s->elem(0), XMM0.elem(0));           \ +        d->elem(1) = F(d->elem(1), s->elem(1), XMM0.elem(1));           \ +        if (num > 2) {                                                  \ +            d->elem(2) = F(d->elem(2), s->elem(2), XMM0.elem(2));       \ +            d->elem(3) = F(d->elem(3), s->elem(3), XMM0.elem(3));       \ +            if (num > 4) {                                              \ +                d->elem(4) = F(d->elem(4), s->elem(4), XMM0.elem(4));   \ +                d->elem(5) = F(d->elem(5), s->elem(5), XMM0.elem(5));   \ +                d->elem(6) = F(d->elem(6), s->elem(6), XMM0.elem(6));   \ +                d->elem(7) = F(d->elem(7), s->elem(7), XMM0.elem(7));   \ +                if (num > 8) {                                          \ +                    d->elem(8) = F(d->elem(8), s->elem(8), XMM0.elem(8)); \ +                    d->elem(9) = F(d->elem(9), s->elem(9), XMM0.elem(9)); \ +                    d->elem(10) = F(d->elem(10), s->elem(10), XMM0.elem(10)); \ +                    d->elem(11) = F(d->elem(11), s->elem(11), XMM0.elem(11)); \ +                    d->elem(12) = F(d->elem(12), s->elem(12), XMM0.elem(12)); \ +                    d->elem(13) = F(d->elem(13), s->elem(13), XMM0.elem(13)); \ +                    d->elem(14) = F(d->elem(14), s->elem(14), XMM0.elem(14)); \ +                    d->elem(15) = F(d->elem(15), s->elem(15), XMM0.elem(15)); \ +                }                                                       \ +            }                                                           \ +        }                                                               \ +    } + +#define SSE_HELPER_I(name, elem, num, F)                                \ +    void glue(name, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, uint32_t imm) \ +    {                                                                   \ +        d->elem(0) = F(d->elem(0), s->elem(0), ((imm >> 0) & 1));       \ +        d->elem(1) = F(d->elem(1), s->elem(1), ((imm >> 1) & 1));       \ +        if (num > 2) {                                                  \ +            d->elem(2) = F(d->elem(2), s->elem(2), ((imm >> 2) & 1));   \ +            d->elem(3) = F(d->elem(3), s->elem(3), ((imm >> 3) & 1));   \ +            if (num > 4) {                                              \ +                d->elem(4) = F(d->elem(4), s->elem(4), ((imm >> 4) & 1)); \ +                d->elem(5) = F(d->elem(5), s->elem(5), ((imm >> 5) & 1)); \ +                d->elem(6) = F(d->elem(6), s->elem(6), ((imm >> 6) & 1)); \ +                d->elem(7) = F(d->elem(7), s->elem(7), ((imm >> 7) & 1)); \ +                if (num > 8) {                                          \ +                    d->elem(8) = F(d->elem(8), s->elem(8), ((imm >> 8) & 1)); \ +                    d->elem(9) = F(d->elem(9), s->elem(9), ((imm >> 9) & 1)); \ +                    d->elem(10) = F(d->elem(10), s->elem(10),           \ +                                    ((imm >> 10) & 1));                 \ +                    d->elem(11) = F(d->elem(11), s->elem(11),           \ +                                    ((imm >> 11) & 1));                 \ +                    d->elem(12) = F(d->elem(12), s->elem(12),           \ +                                    ((imm >> 12) & 1));                 \ +                    d->elem(13) = F(d->elem(13), s->elem(13),           \ +                                    ((imm >> 13) & 1));                 \ +                    d->elem(14) = F(d->elem(14), s->elem(14),           \ +                                    ((imm >> 14) & 1));                 \ +                    d->elem(15) = F(d->elem(15), s->elem(15),           \ +                                    ((imm >> 15) & 1));                 \ +                }                                                       \ +            }                                                           \ +        }                                                               \ +    } + +/* SSE4.1 op helpers */ +#define FBLENDVB(d, s, m) ((m & 0x80) ? s : d) +#define FBLENDVPS(d, s, m) ((m & 0x80000000) ? s : d) +#define FBLENDVPD(d, s, m) ((m & 0x8000000000000000LL) ? s : d) +SSE_HELPER_V(helper_pblendvb, B, 16, FBLENDVB) +SSE_HELPER_V(helper_blendvps, L, 4, FBLENDVPS) +SSE_HELPER_V(helper_blendvpd, Q, 2, FBLENDVPD) + +void glue(helper_ptest, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    uint64_t zf = (s->Q(0) &  d->Q(0)) | (s->Q(1) &  d->Q(1)); +    uint64_t cf = (s->Q(0) & ~d->Q(0)) | (s->Q(1) & ~d->Q(1)); + +    CC_SRC = (zf ? 0 : CC_Z) | (cf ? 0 : CC_C); +} + +#define SSE_HELPER_F(name, elem, num, F)        \ +    void glue(name, SUFFIX)(CPUX86State *env, Reg *d, Reg *s)     \ +    {                                           \ +        d->elem(0) = F(0);                      \ +        d->elem(1) = F(1);                      \ +        if (num > 2) {                          \ +            d->elem(2) = F(2);                  \ +            d->elem(3) = F(3);                  \ +            if (num > 4) {                      \ +                d->elem(4) = F(4);              \ +                d->elem(5) = F(5);              \ +                d->elem(6) = F(6);              \ +                d->elem(7) = F(7);              \ +            }                                   \ +        }                                       \ +    } + +SSE_HELPER_F(helper_pmovsxbw, W, 8, (int8_t) s->B) +SSE_HELPER_F(helper_pmovsxbd, L, 4, (int8_t) s->B) +SSE_HELPER_F(helper_pmovsxbq, Q, 2, (int8_t) s->B) +SSE_HELPER_F(helper_pmovsxwd, L, 4, (int16_t) s->W) +SSE_HELPER_F(helper_pmovsxwq, Q, 2, (int16_t) s->W) +SSE_HELPER_F(helper_pmovsxdq, Q, 2, (int32_t) s->L) +SSE_HELPER_F(helper_pmovzxbw, W, 8, s->B) +SSE_HELPER_F(helper_pmovzxbd, L, 4, s->B) +SSE_HELPER_F(helper_pmovzxbq, Q, 2, s->B) +SSE_HELPER_F(helper_pmovzxwd, L, 4, s->W) +SSE_HELPER_F(helper_pmovzxwq, Q, 2, s->W) +SSE_HELPER_F(helper_pmovzxdq, Q, 2, s->L) + +void glue(helper_pmuldq, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    d->Q(0) = (int64_t)(int32_t) d->L(0) * (int32_t) s->L(0); +    d->Q(1) = (int64_t)(int32_t) d->L(2) * (int32_t) s->L(2); +} + +#define FCMPEQQ(d, s) (d == s ? -1 : 0) +SSE_HELPER_Q(helper_pcmpeqq, FCMPEQQ) + +void glue(helper_packusdw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    d->W(0) = satuw((int32_t) d->L(0)); +    d->W(1) = satuw((int32_t) d->L(1)); +    d->W(2) = satuw((int32_t) d->L(2)); +    d->W(3) = satuw((int32_t) d->L(3)); +    d->W(4) = satuw((int32_t) s->L(0)); +    d->W(5) = satuw((int32_t) s->L(1)); +    d->W(6) = satuw((int32_t) s->L(2)); +    d->W(7) = satuw((int32_t) s->L(3)); +} + +#define FMINSB(d, s) MIN((int8_t)d, (int8_t)s) +#define FMINSD(d, s) MIN((int32_t)d, (int32_t)s) +#define FMAXSB(d, s) MAX((int8_t)d, (int8_t)s) +#define FMAXSD(d, s) MAX((int32_t)d, (int32_t)s) +SSE_HELPER_B(helper_pminsb, FMINSB) +SSE_HELPER_L(helper_pminsd, FMINSD) +SSE_HELPER_W(helper_pminuw, MIN) +SSE_HELPER_L(helper_pminud, MIN) +SSE_HELPER_B(helper_pmaxsb, FMAXSB) +SSE_HELPER_L(helper_pmaxsd, FMAXSD) +SSE_HELPER_W(helper_pmaxuw, MAX) +SSE_HELPER_L(helper_pmaxud, MAX) + +#define FMULLD(d, s) ((int32_t)d * (int32_t)s) +SSE_HELPER_L(helper_pmulld, FMULLD) + +void glue(helper_phminposuw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    int idx = 0; + +    if (s->W(1) < s->W(idx)) { +        idx = 1; +    } +    if (s->W(2) < s->W(idx)) { +        idx = 2; +    } +    if (s->W(3) < s->W(idx)) { +        idx = 3; +    } +    if (s->W(4) < s->W(idx)) { +        idx = 4; +    } +    if (s->W(5) < s->W(idx)) { +        idx = 5; +    } +    if (s->W(6) < s->W(idx)) { +        idx = 6; +    } +    if (s->W(7) < s->W(idx)) { +        idx = 7; +    } + +    d->Q(1) = 0; +    d->L(1) = 0; +    d->W(1) = idx; +    d->W(0) = s->W(idx); +} + +void glue(helper_roundps, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, +                                  uint32_t mode) +{ +    signed char prev_rounding_mode; + +    prev_rounding_mode = env->sse_status.float_rounding_mode; +    if (!(mode & (1 << 2))) { +        switch (mode & 3) { +        case 0: +            set_float_rounding_mode(float_round_nearest_even, &env->sse_status); +            break; +        case 1: +            set_float_rounding_mode(float_round_down, &env->sse_status); +            break; +        case 2: +            set_float_rounding_mode(float_round_up, &env->sse_status); +            break; +        case 3: +            set_float_rounding_mode(float_round_to_zero, &env->sse_status); +            break; +        } +    } + +    d->XMM_S(0) = float32_round_to_int(s->XMM_S(0), &env->sse_status); +    d->XMM_S(1) = float32_round_to_int(s->XMM_S(1), &env->sse_status); +    d->XMM_S(2) = float32_round_to_int(s->XMM_S(2), &env->sse_status); +    d->XMM_S(3) = float32_round_to_int(s->XMM_S(3), &env->sse_status); + +#if 0 /* TODO */ +    if (mode & (1 << 3)) { +        set_float_exception_flags(get_float_exception_flags(&env->sse_status) & +                                  ~float_flag_inexact, +                                  &env->sse_status); +    } +#endif +    env->sse_status.float_rounding_mode = prev_rounding_mode; +} + +void glue(helper_roundpd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, +                                  uint32_t mode) +{ +    signed char prev_rounding_mode; + +    prev_rounding_mode = env->sse_status.float_rounding_mode; +    if (!(mode & (1 << 2))) { +        switch (mode & 3) { +        case 0: +            set_float_rounding_mode(float_round_nearest_even, &env->sse_status); +            break; +        case 1: +            set_float_rounding_mode(float_round_down, &env->sse_status); +            break; +        case 2: +            set_float_rounding_mode(float_round_up, &env->sse_status); +            break; +        case 3: +            set_float_rounding_mode(float_round_to_zero, &env->sse_status); +            break; +        } +    } + +    d->XMM_D(0) = float64_round_to_int(s->XMM_D(0), &env->sse_status); +    d->XMM_D(1) = float64_round_to_int(s->XMM_D(1), &env->sse_status); + +#if 0 /* TODO */ +    if (mode & (1 << 3)) { +        set_float_exception_flags(get_float_exception_flags(&env->sse_status) & +                                  ~float_flag_inexact, +                                  &env->sse_status); +    } +#endif +    env->sse_status.float_rounding_mode = prev_rounding_mode; +} + +void glue(helper_roundss, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, +                                  uint32_t mode) +{ +    signed char prev_rounding_mode; + +    prev_rounding_mode = env->sse_status.float_rounding_mode; +    if (!(mode & (1 << 2))) { +        switch (mode & 3) { +        case 0: +            set_float_rounding_mode(float_round_nearest_even, &env->sse_status); +            break; +        case 1: +            set_float_rounding_mode(float_round_down, &env->sse_status); +            break; +        case 2: +            set_float_rounding_mode(float_round_up, &env->sse_status); +            break; +        case 3: +            set_float_rounding_mode(float_round_to_zero, &env->sse_status); +            break; +        } +    } + +    d->XMM_S(0) = float32_round_to_int(s->XMM_S(0), &env->sse_status); + +#if 0 /* TODO */ +    if (mode & (1 << 3)) { +        set_float_exception_flags(get_float_exception_flags(&env->sse_status) & +                                  ~float_flag_inexact, +                                  &env->sse_status); +    } +#endif +    env->sse_status.float_rounding_mode = prev_rounding_mode; +} + +void glue(helper_roundsd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, +                                  uint32_t mode) +{ +    signed char prev_rounding_mode; + +    prev_rounding_mode = env->sse_status.float_rounding_mode; +    if (!(mode & (1 << 2))) { +        switch (mode & 3) { +        case 0: +            set_float_rounding_mode(float_round_nearest_even, &env->sse_status); +            break; +        case 1: +            set_float_rounding_mode(float_round_down, &env->sse_status); +            break; +        case 2: +            set_float_rounding_mode(float_round_up, &env->sse_status); +            break; +        case 3: +            set_float_rounding_mode(float_round_to_zero, &env->sse_status); +            break; +        } +    } + +    d->XMM_D(0) = float64_round_to_int(s->XMM_D(0), &env->sse_status); + +#if 0 /* TODO */ +    if (mode & (1 << 3)) { +        set_float_exception_flags(get_float_exception_flags(&env->sse_status) & +                                  ~float_flag_inexact, +                                  &env->sse_status); +    } +#endif +    env->sse_status.float_rounding_mode = prev_rounding_mode; +} + +#define FBLENDP(d, s, m) (m ? s : d) +SSE_HELPER_I(helper_blendps, L, 4, FBLENDP) +SSE_HELPER_I(helper_blendpd, Q, 2, FBLENDP) +SSE_HELPER_I(helper_pblendw, W, 8, FBLENDP) + +void glue(helper_dpps, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, uint32_t mask) +{ +    float32 iresult = float32_zero; + +    if (mask & (1 << 4)) { +        iresult = float32_add(iresult, +                              float32_mul(d->XMM_S(0), s->XMM_S(0), +                                          &env->sse_status), +                              &env->sse_status); +    } +    if (mask & (1 << 5)) { +        iresult = float32_add(iresult, +                              float32_mul(d->XMM_S(1), s->XMM_S(1), +                                          &env->sse_status), +                              &env->sse_status); +    } +    if (mask & (1 << 6)) { +        iresult = float32_add(iresult, +                              float32_mul(d->XMM_S(2), s->XMM_S(2), +                                          &env->sse_status), +                              &env->sse_status); +    } +    if (mask & (1 << 7)) { +        iresult = float32_add(iresult, +                              float32_mul(d->XMM_S(3), s->XMM_S(3), +                                          &env->sse_status), +                              &env->sse_status); +    } +    d->XMM_S(0) = (mask & (1 << 0)) ? iresult : float32_zero; +    d->XMM_S(1) = (mask & (1 << 1)) ? iresult : float32_zero; +    d->XMM_S(2) = (mask & (1 << 2)) ? iresult : float32_zero; +    d->XMM_S(3) = (mask & (1 << 3)) ? iresult : float32_zero; +} + +void glue(helper_dppd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, uint32_t mask) +{ +    float64 iresult = float64_zero; + +    if (mask & (1 << 4)) { +        iresult = float64_add(iresult, +                              float64_mul(d->XMM_D(0), s->XMM_D(0), +                                          &env->sse_status), +                              &env->sse_status); +    } +    if (mask & (1 << 5)) { +        iresult = float64_add(iresult, +                              float64_mul(d->XMM_D(1), s->XMM_D(1), +                                          &env->sse_status), +                              &env->sse_status); +    } +    d->XMM_D(0) = (mask & (1 << 0)) ? iresult : float64_zero; +    d->XMM_D(1) = (mask & (1 << 1)) ? iresult : float64_zero; +} + +void glue(helper_mpsadbw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, +                                  uint32_t offset) +{ +    int s0 = (offset & 3) << 2; +    int d0 = (offset & 4) << 0; +    int i; +    Reg r; + +    for (i = 0; i < 8; i++, d0++) { +        r.W(i) = 0; +        r.W(i) += abs1(d->B(d0 + 0) - s->B(s0 + 0)); +        r.W(i) += abs1(d->B(d0 + 1) - s->B(s0 + 1)); +        r.W(i) += abs1(d->B(d0 + 2) - s->B(s0 + 2)); +        r.W(i) += abs1(d->B(d0 + 3) - s->B(s0 + 3)); +    } + +    *d = r; +} + +/* SSE4.2 op helpers */ +#define FCMPGTQ(d, s) ((int64_t)d > (int64_t)s ? -1 : 0) +SSE_HELPER_Q(helper_pcmpgtq, FCMPGTQ) + +static inline int pcmp_elen(CPUX86State *env, int reg, uint32_t ctrl) +{ +    int val; + +    /* Presence of REX.W is indicated by a bit higher than 7 set */ +    if (ctrl >> 8) { +        val = abs1((int64_t)env->regs[reg]); +    } else { +        val = abs1((int32_t)env->regs[reg]); +    } + +    if (ctrl & 1) { +        if (val > 8) { +            return 8; +        } +    } else { +        if (val > 16) { +            return 16; +        } +    } +    return val; +} + +static inline int pcmp_ilen(Reg *r, uint8_t ctrl) +{ +    int val = 0; + +    if (ctrl & 1) { +        while (val < 8 && r->W(val)) { +            val++; +        } +    } else { +        while (val < 16 && r->B(val)) { +            val++; +        } +    } + +    return val; +} + +static inline int pcmp_val(Reg *r, uint8_t ctrl, int i) +{ +    switch ((ctrl >> 0) & 3) { +    case 0: +        return r->B(i); +    case 1: +        return r->W(i); +    case 2: +        return (int8_t)r->B(i); +    case 3: +    default: +        return (int16_t)r->W(i); +    } +} + +static inline unsigned pcmpxstrx(CPUX86State *env, Reg *d, Reg *s, +                                 int8_t ctrl, int valids, int validd) +{ +    unsigned int res = 0; +    int v; +    int j, i; +    int upper = (ctrl & 1) ? 7 : 15; + +    valids--; +    validd--; + +    CC_SRC = (valids < upper ? CC_Z : 0) | (validd < upper ? CC_S : 0); + +    switch ((ctrl >> 2) & 3) { +    case 0: +        for (j = valids; j >= 0; j--) { +            res <<= 1; +            v = pcmp_val(s, ctrl, j); +            for (i = validd; i >= 0; i--) { +                res |= (v == pcmp_val(d, ctrl, i)); +            } +        } +        break; +    case 1: +        for (j = valids; j >= 0; j--) { +            res <<= 1; +            v = pcmp_val(s, ctrl, j); +            for (i = ((validd - 1) | 1); i >= 0; i -= 2) { +                res |= (pcmp_val(d, ctrl, i - 0) >= v && +                        pcmp_val(d, ctrl, i - 1) <= v); +            } +        } +        break; +    case 2: +        res = (1 << (upper - MAX(valids, validd))) - 1; +        res <<= MAX(valids, validd) - MIN(valids, validd); +        for (i = MIN(valids, validd); i >= 0; i--) { +            res <<= 1; +            v = pcmp_val(s, ctrl, i); +            res |= (v == pcmp_val(d, ctrl, i)); +        } +        break; +    case 3: +        for (j = valids; j >= 0; j--) { +            res <<= 1; +            v = 1; +            for (i = MIN(valids - j, validd); i >= 0; i--) { +                v &= (pcmp_val(s, ctrl, i + j) == pcmp_val(d, ctrl, i)); +            } +            res |= v; +        } +        break; +    } + +    switch ((ctrl >> 4) & 3) { +    case 1: +        res ^= (2 << upper) - 1; +        break; +    case 3: +        res ^= (1 << (valids + 1)) - 1; +        break; +    } + +    if (res) { +        CC_SRC |= CC_C; +    } +    if (res & 1) { +        CC_SRC |= CC_O; +    } + +    return res; +} + +void glue(helper_pcmpestri, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, +                                    uint32_t ctrl) +{ +    unsigned int res = pcmpxstrx(env, d, s, ctrl, +                                 pcmp_elen(env, R_EDX, ctrl), +                                 pcmp_elen(env, R_EAX, ctrl)); + +    if (res) { +        env->regs[R_ECX] = (ctrl & (1 << 6)) ? 31 - clz32(res) : ctz32(res); +    } else { +        env->regs[R_ECX] = 16 >> (ctrl & (1 << 0)); +    } +} + +void glue(helper_pcmpestrm, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, +                                    uint32_t ctrl) +{ +    int i; +    unsigned int res = pcmpxstrx(env, d, s, ctrl, +                                 pcmp_elen(env, R_EDX, ctrl), +                                 pcmp_elen(env, R_EAX, ctrl)); + +    if ((ctrl >> 6) & 1) { +        if (ctrl & 1) { +            for (i = 0; i < 8; i++, res >>= 1) { +                env->xmm_regs[0].W(i) = (res & 1) ? ~0 : 0; +            } +        } else { +            for (i = 0; i < 16; i++, res >>= 1) { +                env->xmm_regs[0].B(i) = (res & 1) ? ~0 : 0; +            } +        } +    } else { +        env->xmm_regs[0].Q(1) = 0; +        env->xmm_regs[0].Q(0) = res; +    } +} + +void glue(helper_pcmpistri, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, +                                    uint32_t ctrl) +{ +    unsigned int res = pcmpxstrx(env, d, s, ctrl, +                                 pcmp_ilen(s, ctrl), +                                 pcmp_ilen(d, ctrl)); + +    if (res) { +        env->regs[R_ECX] = (ctrl & (1 << 6)) ? 31 - clz32(res) : ctz32(res); +    } else { +        env->regs[R_ECX] = 16 >> (ctrl & (1 << 0)); +    } +} + +void glue(helper_pcmpistrm, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, +                                    uint32_t ctrl) +{ +    int i; +    unsigned int res = pcmpxstrx(env, d, s, ctrl, +                                 pcmp_ilen(s, ctrl), +                                 pcmp_ilen(d, ctrl)); + +    if ((ctrl >> 6) & 1) { +        if (ctrl & 1) { +            for (i = 0; i < 8; i++, res >>= 1) { +                env->xmm_regs[0].W(i) = (res & 1) ? ~0 : 0; +            } +        } else { +            for (i = 0; i < 16; i++, res >>= 1) { +                env->xmm_regs[0].B(i) = (res & 1) ? ~0 : 0; +            } +        } +    } else { +        env->xmm_regs[0].Q(1) = 0; +        env->xmm_regs[0].Q(0) = res; +    } +} + +#define CRCPOLY        0x1edc6f41 +#define CRCPOLY_BITREV 0x82f63b78 +target_ulong helper_crc32(uint32_t crc1, target_ulong msg, uint32_t len) +{ +    target_ulong crc = (msg & ((target_ulong) -1 >> +                               (TARGET_LONG_BITS - len))) ^ crc1; + +    while (len--) { +        crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_BITREV : 0); +    } + +    return crc; +} + +#define POPMASK(i)     ((target_ulong) -1 / ((1LL << (1 << i)) + 1)) +#define POPCOUNT(n, i) ((n & POPMASK(i)) + ((n >> (1 << i)) & POPMASK(i))) +target_ulong helper_popcnt(CPUX86State *env, target_ulong n, uint32_t type) +{ +    CC_SRC = n ? 0 : CC_Z; + +    n = POPCOUNT(n, 0); +    n = POPCOUNT(n, 1); +    n = POPCOUNT(n, 2); +    n = POPCOUNT(n, 3); +    if (type == 1) { +        return n & 0xff; +    } + +    n = POPCOUNT(n, 4); +#ifndef TARGET_X86_64 +    return n; +#else +    if (type == 2) { +        return n & 0xff; +    } + +    return POPCOUNT(n, 5); +#endif +} + +void glue(helper_pclmulqdq, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, +                                    uint32_t ctrl) +{ +    uint64_t ah, al, b, resh, resl; + +    ah = 0; +    al = d->Q((ctrl & 1) != 0); +    b = s->Q((ctrl & 16) != 0); +    resh = resl = 0; + +    while (b) { +        if (b & 1) { +            resl ^= al; +            resh ^= ah; +        } +        ah = (ah << 1) | (al >> 63); +        al <<= 1; +        b >>= 1; +    } + +    d->Q(0) = resl; +    d->Q(1) = resh; +} + +void glue(helper_aesdec, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    int i; +    Reg st = *d; +    Reg rk = *s; + +    for (i = 0 ; i < 4 ; i++) { +        d->L(i) = rk.L(i) ^ bswap32(AES_Td0[st.B(AES_ishifts[4*i+0])] ^ +                                    AES_Td1[st.B(AES_ishifts[4*i+1])] ^ +                                    AES_Td2[st.B(AES_ishifts[4*i+2])] ^ +                                    AES_Td3[st.B(AES_ishifts[4*i+3])]); +    } +} + +void glue(helper_aesdeclast, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    int i; +    Reg st = *d; +    Reg rk = *s; + +    for (i = 0; i < 16; i++) { +        d->B(i) = rk.B(i) ^ (AES_isbox[st.B(AES_ishifts[i])]); +    } +} + +void glue(helper_aesenc, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    int i; +    Reg st = *d; +    Reg rk = *s; + +    for (i = 0 ; i < 4 ; i++) { +        d->L(i) = rk.L(i) ^ bswap32(AES_Te0[st.B(AES_shifts[4*i+0])] ^ +                                    AES_Te1[st.B(AES_shifts[4*i+1])] ^ +                                    AES_Te2[st.B(AES_shifts[4*i+2])] ^ +                                    AES_Te3[st.B(AES_shifts[4*i+3])]); +    } +} + +void glue(helper_aesenclast, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    int i; +    Reg st = *d; +    Reg rk = *s; + +    for (i = 0; i < 16; i++) { +        d->B(i) = rk.B(i) ^ (AES_sbox[st.B(AES_shifts[i])]); +    } + +} + +void glue(helper_aesimc, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) +{ +    int i; +    Reg tmp = *s; + +    for (i = 0 ; i < 4 ; i++) { +        d->L(i) = bswap32(AES_imc[tmp.B(4*i+0)][0] ^ +                          AES_imc[tmp.B(4*i+1)][1] ^ +                          AES_imc[tmp.B(4*i+2)][2] ^ +                          AES_imc[tmp.B(4*i+3)][3]); +    } +} + +void glue(helper_aeskeygenassist, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, +                                          uint32_t ctrl) +{ +    int i; +    Reg tmp = *s; + +    for (i = 0 ; i < 4 ; i++) { +        d->B(i) = AES_sbox[tmp.B(i + 4)]; +        d->B(i + 8) = AES_sbox[tmp.B(i + 12)]; +    } +    d->L(1) = (d->L(0) << 24 | d->L(0) >> 8) ^ ctrl; +    d->L(3) = (d->L(2) << 24 | d->L(2) >> 8) ^ ctrl; +} +#endif + +#undef SHIFT +#undef XMM_ONLY +#undef Reg +#undef B +#undef W +#undef L +#undef Q +#undef SUFFIX diff --git a/target-i386/ops_sse_header.h b/target-i386/ops_sse_header.h new file mode 100644 index 00000000..a68c7cc0 --- /dev/null +++ b/target-i386/ops_sse_header.h @@ -0,0 +1,360 @@ +/* + *  MMX/3DNow!/SSE/SSE2/SSE3/SSSE3/SSE4/PNI support + * + *  Copyright (c) 2005 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#if SHIFT == 0 +#define Reg MMXReg +#define SUFFIX _mmx +#else +#define Reg XMMReg +#define SUFFIX _xmm +#endif + +#define dh_alias_Reg ptr +#define dh_alias_XMMReg ptr +#define dh_alias_MMXReg ptr +#define dh_ctype_Reg Reg * +#define dh_ctype_XMMReg XMMReg * +#define dh_ctype_MMXReg MMXReg * +#define dh_is_signed_Reg dh_is_signed_ptr +#define dh_is_signed_XMMReg dh_is_signed_ptr +#define dh_is_signed_MMXReg dh_is_signed_ptr + +DEF_HELPER_3(glue(psrlw, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(psraw, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(psllw, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(psrld, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(psrad, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pslld, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(psrlq, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(psllq, SUFFIX), void, env, Reg, Reg) + +#if SHIFT == 1 +DEF_HELPER_3(glue(psrldq, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pslldq, SUFFIX), void, env, Reg, Reg) +#endif + +#define SSE_HELPER_B(name, F)\ +    DEF_HELPER_3(glue(name, SUFFIX), void, env, Reg, Reg) + +#define SSE_HELPER_W(name, F)\ +    DEF_HELPER_3(glue(name, SUFFIX), void, env, Reg, Reg) + +#define SSE_HELPER_L(name, F)\ +    DEF_HELPER_3(glue(name, SUFFIX), void, env, Reg, Reg) + +#define SSE_HELPER_Q(name, F)\ +    DEF_HELPER_3(glue(name, SUFFIX), void, env, Reg, Reg) + +SSE_HELPER_B(paddb, FADD) +SSE_HELPER_W(paddw, FADD) +SSE_HELPER_L(paddl, FADD) +SSE_HELPER_Q(paddq, FADD) + +SSE_HELPER_B(psubb, FSUB) +SSE_HELPER_W(psubw, FSUB) +SSE_HELPER_L(psubl, FSUB) +SSE_HELPER_Q(psubq, FSUB) + +SSE_HELPER_B(paddusb, FADDUB) +SSE_HELPER_B(paddsb, FADDSB) +SSE_HELPER_B(psubusb, FSUBUB) +SSE_HELPER_B(psubsb, FSUBSB) + +SSE_HELPER_W(paddusw, FADDUW) +SSE_HELPER_W(paddsw, FADDSW) +SSE_HELPER_W(psubusw, FSUBUW) +SSE_HELPER_W(psubsw, FSUBSW) + +SSE_HELPER_B(pminub, FMINUB) +SSE_HELPER_B(pmaxub, FMAXUB) + +SSE_HELPER_W(pminsw, FMINSW) +SSE_HELPER_W(pmaxsw, FMAXSW) + +SSE_HELPER_Q(pand, FAND) +SSE_HELPER_Q(pandn, FANDN) +SSE_HELPER_Q(por, FOR) +SSE_HELPER_Q(pxor, FXOR) + +SSE_HELPER_B(pcmpgtb, FCMPGTB) +SSE_HELPER_W(pcmpgtw, FCMPGTW) +SSE_HELPER_L(pcmpgtl, FCMPGTL) + +SSE_HELPER_B(pcmpeqb, FCMPEQ) +SSE_HELPER_W(pcmpeqw, FCMPEQ) +SSE_HELPER_L(pcmpeql, FCMPEQ) + +SSE_HELPER_W(pmullw, FMULLW) +#if SHIFT == 0 +SSE_HELPER_W(pmulhrw, FMULHRW) +#endif +SSE_HELPER_W(pmulhuw, FMULHUW) +SSE_HELPER_W(pmulhw, FMULHW) + +SSE_HELPER_B(pavgb, FAVG) +SSE_HELPER_W(pavgw, FAVG) + +DEF_HELPER_3(glue(pmuludq, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmaddwd, SUFFIX), void, env, Reg, Reg) + +DEF_HELPER_3(glue(psadbw, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_4(glue(maskmov, SUFFIX), void, env, Reg, Reg, tl) +DEF_HELPER_2(glue(movl_mm_T0, SUFFIX), void, Reg, i32) +#ifdef TARGET_X86_64 +DEF_HELPER_2(glue(movq_mm_T0, SUFFIX), void, Reg, i64) +#endif + +#if SHIFT == 0 +DEF_HELPER_3(glue(pshufw, SUFFIX), void, Reg, Reg, int) +#else +DEF_HELPER_3(shufps, void, Reg, Reg, int) +DEF_HELPER_3(shufpd, void, Reg, Reg, int) +DEF_HELPER_3(glue(pshufd, SUFFIX), void, Reg, Reg, int) +DEF_HELPER_3(glue(pshuflw, SUFFIX), void, Reg, Reg, int) +DEF_HELPER_3(glue(pshufhw, SUFFIX), void, Reg, Reg, int) +#endif + +#if SHIFT == 1 +/* FPU ops */ +/* XXX: not accurate */ + +#define SSE_HELPER_S(name, F)                            \ +    DEF_HELPER_3(name ## ps, void, env, Reg, Reg)        \ +    DEF_HELPER_3(name ## ss, void, env, Reg, Reg)        \ +    DEF_HELPER_3(name ## pd, void, env, Reg, Reg)        \ +    DEF_HELPER_3(name ## sd, void, env, Reg, Reg) + +SSE_HELPER_S(add, FPU_ADD) +SSE_HELPER_S(sub, FPU_SUB) +SSE_HELPER_S(mul, FPU_MUL) +SSE_HELPER_S(div, FPU_DIV) +SSE_HELPER_S(min, FPU_MIN) +SSE_HELPER_S(max, FPU_MAX) +SSE_HELPER_S(sqrt, FPU_SQRT) + + +DEF_HELPER_3(cvtps2pd, void, env, Reg, Reg) +DEF_HELPER_3(cvtpd2ps, void, env, Reg, Reg) +DEF_HELPER_3(cvtss2sd, void, env, Reg, Reg) +DEF_HELPER_3(cvtsd2ss, void, env, Reg, Reg) +DEF_HELPER_3(cvtdq2ps, void, env, Reg, Reg) +DEF_HELPER_3(cvtdq2pd, void, env, Reg, Reg) +DEF_HELPER_3(cvtpi2ps, void, env, XMMReg, MMXReg) +DEF_HELPER_3(cvtpi2pd, void, env, XMMReg, MMXReg) +DEF_HELPER_3(cvtsi2ss, void, env, XMMReg, i32) +DEF_HELPER_3(cvtsi2sd, void, env, XMMReg, i32) + +#ifdef TARGET_X86_64 +DEF_HELPER_3(cvtsq2ss, void, env, XMMReg, i64) +DEF_HELPER_3(cvtsq2sd, void, env, XMMReg, i64) +#endif + +DEF_HELPER_3(cvtps2dq, void, env, XMMReg, XMMReg) +DEF_HELPER_3(cvtpd2dq, void, env, XMMReg, XMMReg) +DEF_HELPER_3(cvtps2pi, void, env, MMXReg, XMMReg) +DEF_HELPER_3(cvtpd2pi, void, env, MMXReg, XMMReg) +DEF_HELPER_2(cvtss2si, s32, env, XMMReg) +DEF_HELPER_2(cvtsd2si, s32, env, XMMReg) +#ifdef TARGET_X86_64 +DEF_HELPER_2(cvtss2sq, s64, env, XMMReg) +DEF_HELPER_2(cvtsd2sq, s64, env, XMMReg) +#endif + +DEF_HELPER_3(cvttps2dq, void, env, XMMReg, XMMReg) +DEF_HELPER_3(cvttpd2dq, void, env, XMMReg, XMMReg) +DEF_HELPER_3(cvttps2pi, void, env, MMXReg, XMMReg) +DEF_HELPER_3(cvttpd2pi, void, env, MMXReg, XMMReg) +DEF_HELPER_2(cvttss2si, s32, env, XMMReg) +DEF_HELPER_2(cvttsd2si, s32, env, XMMReg) +#ifdef TARGET_X86_64 +DEF_HELPER_2(cvttss2sq, s64, env, XMMReg) +DEF_HELPER_2(cvttsd2sq, s64, env, XMMReg) +#endif + +DEF_HELPER_3(rsqrtps, void, env, XMMReg, XMMReg) +DEF_HELPER_3(rsqrtss, void, env, XMMReg, XMMReg) +DEF_HELPER_3(rcpps, void, env, XMMReg, XMMReg) +DEF_HELPER_3(rcpss, void, env, XMMReg, XMMReg) +DEF_HELPER_3(extrq_r, void, env, XMMReg, XMMReg) +DEF_HELPER_4(extrq_i, void, env, XMMReg, int, int) +DEF_HELPER_3(insertq_r, void, env, XMMReg, XMMReg) +DEF_HELPER_4(insertq_i, void, env, XMMReg, int, int) +DEF_HELPER_3(haddps, void, env, XMMReg, XMMReg) +DEF_HELPER_3(haddpd, void, env, XMMReg, XMMReg) +DEF_HELPER_3(hsubps, void, env, XMMReg, XMMReg) +DEF_HELPER_3(hsubpd, void, env, XMMReg, XMMReg) +DEF_HELPER_3(addsubps, void, env, XMMReg, XMMReg) +DEF_HELPER_3(addsubpd, void, env, XMMReg, XMMReg) + +#define SSE_HELPER_CMP(name, F)                           \ +    DEF_HELPER_3(name ## ps, void, env, Reg, Reg)         \ +    DEF_HELPER_3(name ## ss, void, env, Reg, Reg)         \ +    DEF_HELPER_3(name ## pd, void, env, Reg, Reg)         \ +    DEF_HELPER_3(name ## sd, void, env, Reg, Reg) + +SSE_HELPER_CMP(cmpeq, FPU_CMPEQ) +SSE_HELPER_CMP(cmplt, FPU_CMPLT) +SSE_HELPER_CMP(cmple, FPU_CMPLE) +SSE_HELPER_CMP(cmpunord, FPU_CMPUNORD) +SSE_HELPER_CMP(cmpneq, FPU_CMPNEQ) +SSE_HELPER_CMP(cmpnlt, FPU_CMPNLT) +SSE_HELPER_CMP(cmpnle, FPU_CMPNLE) +SSE_HELPER_CMP(cmpord, FPU_CMPORD) + +DEF_HELPER_3(ucomiss, void, env, Reg, Reg) +DEF_HELPER_3(comiss, void, env, Reg, Reg) +DEF_HELPER_3(ucomisd, void, env, Reg, Reg) +DEF_HELPER_3(comisd, void, env, Reg, Reg) +DEF_HELPER_2(movmskps, i32, env, Reg) +DEF_HELPER_2(movmskpd, i32, env, Reg) +#endif + +DEF_HELPER_2(glue(pmovmskb, SUFFIX), i32, env, Reg) +DEF_HELPER_3(glue(packsswb, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(packuswb, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(packssdw, SUFFIX), void, env, Reg, Reg) +#define UNPCK_OP(base_name, base)                                       \ +    DEF_HELPER_3(glue(punpck ## base_name ## bw, SUFFIX), void, env, Reg, Reg) \ +    DEF_HELPER_3(glue(punpck ## base_name ## wd, SUFFIX), void, env, Reg, Reg) \ +    DEF_HELPER_3(glue(punpck ## base_name ## dq, SUFFIX), void, env, Reg, Reg) + +UNPCK_OP(l, 0) +UNPCK_OP(h, 1) + +#if SHIFT == 1 +DEF_HELPER_3(glue(punpcklqdq, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(punpckhqdq, SUFFIX), void, env, Reg, Reg) +#endif + +/* 3DNow! float ops */ +#if SHIFT == 0 +DEF_HELPER_3(pi2fd, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pi2fw, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pf2id, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pf2iw, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pfacc, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pfadd, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pfcmpeq, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pfcmpge, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pfcmpgt, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pfmax, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pfmin, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pfmul, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pfnacc, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pfpnacc, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pfrcp, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pfrsqrt, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pfsub, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pfsubr, void, env, MMXReg, MMXReg) +DEF_HELPER_3(pswapd, void, env, MMXReg, MMXReg) +#endif + +/* SSSE3 op helpers */ +DEF_HELPER_3(glue(phaddw, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(phaddd, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(phaddsw, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(phsubw, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(phsubd, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(phsubsw, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pabsb, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pabsw, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pabsd, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmaddubsw, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmulhrsw, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pshufb, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(psignb, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(psignw, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(psignd, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_4(glue(palignr, SUFFIX), void, env, Reg, Reg, s32) + +/* SSE4.1 op helpers */ +#if SHIFT == 1 +DEF_HELPER_3(glue(pblendvb, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(blendvps, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(blendvpd, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(ptest, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmovsxbw, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmovsxbd, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmovsxbq, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmovsxwd, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmovsxwq, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmovsxdq, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmovzxbw, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmovzxbd, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmovzxbq, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmovzxwd, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmovzxwq, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmovzxdq, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmuldq, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pcmpeqq, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(packusdw, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pminsb, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pminsd, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pminuw, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pminud, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmaxsb, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmaxsd, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmaxuw, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmaxud, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(pmulld, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(phminposuw, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_4(glue(roundps, SUFFIX), void, env, Reg, Reg, i32) +DEF_HELPER_4(glue(roundpd, SUFFIX), void, env, Reg, Reg, i32) +DEF_HELPER_4(glue(roundss, SUFFIX), void, env, Reg, Reg, i32) +DEF_HELPER_4(glue(roundsd, SUFFIX), void, env, Reg, Reg, i32) +DEF_HELPER_4(glue(blendps, SUFFIX), void, env, Reg, Reg, i32) +DEF_HELPER_4(glue(blendpd, SUFFIX), void, env, Reg, Reg, i32) +DEF_HELPER_4(glue(pblendw, SUFFIX), void, env, Reg, Reg, i32) +DEF_HELPER_4(glue(dpps, SUFFIX), void, env, Reg, Reg, i32) +DEF_HELPER_4(glue(dppd, SUFFIX), void, env, Reg, Reg, i32) +DEF_HELPER_4(glue(mpsadbw, SUFFIX), void, env, Reg, Reg, i32) +#endif + +/* SSE4.2 op helpers */ +#if SHIFT == 1 +DEF_HELPER_3(glue(pcmpgtq, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_4(glue(pcmpestri, SUFFIX), void, env, Reg, Reg, i32) +DEF_HELPER_4(glue(pcmpestrm, SUFFIX), void, env, Reg, Reg, i32) +DEF_HELPER_4(glue(pcmpistri, SUFFIX), void, env, Reg, Reg, i32) +DEF_HELPER_4(glue(pcmpistrm, SUFFIX), void, env, Reg, Reg, i32) +DEF_HELPER_3(crc32, tl, i32, tl, i32) +DEF_HELPER_3(popcnt, tl, env, tl, i32) +#endif + +/* AES-NI op helpers */ +#if SHIFT == 1 +DEF_HELPER_3(glue(aesdec, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(aesdeclast, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(aesenc, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(aesenclast, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_3(glue(aesimc, SUFFIX), void, env, Reg, Reg) +DEF_HELPER_4(glue(aeskeygenassist, SUFFIX), void, env, Reg, Reg, i32) +DEF_HELPER_4(glue(pclmulqdq, SUFFIX), void, env, Reg, Reg, i32) +#endif + +#undef SHIFT +#undef Reg +#undef SUFFIX + +#undef SSE_HELPER_B +#undef SSE_HELPER_W +#undef SSE_HELPER_L +#undef SSE_HELPER_Q +#undef SSE_HELPER_S +#undef SSE_HELPER_CMP +#undef UNPCK_OP diff --git a/target-i386/seg_helper.c b/target-i386/seg_helper.c new file mode 100644 index 00000000..8a4271eb --- /dev/null +++ b/target-i386/seg_helper.c @@ -0,0 +1,2604 @@ +/* + *  x86 segmentation related helpers: + *  TSS, interrupts, system calls, jumps and call/task gates, descriptors + * + *  Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "cpu.h" +#include "qemu/log.h" +#include "exec/helper-proto.h" +#include "exec/cpu_ldst.h" + +//#define DEBUG_PCALL + +#ifdef DEBUG_PCALL +# define LOG_PCALL(...) qemu_log_mask(CPU_LOG_PCALL, ## __VA_ARGS__) +# define LOG_PCALL_STATE(cpu)                                  \ +    log_cpu_state_mask(CPU_LOG_PCALL, (cpu), CPU_DUMP_CCOP) +#else +# define LOG_PCALL(...) do { } while (0) +# define LOG_PCALL_STATE(cpu) do { } while (0) +#endif + +#ifdef CONFIG_USER_ONLY +#define MEMSUFFIX _kernel +#define DATA_SIZE 1 +#include "exec/cpu_ldst_useronly_template.h" + +#define DATA_SIZE 2 +#include "exec/cpu_ldst_useronly_template.h" + +#define DATA_SIZE 4 +#include "exec/cpu_ldst_useronly_template.h" + +#define DATA_SIZE 8 +#include "exec/cpu_ldst_useronly_template.h" +#undef MEMSUFFIX +#else +#define CPU_MMU_INDEX (cpu_mmu_index_kernel(env)) +#define MEMSUFFIX _kernel +#define DATA_SIZE 1 +#include "exec/cpu_ldst_template.h" + +#define DATA_SIZE 2 +#include "exec/cpu_ldst_template.h" + +#define DATA_SIZE 4 +#include "exec/cpu_ldst_template.h" + +#define DATA_SIZE 8 +#include "exec/cpu_ldst_template.h" +#undef CPU_MMU_INDEX +#undef MEMSUFFIX +#endif + +/* return non zero if error */ +static inline int load_segment(CPUX86State *env, uint32_t *e1_ptr, +                               uint32_t *e2_ptr, int selector) +{ +    SegmentCache *dt; +    int index; +    target_ulong ptr; + +    if (selector & 0x4) { +        dt = &env->ldt; +    } else { +        dt = &env->gdt; +    } +    index = selector & ~7; +    if ((index + 7) > dt->limit) { +        return -1; +    } +    ptr = dt->base + index; +    *e1_ptr = cpu_ldl_kernel(env, ptr); +    *e2_ptr = cpu_ldl_kernel(env, ptr + 4); +    return 0; +} + +static inline unsigned int get_seg_limit(uint32_t e1, uint32_t e2) +{ +    unsigned int limit; + +    limit = (e1 & 0xffff) | (e2 & 0x000f0000); +    if (e2 & DESC_G_MASK) { +        limit = (limit << 12) | 0xfff; +    } +    return limit; +} + +static inline uint32_t get_seg_base(uint32_t e1, uint32_t e2) +{ +    return (e1 >> 16) | ((e2 & 0xff) << 16) | (e2 & 0xff000000); +} + +static inline void load_seg_cache_raw_dt(SegmentCache *sc, uint32_t e1, +                                         uint32_t e2) +{ +    sc->base = get_seg_base(e1, e2); +    sc->limit = get_seg_limit(e1, e2); +    sc->flags = e2; +} + +/* init the segment cache in vm86 mode. */ +static inline void load_seg_vm(CPUX86State *env, int seg, int selector) +{ +    selector &= 0xffff; + +    cpu_x86_load_seg_cache(env, seg, selector, (selector << 4), 0xffff, +                           DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | +                           DESC_A_MASK | (3 << DESC_DPL_SHIFT)); +} + +static inline void get_ss_esp_from_tss(CPUX86State *env, uint32_t *ss_ptr, +                                       uint32_t *esp_ptr, int dpl) +{ +    X86CPU *cpu = x86_env_get_cpu(env); +    int type, index, shift; + +#if 0 +    { +        int i; +        printf("TR: base=%p limit=%x\n", env->tr.base, env->tr.limit); +        for (i = 0; i < env->tr.limit; i++) { +            printf("%02x ", env->tr.base[i]); +            if ((i & 7) == 7) { +                printf("\n"); +            } +        } +        printf("\n"); +    } +#endif + +    if (!(env->tr.flags & DESC_P_MASK)) { +        cpu_abort(CPU(cpu), "invalid tss"); +    } +    type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf; +    if ((type & 7) != 1) { +        cpu_abort(CPU(cpu), "invalid tss type"); +    } +    shift = type >> 3; +    index = (dpl * 4 + 2) << shift; +    if (index + (4 << shift) - 1 > env->tr.limit) { +        raise_exception_err(env, EXCP0A_TSS, env->tr.selector & 0xfffc); +    } +    if (shift == 0) { +        *esp_ptr = cpu_lduw_kernel(env, env->tr.base + index); +        *ss_ptr = cpu_lduw_kernel(env, env->tr.base + index + 2); +    } else { +        *esp_ptr = cpu_ldl_kernel(env, env->tr.base + index); +        *ss_ptr = cpu_lduw_kernel(env, env->tr.base + index + 4); +    } +} + +static void tss_load_seg(CPUX86State *env, int seg_reg, int selector, int cpl) +{ +    uint32_t e1, e2; +    int rpl, dpl; + +    if ((selector & 0xfffc) != 0) { +        if (load_segment(env, &e1, &e2, selector) != 0) { +            raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc); +        } +        if (!(e2 & DESC_S_MASK)) { +            raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc); +        } +        rpl = selector & 3; +        dpl = (e2 >> DESC_DPL_SHIFT) & 3; +        if (seg_reg == R_CS) { +            if (!(e2 & DESC_CS_MASK)) { +                raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc); +            } +            if (dpl != rpl) { +                raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc); +            } +        } else if (seg_reg == R_SS) { +            /* SS must be writable data */ +            if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK)) { +                raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc); +            } +            if (dpl != cpl || dpl != rpl) { +                raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc); +            } +        } else { +            /* not readable code */ +            if ((e2 & DESC_CS_MASK) && !(e2 & DESC_R_MASK)) { +                raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc); +            } +            /* if data or non conforming code, checks the rights */ +            if (((e2 >> DESC_TYPE_SHIFT) & 0xf) < 12) { +                if (dpl < cpl || dpl < rpl) { +                    raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc); +                } +            } +        } +        if (!(e2 & DESC_P_MASK)) { +            raise_exception_err(env, EXCP0B_NOSEG, selector & 0xfffc); +        } +        cpu_x86_load_seg_cache(env, seg_reg, selector, +                               get_seg_base(e1, e2), +                               get_seg_limit(e1, e2), +                               e2); +    } else { +        if (seg_reg == R_SS || seg_reg == R_CS) { +            raise_exception_err(env, EXCP0A_TSS, selector & 0xfffc); +        } +    } +} + +#define SWITCH_TSS_JMP  0 +#define SWITCH_TSS_IRET 1 +#define SWITCH_TSS_CALL 2 + +/* XXX: restore CPU state in registers (PowerPC case) */ +static void switch_tss(CPUX86State *env, int tss_selector, +                       uint32_t e1, uint32_t e2, int source, +                       uint32_t next_eip) +{ +    int tss_limit, tss_limit_max, type, old_tss_limit_max, old_type, v1, v2, i; +    target_ulong tss_base; +    uint32_t new_regs[8], new_segs[6]; +    uint32_t new_eflags, new_eip, new_cr3, new_ldt, new_trap; +    uint32_t old_eflags, eflags_mask; +    SegmentCache *dt; +    int index; +    target_ulong ptr; + +    type = (e2 >> DESC_TYPE_SHIFT) & 0xf; +    LOG_PCALL("switch_tss: sel=0x%04x type=%d src=%d\n", tss_selector, type, +              source); + +    /* if task gate, we read the TSS segment and we load it */ +    if (type == 5) { +        if (!(e2 & DESC_P_MASK)) { +            raise_exception_err(env, EXCP0B_NOSEG, tss_selector & 0xfffc); +        } +        tss_selector = e1 >> 16; +        if (tss_selector & 4) { +            raise_exception_err(env, EXCP0A_TSS, tss_selector & 0xfffc); +        } +        if (load_segment(env, &e1, &e2, tss_selector) != 0) { +            raise_exception_err(env, EXCP0D_GPF, tss_selector & 0xfffc); +        } +        if (e2 & DESC_S_MASK) { +            raise_exception_err(env, EXCP0D_GPF, tss_selector & 0xfffc); +        } +        type = (e2 >> DESC_TYPE_SHIFT) & 0xf; +        if ((type & 7) != 1) { +            raise_exception_err(env, EXCP0D_GPF, tss_selector & 0xfffc); +        } +    } + +    if (!(e2 & DESC_P_MASK)) { +        raise_exception_err(env, EXCP0B_NOSEG, tss_selector & 0xfffc); +    } + +    if (type & 8) { +        tss_limit_max = 103; +    } else { +        tss_limit_max = 43; +    } +    tss_limit = get_seg_limit(e1, e2); +    tss_base = get_seg_base(e1, e2); +    if ((tss_selector & 4) != 0 || +        tss_limit < tss_limit_max) { +        raise_exception_err(env, EXCP0A_TSS, tss_selector & 0xfffc); +    } +    old_type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf; +    if (old_type & 8) { +        old_tss_limit_max = 103; +    } else { +        old_tss_limit_max = 43; +    } + +    /* read all the registers from the new TSS */ +    if (type & 8) { +        /* 32 bit */ +        new_cr3 = cpu_ldl_kernel(env, tss_base + 0x1c); +        new_eip = cpu_ldl_kernel(env, tss_base + 0x20); +        new_eflags = cpu_ldl_kernel(env, tss_base + 0x24); +        for (i = 0; i < 8; i++) { +            new_regs[i] = cpu_ldl_kernel(env, tss_base + (0x28 + i * 4)); +        } +        for (i = 0; i < 6; i++) { +            new_segs[i] = cpu_lduw_kernel(env, tss_base + (0x48 + i * 4)); +        } +        new_ldt = cpu_lduw_kernel(env, tss_base + 0x60); +        new_trap = cpu_ldl_kernel(env, tss_base + 0x64); +    } else { +        /* 16 bit */ +        new_cr3 = 0; +        new_eip = cpu_lduw_kernel(env, tss_base + 0x0e); +        new_eflags = cpu_lduw_kernel(env, tss_base + 0x10); +        for (i = 0; i < 8; i++) { +            new_regs[i] = cpu_lduw_kernel(env, tss_base + (0x12 + i * 2)) | +                0xffff0000; +        } +        for (i = 0; i < 4; i++) { +            new_segs[i] = cpu_lduw_kernel(env, tss_base + (0x22 + i * 4)); +        } +        new_ldt = cpu_lduw_kernel(env, tss_base + 0x2a); +        new_segs[R_FS] = 0; +        new_segs[R_GS] = 0; +        new_trap = 0; +    } +    /* XXX: avoid a compiler warning, see +     http://support.amd.com/us/Processor_TechDocs/24593.pdf +     chapters 12.2.5 and 13.2.4 on how to implement TSS Trap bit */ +    (void)new_trap; + +    /* NOTE: we must avoid memory exceptions during the task switch, +       so we make dummy accesses before */ +    /* XXX: it can still fail in some cases, so a bigger hack is +       necessary to valid the TLB after having done the accesses */ + +    v1 = cpu_ldub_kernel(env, env->tr.base); +    v2 = cpu_ldub_kernel(env, env->tr.base + old_tss_limit_max); +    cpu_stb_kernel(env, env->tr.base, v1); +    cpu_stb_kernel(env, env->tr.base + old_tss_limit_max, v2); + +    /* clear busy bit (it is restartable) */ +    if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_IRET) { +        target_ulong ptr; +        uint32_t e2; + +        ptr = env->gdt.base + (env->tr.selector & ~7); +        e2 = cpu_ldl_kernel(env, ptr + 4); +        e2 &= ~DESC_TSS_BUSY_MASK; +        cpu_stl_kernel(env, ptr + 4, e2); +    } +    old_eflags = cpu_compute_eflags(env); +    if (source == SWITCH_TSS_IRET) { +        old_eflags &= ~NT_MASK; +    } + +    /* save the current state in the old TSS */ +    if (type & 8) { +        /* 32 bit */ +        cpu_stl_kernel(env, env->tr.base + 0x20, next_eip); +        cpu_stl_kernel(env, env->tr.base + 0x24, old_eflags); +        cpu_stl_kernel(env, env->tr.base + (0x28 + 0 * 4), env->regs[R_EAX]); +        cpu_stl_kernel(env, env->tr.base + (0x28 + 1 * 4), env->regs[R_ECX]); +        cpu_stl_kernel(env, env->tr.base + (0x28 + 2 * 4), env->regs[R_EDX]); +        cpu_stl_kernel(env, env->tr.base + (0x28 + 3 * 4), env->regs[R_EBX]); +        cpu_stl_kernel(env, env->tr.base + (0x28 + 4 * 4), env->regs[R_ESP]); +        cpu_stl_kernel(env, env->tr.base + (0x28 + 5 * 4), env->regs[R_EBP]); +        cpu_stl_kernel(env, env->tr.base + (0x28 + 6 * 4), env->regs[R_ESI]); +        cpu_stl_kernel(env, env->tr.base + (0x28 + 7 * 4), env->regs[R_EDI]); +        for (i = 0; i < 6; i++) { +            cpu_stw_kernel(env, env->tr.base + (0x48 + i * 4), +                           env->segs[i].selector); +        } +    } else { +        /* 16 bit */ +        cpu_stw_kernel(env, env->tr.base + 0x0e, next_eip); +        cpu_stw_kernel(env, env->tr.base + 0x10, old_eflags); +        cpu_stw_kernel(env, env->tr.base + (0x12 + 0 * 2), env->regs[R_EAX]); +        cpu_stw_kernel(env, env->tr.base + (0x12 + 1 * 2), env->regs[R_ECX]); +        cpu_stw_kernel(env, env->tr.base + (0x12 + 2 * 2), env->regs[R_EDX]); +        cpu_stw_kernel(env, env->tr.base + (0x12 + 3 * 2), env->regs[R_EBX]); +        cpu_stw_kernel(env, env->tr.base + (0x12 + 4 * 2), env->regs[R_ESP]); +        cpu_stw_kernel(env, env->tr.base + (0x12 + 5 * 2), env->regs[R_EBP]); +        cpu_stw_kernel(env, env->tr.base + (0x12 + 6 * 2), env->regs[R_ESI]); +        cpu_stw_kernel(env, env->tr.base + (0x12 + 7 * 2), env->regs[R_EDI]); +        for (i = 0; i < 4; i++) { +            cpu_stw_kernel(env, env->tr.base + (0x22 + i * 4), +                           env->segs[i].selector); +        } +    } + +    /* now if an exception occurs, it will occurs in the next task +       context */ + +    if (source == SWITCH_TSS_CALL) { +        cpu_stw_kernel(env, tss_base, env->tr.selector); +        new_eflags |= NT_MASK; +    } + +    /* set busy bit */ +    if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_CALL) { +        target_ulong ptr; +        uint32_t e2; + +        ptr = env->gdt.base + (tss_selector & ~7); +        e2 = cpu_ldl_kernel(env, ptr + 4); +        e2 |= DESC_TSS_BUSY_MASK; +        cpu_stl_kernel(env, ptr + 4, e2); +    } + +    /* set the new CPU state */ +    /* from this point, any exception which occurs can give problems */ +    env->cr[0] |= CR0_TS_MASK; +    env->hflags |= HF_TS_MASK; +    env->tr.selector = tss_selector; +    env->tr.base = tss_base; +    env->tr.limit = tss_limit; +    env->tr.flags = e2 & ~DESC_TSS_BUSY_MASK; + +    if ((type & 8) && (env->cr[0] & CR0_PG_MASK)) { +        cpu_x86_update_cr3(env, new_cr3); +    } + +    /* load all registers without an exception, then reload them with +       possible exception */ +    env->eip = new_eip; +    eflags_mask = TF_MASK | AC_MASK | ID_MASK | +        IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | NT_MASK; +    if (!(type & 8)) { +        eflags_mask &= 0xffff; +    } +    cpu_load_eflags(env, new_eflags, eflags_mask); +    /* XXX: what to do in 16 bit case? */ +    env->regs[R_EAX] = new_regs[0]; +    env->regs[R_ECX] = new_regs[1]; +    env->regs[R_EDX] = new_regs[2]; +    env->regs[R_EBX] = new_regs[3]; +    env->regs[R_ESP] = new_regs[4]; +    env->regs[R_EBP] = new_regs[5]; +    env->regs[R_ESI] = new_regs[6]; +    env->regs[R_EDI] = new_regs[7]; +    if (new_eflags & VM_MASK) { +        for (i = 0; i < 6; i++) { +            load_seg_vm(env, i, new_segs[i]); +        } +    } else { +        /* first just selectors as the rest may trigger exceptions */ +        for (i = 0; i < 6; i++) { +            cpu_x86_load_seg_cache(env, i, new_segs[i], 0, 0, 0); +        } +    } + +    env->ldt.selector = new_ldt & ~4; +    env->ldt.base = 0; +    env->ldt.limit = 0; +    env->ldt.flags = 0; + +    /* load the LDT */ +    if (new_ldt & 4) { +        raise_exception_err(env, EXCP0A_TSS, new_ldt & 0xfffc); +    } + +    if ((new_ldt & 0xfffc) != 0) { +        dt = &env->gdt; +        index = new_ldt & ~7; +        if ((index + 7) > dt->limit) { +            raise_exception_err(env, EXCP0A_TSS, new_ldt & 0xfffc); +        } +        ptr = dt->base + index; +        e1 = cpu_ldl_kernel(env, ptr); +        e2 = cpu_ldl_kernel(env, ptr + 4); +        if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2) { +            raise_exception_err(env, EXCP0A_TSS, new_ldt & 0xfffc); +        } +        if (!(e2 & DESC_P_MASK)) { +            raise_exception_err(env, EXCP0A_TSS, new_ldt & 0xfffc); +        } +        load_seg_cache_raw_dt(&env->ldt, e1, e2); +    } + +    /* load the segments */ +    if (!(new_eflags & VM_MASK)) { +        int cpl = new_segs[R_CS] & 3; +        tss_load_seg(env, R_CS, new_segs[R_CS], cpl); +        tss_load_seg(env, R_SS, new_segs[R_SS], cpl); +        tss_load_seg(env, R_ES, new_segs[R_ES], cpl); +        tss_load_seg(env, R_DS, new_segs[R_DS], cpl); +        tss_load_seg(env, R_FS, new_segs[R_FS], cpl); +        tss_load_seg(env, R_GS, new_segs[R_GS], cpl); +    } + +    /* check that env->eip is in the CS segment limits */ +    if (new_eip > env->segs[R_CS].limit) { +        /* XXX: different exception if CALL? */ +        raise_exception_err(env, EXCP0D_GPF, 0); +    } + +#ifndef CONFIG_USER_ONLY +    /* reset local breakpoints */ +    if (env->dr[7] & DR7_LOCAL_BP_MASK) { +        for (i = 0; i < DR7_MAX_BP; i++) { +            if (hw_local_breakpoint_enabled(env->dr[7], i) && +                !hw_global_breakpoint_enabled(env->dr[7], i)) { +                hw_breakpoint_remove(env, i); +            } +        } +        env->dr[7] &= ~DR7_LOCAL_BP_MASK; +    } +#endif +} + +static inline unsigned int get_sp_mask(unsigned int e2) +{ +    if (e2 & DESC_B_MASK) { +        return 0xffffffff; +    } else { +        return 0xffff; +    } +} + +static int exception_has_error_code(int intno) +{ +    switch (intno) { +    case 8: +    case 10: +    case 11: +    case 12: +    case 13: +    case 14: +    case 17: +        return 1; +    } +    return 0; +} + +#ifdef TARGET_X86_64 +#define SET_ESP(val, sp_mask)                                   \ +    do {                                                        \ +        if ((sp_mask) == 0xffff) {                              \ +            env->regs[R_ESP] = (env->regs[R_ESP] & ~0xffff) |   \ +                ((val) & 0xffff);                               \ +        } else if ((sp_mask) == 0xffffffffLL) {                 \ +            env->regs[R_ESP] = (uint32_t)(val);                 \ +        } else {                                                \ +            env->regs[R_ESP] = (val);                           \ +        }                                                       \ +    } while (0) +#else +#define SET_ESP(val, sp_mask)                                   \ +    do {                                                        \ +        env->regs[R_ESP] = (env->regs[R_ESP] & ~(sp_mask)) |    \ +            ((val) & (sp_mask));                                \ +    } while (0) +#endif + +/* in 64-bit machines, this can overflow. So this segment addition macro + * can be used to trim the value to 32-bit whenever needed */ +#define SEG_ADDL(ssp, sp, sp_mask) ((uint32_t)((ssp) + (sp & (sp_mask)))) + +/* XXX: add a is_user flag to have proper security support */ +#define PUSHW(ssp, sp, sp_mask, val)                             \ +    {                                                            \ +        sp -= 2;                                                 \ +        cpu_stw_kernel(env, (ssp) + (sp & (sp_mask)), (val));    \ +    } + +#define PUSHL(ssp, sp, sp_mask, val)                                    \ +    {                                                                   \ +        sp -= 4;                                                        \ +        cpu_stl_kernel(env, SEG_ADDL(ssp, sp, sp_mask), (uint32_t)(val)); \ +    } + +#define POPW(ssp, sp, sp_mask, val)                              \ +    {                                                            \ +        val = cpu_lduw_kernel(env, (ssp) + (sp & (sp_mask)));    \ +        sp += 2;                                                 \ +    } + +#define POPL(ssp, sp, sp_mask, val)                                     \ +    {                                                                   \ +        val = (uint32_t)cpu_ldl_kernel(env, SEG_ADDL(ssp, sp, sp_mask)); \ +        sp += 4;                                                        \ +    } + +/* protected mode interrupt */ +static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, +                                   int error_code, unsigned int next_eip, +                                   int is_hw) +{ +    SegmentCache *dt; +    target_ulong ptr, ssp; +    int type, dpl, selector, ss_dpl, cpl; +    int has_error_code, new_stack, shift; +    uint32_t e1, e2, offset, ss = 0, esp, ss_e1 = 0, ss_e2 = 0; +    uint32_t old_eip, sp_mask; +    int vm86 = env->eflags & VM_MASK; + +    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 * 8 + 7 > dt->limit) { +        raise_exception_err(env, EXCP0D_GPF, intno * 8 + 2); +    } +    ptr = dt->base + intno * 8; +    e1 = cpu_ldl_kernel(env, ptr); +    e2 = cpu_ldl_kernel(env, ptr + 4); +    /* check gate type */ +    type = (e2 >> DESC_TYPE_SHIFT) & 0x1f; +    switch (type) { +    case 5: /* task gate */ +        /* must do that check here to return the correct error code */ +        if (!(e2 & DESC_P_MASK)) { +            raise_exception_err(env, EXCP0B_NOSEG, intno * 8 + 2); +        } +        switch_tss(env, intno * 8, e1, e2, SWITCH_TSS_CALL, old_eip); +        if (has_error_code) { +            int type; +            uint32_t mask; + +            /* push the error code */ +            type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf; +            shift = type >> 3; +            if (env->segs[R_SS].flags & DESC_B_MASK) { +                mask = 0xffffffff; +            } else { +                mask = 0xffff; +            } +            esp = (env->regs[R_ESP] - (2 << shift)) & mask; +            ssp = env->segs[R_SS].base + esp; +            if (shift) { +                cpu_stl_kernel(env, ssp, error_code); +            } else { +                cpu_stw_kernel(env, ssp, error_code); +            } +            SET_ESP(esp, mask); +        } +        return; +    case 6: /* 286 interrupt gate */ +    case 7: /* 286 trap gate */ +    case 14: /* 386 interrupt gate */ +    case 15: /* 386 trap gate */ +        break; +    default: +        raise_exception_err(env, EXCP0D_GPF, intno * 8 + 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 * 8 + 2); +    } +    /* check valid bit */ +    if (!(e2 & DESC_P_MASK)) { +        raise_exception_err(env, EXCP0B_NOSEG, intno * 8 + 2); +    } +    selector = e1 >> 16; +    offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff); +    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_C_MASK) && dpl < cpl) { +        /* to inner privilege */ +        get_ss_esp_from_tss(env, &ss, &esp, dpl); +        if ((ss & 0xfffc) == 0) { +            raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); +        } +        if ((ss & 3) != dpl) { +            raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); +        } +        if (load_segment(env, &ss_e1, &ss_e2, ss) != 0) { +            raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); +        } +        ss_dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3; +        if (ss_dpl != dpl) { +            raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); +        } +        if (!(ss_e2 & DESC_S_MASK) || +            (ss_e2 & DESC_CS_MASK) || +            !(ss_e2 & DESC_W_MASK)) { +            raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); +        } +        if (!(ss_e2 & DESC_P_MASK)) { +            raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); +        } +        new_stack = 1; +        sp_mask = get_sp_mask(ss_e2); +        ssp = get_seg_base(ss_e1, ss_e2); +    } else if ((e2 & DESC_C_MASK) || dpl == cpl) { +        /* to same privilege */ +        if (vm86) { +            raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); +        } +        new_stack = 0; +        sp_mask = get_sp_mask(env->segs[R_SS].flags); +        ssp = env->segs[R_SS].base; +        esp = env->regs[R_ESP]; +        dpl = cpl; +    } else { +        raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); +        new_stack = 0; /* avoid warning */ +        sp_mask = 0; /* avoid warning */ +        ssp = 0; /* avoid warning */ +        esp = 0; /* avoid warning */ +    } + +    shift = type >> 3; + +#if 0 +    /* XXX: check that enough room is available */ +    push_size = 6 + (new_stack << 2) + (has_error_code << 1); +    if (vm86) { +        push_size += 8; +    } +    push_size <<= shift; +#endif +    if (shift == 1) { +        if (new_stack) { +            if (vm86) { +                PUSHL(ssp, esp, sp_mask, env->segs[R_GS].selector); +                PUSHL(ssp, esp, sp_mask, env->segs[R_FS].selector); +                PUSHL(ssp, esp, sp_mask, env->segs[R_DS].selector); +                PUSHL(ssp, esp, sp_mask, env->segs[R_ES].selector); +            } +            PUSHL(ssp, esp, sp_mask, env->segs[R_SS].selector); +            PUSHL(ssp, esp, sp_mask, env->regs[R_ESP]); +        } +        PUSHL(ssp, esp, sp_mask, cpu_compute_eflags(env)); +        PUSHL(ssp, esp, sp_mask, env->segs[R_CS].selector); +        PUSHL(ssp, esp, sp_mask, old_eip); +        if (has_error_code) { +            PUSHL(ssp, esp, sp_mask, error_code); +        } +    } else { +        if (new_stack) { +            if (vm86) { +                PUSHW(ssp, esp, sp_mask, env->segs[R_GS].selector); +                PUSHW(ssp, esp, sp_mask, env->segs[R_FS].selector); +                PUSHW(ssp, esp, sp_mask, env->segs[R_DS].selector); +                PUSHW(ssp, esp, sp_mask, env->segs[R_ES].selector); +            } +            PUSHW(ssp, esp, sp_mask, env->segs[R_SS].selector); +            PUSHW(ssp, esp, sp_mask, env->regs[R_ESP]); +        } +        PUSHW(ssp, esp, sp_mask, cpu_compute_eflags(env)); +        PUSHW(ssp, esp, sp_mask, env->segs[R_CS].selector); +        PUSHW(ssp, esp, sp_mask, old_eip); +        if (has_error_code) { +            PUSHW(ssp, esp, sp_mask, 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) { +        if (vm86) { +            cpu_x86_load_seg_cache(env, R_ES, 0, 0, 0, 0); +            cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0, 0); +            cpu_x86_load_seg_cache(env, R_FS, 0, 0, 0, 0); +            cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0, 0); +        } +        ss = (ss & ~3) | dpl; +        cpu_x86_load_seg_cache(env, R_SS, ss, +                               ssp, get_seg_limit(ss_e1, ss_e2), ss_e2); +    } +    SET_ESP(esp, sp_mask); + +    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; +} + +#ifdef TARGET_X86_64 + +#define PUSHQ(sp, val)                          \ +    {                                           \ +        sp -= 8;                                \ +        cpu_stq_kernel(env, sp, (val));         \ +    } + +#define POPQ(sp, val)                           \ +    {                                           \ +        val = cpu_ldq_kernel(env, sp);          \ +        sp += 8;                                \ +    } + +static inline target_ulong get_rsp_from_tss(CPUX86State *env, int level) +{ +    X86CPU *cpu = x86_env_get_cpu(env); +    int index; + +#if 0 +    printf("TR: base=" TARGET_FMT_lx " limit=%x\n", +           env->tr.base, env->tr.limit); +#endif + +    if (!(env->tr.flags & DESC_P_MASK)) { +        cpu_abort(CPU(cpu), "invalid tss"); +    } +    index = 8 * level + 4; +    if ((index + 7) > env->tr.limit) { +        raise_exception_err(env, EXCP0A_TSS, env->tr.selector & 0xfffc); +    } +    return cpu_ldq_kernel(env, env->tr.base + index); +} + +/* 64 bit interrupt */ +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 */ +        new_stack = 1; +        esp = get_rsp_from_tss(env, ist != 0 ? ist + 3 : dpl); +        ss = 0; +    } 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; +        esp = env->regs[R_ESP]; +        dpl = cpl; +    } else { +        raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); +        new_stack = 0; /* avoid warning */ +        esp = 0; /* avoid warning */ +    } +    esp &= ~0xfLL; /* align stack */ + +    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; +} +#endif + +#ifdef TARGET_X86_64 +#if defined(CONFIG_USER_ONLY) +void helper_syscall(CPUX86State *env, int next_eip_addend) +{ +    CPUState *cs = CPU(x86_env_get_cpu(env)); + +    cs->exception_index = EXCP_SYSCALL; +    env->exception_next_eip = env->eip + next_eip_addend; +    cpu_loop_exit(cs); +} +#else +void helper_syscall(CPUX86State *env, int next_eip_addend) +{ +    int selector; + +    if (!(env->efer & MSR_EFER_SCE)) { +        raise_exception_err(env, EXCP06_ILLOP, 0); +    } +    selector = (env->star >> 32) & 0xffff; +    if (env->hflags & HF_LMA_MASK) { +        int code64; + +        env->regs[R_ECX] = env->eip + next_eip_addend; +        env->regs[11] = cpu_compute_eflags(env); + +        code64 = env->hflags & HF_CS64_MASK; + +        env->eflags &= ~env->fmask; +        cpu_load_eflags(env, env->eflags, 0); +        cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc, +                           0, 0xffffffff, +                               DESC_G_MASK | DESC_P_MASK | +                               DESC_S_MASK | +                               DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | +                               DESC_L_MASK); +        cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc, +                               0, 0xffffffff, +                               DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | +                               DESC_S_MASK | +                               DESC_W_MASK | DESC_A_MASK); +        if (code64) { +            env->eip = env->lstar; +        } else { +            env->eip = env->cstar; +        } +    } else { +        env->regs[R_ECX] = (uint32_t)(env->eip + next_eip_addend); + +        env->eflags &= ~(IF_MASK | RF_MASK | VM_MASK); +        cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc, +                           0, 0xffffffff, +                               DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | +                               DESC_S_MASK | +                               DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); +        cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc, +                               0, 0xffffffff, +                               DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | +                               DESC_S_MASK | +                               DESC_W_MASK | DESC_A_MASK); +        env->eip = (uint32_t)env->star; +    } +} +#endif +#endif + +#ifdef TARGET_X86_64 +void helper_sysret(CPUX86State *env, int dflag) +{ +    int cpl, selector; + +    if (!(env->efer & MSR_EFER_SCE)) { +        raise_exception_err(env, EXCP06_ILLOP, 0); +    } +    cpl = env->hflags & HF_CPL_MASK; +    if (!(env->cr[0] & CR0_PE_MASK) || cpl != 0) { +        raise_exception_err(env, EXCP0D_GPF, 0); +    } +    selector = (env->star >> 48) & 0xffff; +    if (env->hflags & HF_LMA_MASK) { +        cpu_load_eflags(env, (uint32_t)(env->regs[11]), TF_MASK | AC_MASK +                        | ID_MASK | IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | +                        NT_MASK); +        if (dflag == 2) { +            cpu_x86_load_seg_cache(env, R_CS, (selector + 16) | 3, +                                   0, 0xffffffff, +                                   DESC_G_MASK | DESC_P_MASK | +                                   DESC_S_MASK | (3 << DESC_DPL_SHIFT) | +                                   DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | +                                   DESC_L_MASK); +            env->eip = env->regs[R_ECX]; +        } else { +            cpu_x86_load_seg_cache(env, R_CS, selector | 3, +                                   0, 0xffffffff, +                                   DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | +                                   DESC_S_MASK | (3 << DESC_DPL_SHIFT) | +                                   DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); +            env->eip = (uint32_t)env->regs[R_ECX]; +        } +        cpu_x86_load_seg_cache(env, R_SS, (selector + 8) | 3, +                               0, 0xffffffff, +                               DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | +                               DESC_S_MASK | (3 << DESC_DPL_SHIFT) | +                               DESC_W_MASK | DESC_A_MASK); +    } else { +        env->eflags |= IF_MASK; +        cpu_x86_load_seg_cache(env, R_CS, selector | 3, +                               0, 0xffffffff, +                               DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | +                               DESC_S_MASK | (3 << DESC_DPL_SHIFT) | +                               DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); +        env->eip = (uint32_t)env->regs[R_ECX]; +        cpu_x86_load_seg_cache(env, R_SS, (selector + 8) | 3, +                               0, 0xffffffff, +                               DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | +                               DESC_S_MASK | (3 << DESC_DPL_SHIFT) | +                               DESC_W_MASK | DESC_A_MASK); +    } +} +#endif + +/* real mode interrupt */ +static void do_interrupt_real(CPUX86State *env, int intno, int is_int, +                              int error_code, unsigned int next_eip) +{ +    SegmentCache *dt; +    target_ulong ptr, ssp; +    int selector; +    uint32_t offset, esp; +    uint32_t old_cs, old_eip; + +    /* real mode (simpler!) */ +    dt = &env->idt; +    if (intno * 4 + 3 > dt->limit) { +        raise_exception_err(env, EXCP0D_GPF, intno * 8 + 2); +    } +    ptr = dt->base + intno * 4; +    offset = cpu_lduw_kernel(env, ptr); +    selector = cpu_lduw_kernel(env, ptr + 2); +    esp = env->regs[R_ESP]; +    ssp = env->segs[R_SS].base; +    if (is_int) { +        old_eip = next_eip; +    } else { +        old_eip = env->eip; +    } +    old_cs = env->segs[R_CS].selector; +    /* XXX: use SS segment size? */ +    PUSHW(ssp, esp, 0xffff, cpu_compute_eflags(env)); +    PUSHW(ssp, esp, 0xffff, old_cs); +    PUSHW(ssp, esp, 0xffff, old_eip); + +    /* update processor state */ +    env->regs[R_ESP] = (env->regs[R_ESP] & ~0xffff) | (esp & 0xffff); +    env->eip = offset; +    env->segs[R_CS].selector = selector; +    env->segs[R_CS].base = (selector << 4); +    env->eflags &= ~(IF_MASK | TF_MASK | AC_MASK | RF_MASK); +} + +#if defined(CONFIG_USER_ONLY) +/* fake user mode interrupt */ +static void do_interrupt_user(CPUX86State *env, int intno, int is_int, +                              int error_code, target_ulong next_eip) +{ +    SegmentCache *dt; +    target_ulong ptr; +    int dpl, cpl, shift; +    uint32_t e2; + +    dt = &env->idt; +    if (env->hflags & HF_LMA_MASK) { +        shift = 4; +    } else { +        shift = 3; +    } +    ptr = dt->base + (intno << shift); +    e2 = cpu_ldl_kernel(env, ptr + 4); + +    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 << shift) + 2); +    } + +    /* Since we emulate only user space, we cannot do more than +       exiting the emulation with the suitable exception and error +       code. So update EIP for INT 0x80 and EXCP_SYSCALL. */ +    if (is_int || intno == EXCP_SYSCALL) { +        env->eip = next_eip; +    } +} + +#else + +static void handle_even_inj(CPUX86State *env, int intno, int is_int, +                            int error_code, int is_hw, int rm) +{ +    CPUState *cs = CPU(x86_env_get_cpu(env)); +    uint32_t event_inj = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, +                                                          control.event_inj)); + +    if (!(event_inj & SVM_EVTINJ_VALID)) { +        int type; + +        if (is_int) { +            type = SVM_EVTINJ_TYPE_SOFT; +        } else { +            type = SVM_EVTINJ_TYPE_EXEPT; +        } +        event_inj = intno | type | SVM_EVTINJ_VALID; +        if (!rm && exception_has_error_code(intno)) { +            event_inj |= SVM_EVTINJ_VALID_ERR; +            x86_stl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, +                                             control.event_inj_err), +                     error_code); +        } +        x86_stl_phys(cs, +                 env->vm_vmcb + offsetof(struct vmcb, control.event_inj), +                 event_inj); +    } +} +#endif + +/* + * Begin execution of an interruption. is_int is TRUE if coming from + * the int instruction. next_eip is the env->eip value AFTER the interrupt + * instruction. It is only relevant if is_int is TRUE. + */ +static void do_interrupt_all(X86CPU *cpu, int intno, int is_int, +                             int error_code, target_ulong next_eip, int is_hw) +{ +    CPUX86State *env = &cpu->env; + +    if (qemu_loglevel_mask(CPU_LOG_INT)) { +        if ((env->cr[0] & CR0_PE_MASK)) { +            static int count; + +            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, +                     count, intno, error_code, is_int, +                     env->hflags & HF_CPL_MASK, +                     env->segs[R_CS].selector, env->eip, +                     (int)env->segs[R_CS].base + env->eip, +                     env->segs[R_SS].selector, env->regs[R_ESP]); +            if (intno == 0x0e) { +                qemu_log(" CR2=" TARGET_FMT_lx, env->cr[2]); +            } else { +                qemu_log(" env->regs[R_EAX]=" TARGET_FMT_lx, env->regs[R_EAX]); +            } +            qemu_log("\n"); +            log_cpu_state(CPU(cpu), CPU_DUMP_CCOP); +#if 0 +            { +                int i; +                target_ulong ptr; + +                qemu_log("       code="); +                ptr = env->segs[R_CS].base + env->eip; +                for (i = 0; i < 16; i++) { +                    qemu_log(" %02x", ldub(ptr + i)); +                } +                qemu_log("\n"); +            } +#endif +            count++; +        } +    } +    if (env->cr[0] & CR0_PE_MASK) { +#if !defined(CONFIG_USER_ONLY) +        if (env->hflags & HF_SVMI_MASK) { +            handle_even_inj(env, intno, is_int, error_code, is_hw, 0); +        } +#endif +#ifdef TARGET_X86_64 +        if (env->hflags & HF_LMA_MASK) { +            do_interrupt64(env, intno, is_int, error_code, next_eip, is_hw); +        } else +#endif +        { +            do_interrupt_protected(env, intno, is_int, error_code, next_eip, +                                   is_hw); +        } +    } else { +#if !defined(CONFIG_USER_ONLY) +        if (env->hflags & HF_SVMI_MASK) { +            handle_even_inj(env, intno, is_int, error_code, is_hw, 1); +        } +#endif +        do_interrupt_real(env, intno, is_int, error_code, next_eip); +    } + +#if !defined(CONFIG_USER_ONLY) +    if (env->hflags & HF_SVMI_MASK) { +        CPUState *cs = CPU(cpu); +        uint32_t event_inj = x86_ldl_phys(cs, env->vm_vmcb + +                                      offsetof(struct vmcb, +                                               control.event_inj)); + +        x86_stl_phys(cs, +                 env->vm_vmcb + offsetof(struct vmcb, control.event_inj), +                 event_inj & ~SVM_EVTINJ_VALID); +    } +#endif +} + +void x86_cpu_do_interrupt(CPUState *cs) +{ +    X86CPU *cpu = X86_CPU(cs); +    CPUX86State *env = &cpu->env; + +#if defined(CONFIG_USER_ONLY) +    /* if user mode only, we simulate a fake exception +       which will be handled outside the cpu execution +       loop */ +    do_interrupt_user(env, cs->exception_index, +                      env->exception_is_int, +                      env->error_code, +                      env->exception_next_eip); +    /* successfully delivered */ +    env->old_exception = -1; +#else +    /* simulate a real cpu exception. On i386, it can +       trigger new exceptions, but we do not handle +       double or triple faults yet. */ +    do_interrupt_all(cpu, cs->exception_index, +                     env->exception_is_int, +                     env->error_code, +                     env->exception_next_eip, 0); +    /* successfully delivered */ +    env->old_exception = -1; +#endif +} + +void do_interrupt_x86_hardirq(CPUX86State *env, int intno, int is_hw) +{ +    do_interrupt_all(x86_env_get_cpu(env), intno, 0, 0, 0, is_hw); +} + +bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +{ +    X86CPU *cpu = X86_CPU(cs); +    CPUX86State *env = &cpu->env; +    bool ret = false; + +#if !defined(CONFIG_USER_ONLY) +    if (interrupt_request & CPU_INTERRUPT_POLL) { +        cs->interrupt_request &= ~CPU_INTERRUPT_POLL; +        apic_poll_irq(cpu->apic_state); +    } +#endif +    if (interrupt_request & CPU_INTERRUPT_SIPI) { +        do_cpu_sipi(cpu); +    } else if (env->hflags2 & HF2_GIF_MASK) { +        if ((interrupt_request & CPU_INTERRUPT_SMI) && +            !(env->hflags & HF_SMM_MASK)) { +            cpu_svm_check_intercept_param(env, SVM_EXIT_SMI, 0); +            cs->interrupt_request &= ~CPU_INTERRUPT_SMI; +            do_smm_enter(cpu); +            ret = true; +        } else if ((interrupt_request & CPU_INTERRUPT_NMI) && +                   !(env->hflags2 & HF2_NMI_MASK)) { +            cs->interrupt_request &= ~CPU_INTERRUPT_NMI; +            env->hflags2 |= HF2_NMI_MASK; +            do_interrupt_x86_hardirq(env, EXCP02_NMI, 1); +            ret = true; +        } else if (interrupt_request & CPU_INTERRUPT_MCE) { +            cs->interrupt_request &= ~CPU_INTERRUPT_MCE; +            do_interrupt_x86_hardirq(env, EXCP12_MCHK, 0); +            ret = true; +        } else if ((interrupt_request & CPU_INTERRUPT_HARD) && +                   (((env->hflags2 & HF2_VINTR_MASK) && +                     (env->hflags2 & HF2_HIF_MASK)) || +                    (!(env->hflags2 & HF2_VINTR_MASK) && +                     (env->eflags & IF_MASK && +                      !(env->hflags & HF_INHIBIT_IRQ_MASK))))) { +            int intno; +            cpu_svm_check_intercept_param(env, SVM_EXIT_INTR, 0); +            cs->interrupt_request &= ~(CPU_INTERRUPT_HARD | +                                       CPU_INTERRUPT_VIRQ); +            intno = cpu_get_pic_interrupt(env); +            qemu_log_mask(CPU_LOG_TB_IN_ASM, +                          "Servicing hardware INT=0x%02x\n", intno); +            do_interrupt_x86_hardirq(env, intno, 1); +            /* ensure that no TB jump will be modified as +               the program flow was changed */ +            ret = true; +#if !defined(CONFIG_USER_ONLY) +        } else if ((interrupt_request & CPU_INTERRUPT_VIRQ) && +                   (env->eflags & IF_MASK) && +                   !(env->hflags & HF_INHIBIT_IRQ_MASK)) { +            int intno; +            /* FIXME: this should respect TPR */ +            cpu_svm_check_intercept_param(env, SVM_EXIT_VINTR, 0); +            intno = x86_ldl_phys(cs, env->vm_vmcb +                             + offsetof(struct vmcb, control.int_vector)); +            qemu_log_mask(CPU_LOG_TB_IN_ASM, +                          "Servicing virtual hardware INT=0x%02x\n", intno); +            do_interrupt_x86_hardirq(env, intno, 1); +            cs->interrupt_request &= ~CPU_INTERRUPT_VIRQ; +            ret = true; +#endif +        } +    } + +    return ret; +} + +void helper_enter_level(CPUX86State *env, int level, int data32, +                        target_ulong t1) +{ +    target_ulong ssp; +    uint32_t esp_mask, esp, ebp; + +    esp_mask = get_sp_mask(env->segs[R_SS].flags); +    ssp = env->segs[R_SS].base; +    ebp = env->regs[R_EBP]; +    esp = env->regs[R_ESP]; +    if (data32) { +        /* 32 bit */ +        esp -= 4; +        while (--level) { +            esp -= 4; +            ebp -= 4; +            cpu_stl_data(env, ssp + (esp & esp_mask), +                         cpu_ldl_data(env, ssp + (ebp & esp_mask))); +        } +        esp -= 4; +        cpu_stl_data(env, ssp + (esp & esp_mask), t1); +    } else { +        /* 16 bit */ +        esp -= 2; +        while (--level) { +            esp -= 2; +            ebp -= 2; +            cpu_stw_data(env, ssp + (esp & esp_mask), +                         cpu_lduw_data(env, ssp + (ebp & esp_mask))); +        } +        esp -= 2; +        cpu_stw_data(env, ssp + (esp & esp_mask), t1); +    } +} + +#ifdef TARGET_X86_64 +void helper_enter64_level(CPUX86State *env, int level, int data64, +                          target_ulong t1) +{ +    target_ulong esp, ebp; + +    ebp = env->regs[R_EBP]; +    esp = env->regs[R_ESP]; + +    if (data64) { +        /* 64 bit */ +        esp -= 8; +        while (--level) { +            esp -= 8; +            ebp -= 8; +            cpu_stq_data(env, esp, cpu_ldq_data(env, ebp)); +        } +        esp -= 8; +        cpu_stq_data(env, esp, t1); +    } else { +        /* 16 bit */ +        esp -= 2; +        while (--level) { +            esp -= 2; +            ebp -= 2; +            cpu_stw_data(env, esp, cpu_lduw_data(env, ebp)); +        } +        esp -= 2; +        cpu_stw_data(env, esp, t1); +    } +} +#endif + +void helper_lldt(CPUX86State *env, int selector) +{ +    SegmentCache *dt; +    uint32_t e1, e2; +    int index, entry_limit; +    target_ulong ptr; + +    selector &= 0xffff; +    if ((selector & 0xfffc) == 0) { +        /* XXX: NULL selector case: invalid LDT */ +        env->ldt.base = 0; +        env->ldt.limit = 0; +    } else { +        if (selector & 0x4) { +            raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); +        } +        dt = &env->gdt; +        index = selector & ~7; +#ifdef TARGET_X86_64 +        if (env->hflags & HF_LMA_MASK) { +            entry_limit = 15; +        } else +#endif +        { +            entry_limit = 7; +        } +        if ((index + entry_limit) > dt->limit) { +            raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); +        } +        ptr = dt->base + index; +        e1 = cpu_ldl_kernel(env, ptr); +        e2 = cpu_ldl_kernel(env, ptr + 4); +        if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2) { +            raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); +        } +        if (!(e2 & DESC_P_MASK)) { +            raise_exception_err(env, EXCP0B_NOSEG, selector & 0xfffc); +        } +#ifdef TARGET_X86_64 +        if (env->hflags & HF_LMA_MASK) { +            uint32_t e3; + +            e3 = cpu_ldl_kernel(env, ptr + 8); +            load_seg_cache_raw_dt(&env->ldt, e1, e2); +            env->ldt.base |= (target_ulong)e3 << 32; +        } else +#endif +        { +            load_seg_cache_raw_dt(&env->ldt, e1, e2); +        } +    } +    env->ldt.selector = selector; +} + +void helper_ltr(CPUX86State *env, int selector) +{ +    SegmentCache *dt; +    uint32_t e1, e2; +    int index, type, entry_limit; +    target_ulong ptr; + +    selector &= 0xffff; +    if ((selector & 0xfffc) == 0) { +        /* NULL selector case: invalid TR */ +        env->tr.base = 0; +        env->tr.limit = 0; +        env->tr.flags = 0; +    } else { +        if (selector & 0x4) { +            raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); +        } +        dt = &env->gdt; +        index = selector & ~7; +#ifdef TARGET_X86_64 +        if (env->hflags & HF_LMA_MASK) { +            entry_limit = 15; +        } else +#endif +        { +            entry_limit = 7; +        } +        if ((index + entry_limit) > dt->limit) { +            raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); +        } +        ptr = dt->base + index; +        e1 = cpu_ldl_kernel(env, ptr); +        e2 = cpu_ldl_kernel(env, ptr + 4); +        type = (e2 >> DESC_TYPE_SHIFT) & 0xf; +        if ((e2 & DESC_S_MASK) || +            (type != 1 && type != 9)) { +            raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); +        } +        if (!(e2 & DESC_P_MASK)) { +            raise_exception_err(env, EXCP0B_NOSEG, selector & 0xfffc); +        } +#ifdef TARGET_X86_64 +        if (env->hflags & HF_LMA_MASK) { +            uint32_t e3, e4; + +            e3 = cpu_ldl_kernel(env, ptr + 8); +            e4 = cpu_ldl_kernel(env, ptr + 12); +            if ((e4 >> DESC_TYPE_SHIFT) & 0xf) { +                raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); +            } +            load_seg_cache_raw_dt(&env->tr, e1, e2); +            env->tr.base |= (target_ulong)e3 << 32; +        } else +#endif +        { +            load_seg_cache_raw_dt(&env->tr, e1, e2); +        } +        e2 |= DESC_TSS_BUSY_MASK; +        cpu_stl_kernel(env, ptr + 4, e2); +    } +    env->tr.selector = selector; +} + +/* only works if protected mode and not VM86. seg_reg must be != R_CS */ +void helper_load_seg(CPUX86State *env, int seg_reg, int selector) +{ +    uint32_t e1, e2; +    int cpl, dpl, rpl; +    SegmentCache *dt; +    int index; +    target_ulong ptr; + +    selector &= 0xffff; +    cpl = env->hflags & HF_CPL_MASK; +    if ((selector & 0xfffc) == 0) { +        /* null selector case */ +        if (seg_reg == R_SS +#ifdef TARGET_X86_64 +            && (!(env->hflags & HF_CS64_MASK) || cpl == 3) +#endif +            ) { +            raise_exception_err(env, EXCP0D_GPF, 0); +        } +        cpu_x86_load_seg_cache(env, seg_reg, selector, 0, 0, 0); +    } else { + +        if (selector & 0x4) { +            dt = &env->ldt; +        } else { +            dt = &env->gdt; +        } +        index = selector & ~7; +        if ((index + 7) > dt->limit) { +            raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); +        } +        ptr = dt->base + index; +        e1 = cpu_ldl_kernel(env, ptr); +        e2 = cpu_ldl_kernel(env, ptr + 4); + +        if (!(e2 & DESC_S_MASK)) { +            raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); +        } +        rpl = selector & 3; +        dpl = (e2 >> DESC_DPL_SHIFT) & 3; +        if (seg_reg == R_SS) { +            /* must be writable segment */ +            if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK)) { +                raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); +            } +            if (rpl != cpl || dpl != cpl) { +                raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); +            } +        } else { +            /* must be readable segment */ +            if ((e2 & (DESC_CS_MASK | DESC_R_MASK)) == DESC_CS_MASK) { +                raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); +            } + +            if (!(e2 & DESC_CS_MASK) || !(e2 & DESC_C_MASK)) { +                /* if not conforming code, test rights */ +                if (dpl < cpl || dpl < rpl) { +                    raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); +                } +            } +        } + +        if (!(e2 & DESC_P_MASK)) { +            if (seg_reg == R_SS) { +                raise_exception_err(env, EXCP0C_STACK, selector & 0xfffc); +            } else { +                raise_exception_err(env, EXCP0B_NOSEG, selector & 0xfffc); +            } +        } + +        /* set the access bit if not already set */ +        if (!(e2 & DESC_A_MASK)) { +            e2 |= DESC_A_MASK; +            cpu_stl_kernel(env, ptr + 4, e2); +        } + +        cpu_x86_load_seg_cache(env, seg_reg, selector, +                       get_seg_base(e1, e2), +                       get_seg_limit(e1, e2), +                       e2); +#if 0 +        qemu_log("load_seg: sel=0x%04x base=0x%08lx limit=0x%08lx flags=%08x\n", +                selector, (unsigned long)sc->base, sc->limit, sc->flags); +#endif +    } +} + +/* protected mode jump */ +void helper_ljmp_protected(CPUX86State *env, int new_cs, target_ulong new_eip, +                           int next_eip_addend) +{ +    int gate_cs, type; +    uint32_t e1, e2, cpl, dpl, rpl, limit; +    target_ulong next_eip; + +    if ((new_cs & 0xfffc) == 0) { +        raise_exception_err(env, EXCP0D_GPF, 0); +    } +    if (load_segment(env, &e1, &e2, new_cs) != 0) { +        raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); +    } +    cpl = env->hflags & HF_CPL_MASK; +    if (e2 & DESC_S_MASK) { +        if (!(e2 & DESC_CS_MASK)) { +            raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); +        } +        dpl = (e2 >> DESC_DPL_SHIFT) & 3; +        if (e2 & DESC_C_MASK) { +            /* conforming code segment */ +            if (dpl > cpl) { +                raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); +            } +        } else { +            /* non conforming code segment */ +            rpl = new_cs & 3; +            if (rpl > cpl) { +                raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); +            } +            if (dpl != cpl) { +                raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); +            } +        } +        if (!(e2 & DESC_P_MASK)) { +            raise_exception_err(env, EXCP0B_NOSEG, new_cs & 0xfffc); +        } +        limit = get_seg_limit(e1, e2); +        if (new_eip > limit && +            !(env->hflags & HF_LMA_MASK) && !(e2 & DESC_L_MASK)) { +            raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); +        } +        cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl, +                       get_seg_base(e1, e2), limit, e2); +        env->eip = new_eip; +    } else { +        /* jump to call or task gate */ +        dpl = (e2 >> DESC_DPL_SHIFT) & 3; +        rpl = new_cs & 3; +        cpl = env->hflags & HF_CPL_MASK; +        type = (e2 >> DESC_TYPE_SHIFT) & 0xf; +        switch (type) { +        case 1: /* 286 TSS */ +        case 9: /* 386 TSS */ +        case 5: /* task gate */ +            if (dpl < cpl || dpl < rpl) { +                raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); +            } +            next_eip = env->eip + next_eip_addend; +            switch_tss(env, new_cs, e1, e2, SWITCH_TSS_JMP, next_eip); +            break; +        case 4: /* 286 call gate */ +        case 12: /* 386 call gate */ +            if ((dpl < cpl) || (dpl < rpl)) { +                raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); +            } +            if (!(e2 & DESC_P_MASK)) { +                raise_exception_err(env, EXCP0B_NOSEG, new_cs & 0xfffc); +            } +            gate_cs = e1 >> 16; +            new_eip = (e1 & 0xffff); +            if (type == 12) { +                new_eip |= (e2 & 0xffff0000); +            } +            if (load_segment(env, &e1, &e2, gate_cs) != 0) { +                raise_exception_err(env, EXCP0D_GPF, gate_cs & 0xfffc); +            } +            dpl = (e2 >> DESC_DPL_SHIFT) & 3; +            /* must be code segment */ +            if (((e2 & (DESC_S_MASK | DESC_CS_MASK)) != +                 (DESC_S_MASK | DESC_CS_MASK))) { +                raise_exception_err(env, EXCP0D_GPF, gate_cs & 0xfffc); +            } +            if (((e2 & DESC_C_MASK) && (dpl > cpl)) || +                (!(e2 & DESC_C_MASK) && (dpl != cpl))) { +                raise_exception_err(env, EXCP0D_GPF, gate_cs & 0xfffc); +            } +            if (!(e2 & DESC_P_MASK)) { +                raise_exception_err(env, EXCP0D_GPF, gate_cs & 0xfffc); +            } +            limit = get_seg_limit(e1, e2); +            if (new_eip > limit) { +                raise_exception_err(env, EXCP0D_GPF, 0); +            } +            cpu_x86_load_seg_cache(env, R_CS, (gate_cs & 0xfffc) | cpl, +                                   get_seg_base(e1, e2), limit, e2); +            env->eip = new_eip; +            break; +        default: +            raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); +            break; +        } +    } +} + +/* real mode call */ +void helper_lcall_real(CPUX86State *env, int new_cs, target_ulong new_eip1, +                       int shift, int next_eip) +{ +    int new_eip; +    uint32_t esp, esp_mask; +    target_ulong ssp; + +    new_eip = new_eip1; +    esp = env->regs[R_ESP]; +    esp_mask = get_sp_mask(env->segs[R_SS].flags); +    ssp = env->segs[R_SS].base; +    if (shift) { +        PUSHL(ssp, esp, esp_mask, env->segs[R_CS].selector); +        PUSHL(ssp, esp, esp_mask, next_eip); +    } else { +        PUSHW(ssp, esp, esp_mask, env->segs[R_CS].selector); +        PUSHW(ssp, esp, esp_mask, next_eip); +    } + +    SET_ESP(esp, esp_mask); +    env->eip = new_eip; +    env->segs[R_CS].selector = new_cs; +    env->segs[R_CS].base = (new_cs << 4); +} + +/* protected mode call */ +void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, +                            int shift, int next_eip_addend) +{ +    int new_stack, i; +    uint32_t e1, e2, cpl, dpl, rpl, selector, offset, param_count; +    uint32_t ss = 0, ss_e1 = 0, ss_e2 = 0, sp, type, ss_dpl, sp_mask; +    uint32_t val, limit, old_sp_mask; +    target_ulong ssp, old_ssp, next_eip; + +    next_eip = env->eip + next_eip_addend; +    LOG_PCALL("lcall %04x:%08x s=%d\n", new_cs, (uint32_t)new_eip, shift); +    LOG_PCALL_STATE(CPU(x86_env_get_cpu(env))); +    if ((new_cs & 0xfffc) == 0) { +        raise_exception_err(env, EXCP0D_GPF, 0); +    } +    if (load_segment(env, &e1, &e2, new_cs) != 0) { +        raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); +    } +    cpl = env->hflags & HF_CPL_MASK; +    LOG_PCALL("desc=%08x:%08x\n", e1, e2); +    if (e2 & DESC_S_MASK) { +        if (!(e2 & DESC_CS_MASK)) { +            raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); +        } +        dpl = (e2 >> DESC_DPL_SHIFT) & 3; +        if (e2 & DESC_C_MASK) { +            /* conforming code segment */ +            if (dpl > cpl) { +                raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); +            } +        } else { +            /* non conforming code segment */ +            rpl = new_cs & 3; +            if (rpl > cpl) { +                raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); +            } +            if (dpl != cpl) { +                raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); +            } +        } +        if (!(e2 & DESC_P_MASK)) { +            raise_exception_err(env, EXCP0B_NOSEG, new_cs & 0xfffc); +        } + +#ifdef TARGET_X86_64 +        /* XXX: check 16/32 bit cases in long mode */ +        if (shift == 2) { +            target_ulong rsp; + +            /* 64 bit case */ +            rsp = env->regs[R_ESP]; +            PUSHQ(rsp, env->segs[R_CS].selector); +            PUSHQ(rsp, next_eip); +            /* from this point, not restartable */ +            env->regs[R_ESP] = rsp; +            cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl, +                                   get_seg_base(e1, e2), +                                   get_seg_limit(e1, e2), e2); +            env->eip = new_eip; +        } else +#endif +        { +            sp = env->regs[R_ESP]; +            sp_mask = get_sp_mask(env->segs[R_SS].flags); +            ssp = env->segs[R_SS].base; +            if (shift) { +                PUSHL(ssp, sp, sp_mask, env->segs[R_CS].selector); +                PUSHL(ssp, sp, sp_mask, next_eip); +            } else { +                PUSHW(ssp, sp, sp_mask, env->segs[R_CS].selector); +                PUSHW(ssp, sp, sp_mask, next_eip); +            } + +            limit = get_seg_limit(e1, e2); +            if (new_eip > limit) { +                raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); +            } +            /* from this point, not restartable */ +            SET_ESP(sp, sp_mask); +            cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl, +                                   get_seg_base(e1, e2), limit, e2); +            env->eip = new_eip; +        } +    } else { +        /* check gate type */ +        type = (e2 >> DESC_TYPE_SHIFT) & 0x1f; +        dpl = (e2 >> DESC_DPL_SHIFT) & 3; +        rpl = new_cs & 3; +        switch (type) { +        case 1: /* available 286 TSS */ +        case 9: /* available 386 TSS */ +        case 5: /* task gate */ +            if (dpl < cpl || dpl < rpl) { +                raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); +            } +            switch_tss(env, new_cs, e1, e2, SWITCH_TSS_CALL, next_eip); +            return; +        case 4: /* 286 call gate */ +        case 12: /* 386 call gate */ +            break; +        default: +            raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); +            break; +        } +        shift = type >> 3; + +        if (dpl < cpl || dpl < rpl) { +            raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); +        } +        /* check valid bit */ +        if (!(e2 & DESC_P_MASK)) { +            raise_exception_err(env, EXCP0B_NOSEG,  new_cs & 0xfffc); +        } +        selector = e1 >> 16; +        offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff); +        param_count = e2 & 0x1f; +        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_C_MASK) && dpl < cpl) { +            /* to inner privilege */ +            get_ss_esp_from_tss(env, &ss, &sp, dpl); +            LOG_PCALL("new ss:esp=%04x:%08x param_count=%d env->regs[R_ESP]=" +                      TARGET_FMT_lx "\n", ss, sp, param_count, +                      env->regs[R_ESP]); +            if ((ss & 0xfffc) == 0) { +                raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); +            } +            if ((ss & 3) != dpl) { +                raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); +            } +            if (load_segment(env, &ss_e1, &ss_e2, ss) != 0) { +                raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); +            } +            ss_dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3; +            if (ss_dpl != dpl) { +                raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); +            } +            if (!(ss_e2 & DESC_S_MASK) || +                (ss_e2 & DESC_CS_MASK) || +                !(ss_e2 & DESC_W_MASK)) { +                raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); +            } +            if (!(ss_e2 & DESC_P_MASK)) { +                raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); +            } + +            /* push_size = ((param_count * 2) + 8) << shift; */ + +            old_sp_mask = get_sp_mask(env->segs[R_SS].flags); +            old_ssp = env->segs[R_SS].base; + +            sp_mask = get_sp_mask(ss_e2); +            ssp = get_seg_base(ss_e1, ss_e2); +            if (shift) { +                PUSHL(ssp, sp, sp_mask, env->segs[R_SS].selector); +                PUSHL(ssp, sp, sp_mask, env->regs[R_ESP]); +                for (i = param_count - 1; i >= 0; i--) { +                    val = cpu_ldl_kernel(env, old_ssp + +                                         ((env->regs[R_ESP] + i * 4) & +                                          old_sp_mask)); +                    PUSHL(ssp, sp, sp_mask, val); +                } +            } else { +                PUSHW(ssp, sp, sp_mask, env->segs[R_SS].selector); +                PUSHW(ssp, sp, sp_mask, env->regs[R_ESP]); +                for (i = param_count - 1; i >= 0; i--) { +                    val = cpu_lduw_kernel(env, old_ssp + +                                          ((env->regs[R_ESP] + i * 2) & +                                           old_sp_mask)); +                    PUSHW(ssp, sp, sp_mask, val); +                } +            } +            new_stack = 1; +        } else { +            /* to same privilege */ +            sp = env->regs[R_ESP]; +            sp_mask = get_sp_mask(env->segs[R_SS].flags); +            ssp = env->segs[R_SS].base; +            /* push_size = (4 << shift); */ +            new_stack = 0; +        } + +        if (shift) { +            PUSHL(ssp, sp, sp_mask, env->segs[R_CS].selector); +            PUSHL(ssp, sp, sp_mask, next_eip); +        } else { +            PUSHW(ssp, sp, sp_mask, env->segs[R_CS].selector); +            PUSHW(ssp, sp, sp_mask, next_eip); +        } + +        /* from this point, not restartable */ + +        if (new_stack) { +            ss = (ss & ~3) | dpl; +            cpu_x86_load_seg_cache(env, R_SS, ss, +                                   ssp, +                                   get_seg_limit(ss_e1, ss_e2), +                                   ss_e2); +        } + +        selector = (selector & ~3) | dpl; +        cpu_x86_load_seg_cache(env, R_CS, selector, +                       get_seg_base(e1, e2), +                       get_seg_limit(e1, e2), +                       e2); +        SET_ESP(sp, sp_mask); +        env->eip = offset; +    } +} + +/* real and vm86 mode iret */ +void helper_iret_real(CPUX86State *env, int shift) +{ +    uint32_t sp, new_cs, new_eip, new_eflags, sp_mask; +    target_ulong ssp; +    int eflags_mask; + +    sp_mask = 0xffff; /* XXXX: use SS segment size? */ +    sp = env->regs[R_ESP]; +    ssp = env->segs[R_SS].base; +    if (shift == 1) { +        /* 32 bits */ +        POPL(ssp, sp, sp_mask, new_eip); +        POPL(ssp, sp, sp_mask, new_cs); +        new_cs &= 0xffff; +        POPL(ssp, sp, sp_mask, new_eflags); +    } else { +        /* 16 bits */ +        POPW(ssp, sp, sp_mask, new_eip); +        POPW(ssp, sp, sp_mask, new_cs); +        POPW(ssp, sp, sp_mask, new_eflags); +    } +    env->regs[R_ESP] = (env->regs[R_ESP] & ~sp_mask) | (sp & sp_mask); +    env->segs[R_CS].selector = new_cs; +    env->segs[R_CS].base = (new_cs << 4); +    env->eip = new_eip; +    if (env->eflags & VM_MASK) { +        eflags_mask = TF_MASK | AC_MASK | ID_MASK | IF_MASK | RF_MASK | +            NT_MASK; +    } else { +        eflags_mask = TF_MASK | AC_MASK | ID_MASK | IF_MASK | IOPL_MASK | +            RF_MASK | NT_MASK; +    } +    if (shift == 0) { +        eflags_mask &= 0xffff; +    } +    cpu_load_eflags(env, new_eflags, eflags_mask); +    env->hflags2 &= ~HF2_NMI_MASK; +} + +static inline void validate_seg(CPUX86State *env, int seg_reg, int cpl) +{ +    int dpl; +    uint32_t e2; + +    /* XXX: on x86_64, we do not want to nullify FS and GS because +       they may still contain a valid base. I would be interested to +       know how a real x86_64 CPU behaves */ +    if ((seg_reg == R_FS || seg_reg == R_GS) && +        (env->segs[seg_reg].selector & 0xfffc) == 0) { +        return; +    } + +    e2 = env->segs[seg_reg].flags; +    dpl = (e2 >> DESC_DPL_SHIFT) & 3; +    if (!(e2 & DESC_CS_MASK) || !(e2 & DESC_C_MASK)) { +        /* data or non conforming code segment */ +        if (dpl < cpl) { +            cpu_x86_load_seg_cache(env, seg_reg, 0, 0, 0, 0); +        } +    } +} + +/* protected mode iret */ +static inline void helper_ret_protected(CPUX86State *env, int shift, +                                        int is_iret, int addend) +{ +    uint32_t new_cs, new_eflags, new_ss; +    uint32_t new_es, new_ds, new_fs, new_gs; +    uint32_t e1, e2, ss_e1, ss_e2; +    int cpl, dpl, rpl, eflags_mask, iopl; +    target_ulong ssp, sp, new_eip, new_esp, sp_mask; + +#ifdef TARGET_X86_64 +    if (shift == 2) { +        sp_mask = -1; +    } else +#endif +    { +        sp_mask = get_sp_mask(env->segs[R_SS].flags); +    } +    sp = env->regs[R_ESP]; +    ssp = env->segs[R_SS].base; +    new_eflags = 0; /* avoid warning */ +#ifdef TARGET_X86_64 +    if (shift == 2) { +        POPQ(sp, new_eip); +        POPQ(sp, new_cs); +        new_cs &= 0xffff; +        if (is_iret) { +            POPQ(sp, new_eflags); +        } +    } else +#endif +    { +        if (shift == 1) { +            /* 32 bits */ +            POPL(ssp, sp, sp_mask, new_eip); +            POPL(ssp, sp, sp_mask, new_cs); +            new_cs &= 0xffff; +            if (is_iret) { +                POPL(ssp, sp, sp_mask, new_eflags); +                if (new_eflags & VM_MASK) { +                    goto return_to_vm86; +                } +            } +        } else { +            /* 16 bits */ +            POPW(ssp, sp, sp_mask, new_eip); +            POPW(ssp, sp, sp_mask, new_cs); +            if (is_iret) { +                POPW(ssp, sp, sp_mask, new_eflags); +            } +        } +    } +    LOG_PCALL("lret new %04x:" TARGET_FMT_lx " s=%d addend=0x%x\n", +              new_cs, new_eip, shift, addend); +    LOG_PCALL_STATE(CPU(x86_env_get_cpu(env))); +    if ((new_cs & 0xfffc) == 0) { +        raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); +    } +    if (load_segment(env, &e1, &e2, new_cs) != 0) { +        raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); +    } +    if (!(e2 & DESC_S_MASK) || +        !(e2 & DESC_CS_MASK)) { +        raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); +    } +    cpl = env->hflags & HF_CPL_MASK; +    rpl = new_cs & 3; +    if (rpl < cpl) { +        raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); +    } +    dpl = (e2 >> DESC_DPL_SHIFT) & 3; +    if (e2 & DESC_C_MASK) { +        if (dpl > rpl) { +            raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); +        } +    } else { +        if (dpl != rpl) { +            raise_exception_err(env, EXCP0D_GPF, new_cs & 0xfffc); +        } +    } +    if (!(e2 & DESC_P_MASK)) { +        raise_exception_err(env, EXCP0B_NOSEG, new_cs & 0xfffc); +    } + +    sp += addend; +    if (rpl == cpl && (!(env->hflags & HF_CS64_MASK) || +                       ((env->hflags & HF_CS64_MASK) && !is_iret))) { +        /* return to same privilege level */ +        cpu_x86_load_seg_cache(env, R_CS, new_cs, +                       get_seg_base(e1, e2), +                       get_seg_limit(e1, e2), +                       e2); +    } else { +        /* return to different privilege level */ +#ifdef TARGET_X86_64 +        if (shift == 2) { +            POPQ(sp, new_esp); +            POPQ(sp, new_ss); +            new_ss &= 0xffff; +        } else +#endif +        { +            if (shift == 1) { +                /* 32 bits */ +                POPL(ssp, sp, sp_mask, new_esp); +                POPL(ssp, sp, sp_mask, new_ss); +                new_ss &= 0xffff; +            } else { +                /* 16 bits */ +                POPW(ssp, sp, sp_mask, new_esp); +                POPW(ssp, sp, sp_mask, new_ss); +            } +        } +        LOG_PCALL("new ss:esp=%04x:" TARGET_FMT_lx "\n", +                  new_ss, new_esp); +        if ((new_ss & 0xfffc) == 0) { +#ifdef TARGET_X86_64 +            /* NULL ss is allowed in long mode if cpl != 3 */ +            /* XXX: test CS64? */ +            if ((env->hflags & HF_LMA_MASK) && rpl != 3) { +                cpu_x86_load_seg_cache(env, R_SS, new_ss, +                                       0, 0xffffffff, +                                       DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | +                                       DESC_S_MASK | (rpl << DESC_DPL_SHIFT) | +                                       DESC_W_MASK | DESC_A_MASK); +                ss_e2 = DESC_B_MASK; /* XXX: should not be needed? */ +            } else +#endif +            { +                raise_exception_err(env, EXCP0D_GPF, 0); +            } +        } else { +            if ((new_ss & 3) != rpl) { +                raise_exception_err(env, EXCP0D_GPF, new_ss & 0xfffc); +            } +            if (load_segment(env, &ss_e1, &ss_e2, new_ss) != 0) { +                raise_exception_err(env, EXCP0D_GPF, new_ss & 0xfffc); +            } +            if (!(ss_e2 & DESC_S_MASK) || +                (ss_e2 & DESC_CS_MASK) || +                !(ss_e2 & DESC_W_MASK)) { +                raise_exception_err(env, EXCP0D_GPF, new_ss & 0xfffc); +            } +            dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3; +            if (dpl != rpl) { +                raise_exception_err(env, EXCP0D_GPF, new_ss & 0xfffc); +            } +            if (!(ss_e2 & DESC_P_MASK)) { +                raise_exception_err(env, EXCP0B_NOSEG, new_ss & 0xfffc); +            } +            cpu_x86_load_seg_cache(env, R_SS, new_ss, +                                   get_seg_base(ss_e1, ss_e2), +                                   get_seg_limit(ss_e1, ss_e2), +                                   ss_e2); +        } + +        cpu_x86_load_seg_cache(env, R_CS, new_cs, +                       get_seg_base(e1, e2), +                       get_seg_limit(e1, e2), +                       e2); +        sp = new_esp; +#ifdef TARGET_X86_64 +        if (env->hflags & HF_CS64_MASK) { +            sp_mask = -1; +        } else +#endif +        { +            sp_mask = get_sp_mask(ss_e2); +        } + +        /* validate data segments */ +        validate_seg(env, R_ES, rpl); +        validate_seg(env, R_DS, rpl); +        validate_seg(env, R_FS, rpl); +        validate_seg(env, R_GS, rpl); + +        sp += addend; +    } +    SET_ESP(sp, sp_mask); +    env->eip = new_eip; +    if (is_iret) { +        /* NOTE: 'cpl' is the _old_ CPL */ +        eflags_mask = TF_MASK | AC_MASK | ID_MASK | RF_MASK | NT_MASK; +        if (cpl == 0) { +            eflags_mask |= IOPL_MASK; +        } +        iopl = (env->eflags >> IOPL_SHIFT) & 3; +        if (cpl <= iopl) { +            eflags_mask |= IF_MASK; +        } +        if (shift == 0) { +            eflags_mask &= 0xffff; +        } +        cpu_load_eflags(env, new_eflags, eflags_mask); +    } +    return; + + return_to_vm86: +    POPL(ssp, sp, sp_mask, new_esp); +    POPL(ssp, sp, sp_mask, new_ss); +    POPL(ssp, sp, sp_mask, new_es); +    POPL(ssp, sp, sp_mask, new_ds); +    POPL(ssp, sp, sp_mask, new_fs); +    POPL(ssp, sp, sp_mask, new_gs); + +    /* modify processor state */ +    cpu_load_eflags(env, new_eflags, TF_MASK | AC_MASK | ID_MASK | +                    IF_MASK | IOPL_MASK | VM_MASK | NT_MASK | VIF_MASK | +                    VIP_MASK); +    load_seg_vm(env, R_CS, new_cs & 0xffff); +    load_seg_vm(env, R_SS, new_ss & 0xffff); +    load_seg_vm(env, R_ES, new_es & 0xffff); +    load_seg_vm(env, R_DS, new_ds & 0xffff); +    load_seg_vm(env, R_FS, new_fs & 0xffff); +    load_seg_vm(env, R_GS, new_gs & 0xffff); + +    env->eip = new_eip & 0xffff; +    env->regs[R_ESP] = new_esp; +} + +void helper_iret_protected(CPUX86State *env, int shift, int next_eip) +{ +    int tss_selector, type; +    uint32_t e1, e2; + +    /* specific case for TSS */ +    if (env->eflags & NT_MASK) { +#ifdef TARGET_X86_64 +        if (env->hflags & HF_LMA_MASK) { +            raise_exception_err(env, EXCP0D_GPF, 0); +        } +#endif +        tss_selector = cpu_lduw_kernel(env, env->tr.base + 0); +        if (tss_selector & 4) { +            raise_exception_err(env, EXCP0A_TSS, tss_selector & 0xfffc); +        } +        if (load_segment(env, &e1, &e2, tss_selector) != 0) { +            raise_exception_err(env, EXCP0A_TSS, tss_selector & 0xfffc); +        } +        type = (e2 >> DESC_TYPE_SHIFT) & 0x17; +        /* NOTE: we check both segment and busy TSS */ +        if (type != 3) { +            raise_exception_err(env, EXCP0A_TSS, tss_selector & 0xfffc); +        } +        switch_tss(env, tss_selector, e1, e2, SWITCH_TSS_IRET, next_eip); +    } else { +        helper_ret_protected(env, shift, 1, 0); +    } +    env->hflags2 &= ~HF2_NMI_MASK; +} + +void helper_lret_protected(CPUX86State *env, int shift, int addend) +{ +    helper_ret_protected(env, shift, 0, addend); +} + +void helper_sysenter(CPUX86State *env) +{ +    if (env->sysenter_cs == 0) { +        raise_exception_err(env, EXCP0D_GPF, 0); +    } +    env->eflags &= ~(VM_MASK | IF_MASK | RF_MASK); + +#ifdef TARGET_X86_64 +    if (env->hflags & HF_LMA_MASK) { +        cpu_x86_load_seg_cache(env, R_CS, env->sysenter_cs & 0xfffc, +                               0, 0xffffffff, +                               DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | +                               DESC_S_MASK | +                               DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | +                               DESC_L_MASK); +    } else +#endif +    { +        cpu_x86_load_seg_cache(env, R_CS, env->sysenter_cs & 0xfffc, +                               0, 0xffffffff, +                               DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | +                               DESC_S_MASK | +                               DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); +    } +    cpu_x86_load_seg_cache(env, R_SS, (env->sysenter_cs + 8) & 0xfffc, +                           0, 0xffffffff, +                           DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | +                           DESC_S_MASK | +                           DESC_W_MASK | DESC_A_MASK); +    env->regs[R_ESP] = env->sysenter_esp; +    env->eip = env->sysenter_eip; +} + +void helper_sysexit(CPUX86State *env, int dflag) +{ +    int cpl; + +    cpl = env->hflags & HF_CPL_MASK; +    if (env->sysenter_cs == 0 || cpl != 0) { +        raise_exception_err(env, EXCP0D_GPF, 0); +    } +#ifdef TARGET_X86_64 +    if (dflag == 2) { +        cpu_x86_load_seg_cache(env, R_CS, ((env->sysenter_cs + 32) & 0xfffc) | +                               3, 0, 0xffffffff, +                               DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | +                               DESC_S_MASK | (3 << DESC_DPL_SHIFT) | +                               DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | +                               DESC_L_MASK); +        cpu_x86_load_seg_cache(env, R_SS, ((env->sysenter_cs + 40) & 0xfffc) | +                               3, 0, 0xffffffff, +                               DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | +                               DESC_S_MASK | (3 << DESC_DPL_SHIFT) | +                               DESC_W_MASK | DESC_A_MASK); +    } else +#endif +    { +        cpu_x86_load_seg_cache(env, R_CS, ((env->sysenter_cs + 16) & 0xfffc) | +                               3, 0, 0xffffffff, +                               DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | +                               DESC_S_MASK | (3 << DESC_DPL_SHIFT) | +                               DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); +        cpu_x86_load_seg_cache(env, R_SS, ((env->sysenter_cs + 24) & 0xfffc) | +                               3, 0, 0xffffffff, +                               DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | +                               DESC_S_MASK | (3 << DESC_DPL_SHIFT) | +                               DESC_W_MASK | DESC_A_MASK); +    } +    env->regs[R_ESP] = env->regs[R_ECX]; +    env->eip = env->regs[R_EDX]; +} + +target_ulong helper_lsl(CPUX86State *env, target_ulong selector1) +{ +    unsigned int limit; +    uint32_t e1, e2, eflags, selector; +    int rpl, dpl, cpl, type; + +    selector = selector1 & 0xffff; +    eflags = cpu_cc_compute_all(env, CC_OP); +    if ((selector & 0xfffc) == 0) { +        goto fail; +    } +    if (load_segment(env, &e1, &e2, selector) != 0) { +        goto fail; +    } +    rpl = selector & 3; +    dpl = (e2 >> DESC_DPL_SHIFT) & 3; +    cpl = env->hflags & HF_CPL_MASK; +    if (e2 & DESC_S_MASK) { +        if ((e2 & DESC_CS_MASK) && (e2 & DESC_C_MASK)) { +            /* conforming */ +        } else { +            if (dpl < cpl || dpl < rpl) { +                goto fail; +            } +        } +    } else { +        type = (e2 >> DESC_TYPE_SHIFT) & 0xf; +        switch (type) { +        case 1: +        case 2: +        case 3: +        case 9: +        case 11: +            break; +        default: +            goto fail; +        } +        if (dpl < cpl || dpl < rpl) { +        fail: +            CC_SRC = eflags & ~CC_Z; +            return 0; +        } +    } +    limit = get_seg_limit(e1, e2); +    CC_SRC = eflags | CC_Z; +    return limit; +} + +target_ulong helper_lar(CPUX86State *env, target_ulong selector1) +{ +    uint32_t e1, e2, eflags, selector; +    int rpl, dpl, cpl, type; + +    selector = selector1 & 0xffff; +    eflags = cpu_cc_compute_all(env, CC_OP); +    if ((selector & 0xfffc) == 0) { +        goto fail; +    } +    if (load_segment(env, &e1, &e2, selector) != 0) { +        goto fail; +    } +    rpl = selector & 3; +    dpl = (e2 >> DESC_DPL_SHIFT) & 3; +    cpl = env->hflags & HF_CPL_MASK; +    if (e2 & DESC_S_MASK) { +        if ((e2 & DESC_CS_MASK) && (e2 & DESC_C_MASK)) { +            /* conforming */ +        } else { +            if (dpl < cpl || dpl < rpl) { +                goto fail; +            } +        } +    } else { +        type = (e2 >> DESC_TYPE_SHIFT) & 0xf; +        switch (type) { +        case 1: +        case 2: +        case 3: +        case 4: +        case 5: +        case 9: +        case 11: +        case 12: +            break; +        default: +            goto fail; +        } +        if (dpl < cpl || dpl < rpl) { +        fail: +            CC_SRC = eflags & ~CC_Z; +            return 0; +        } +    } +    CC_SRC = eflags | CC_Z; +    return e2 & 0x00f0ff00; +} + +void helper_verr(CPUX86State *env, target_ulong selector1) +{ +    uint32_t e1, e2, eflags, selector; +    int rpl, dpl, cpl; + +    selector = selector1 & 0xffff; +    eflags = cpu_cc_compute_all(env, CC_OP); +    if ((selector & 0xfffc) == 0) { +        goto fail; +    } +    if (load_segment(env, &e1, &e2, selector) != 0) { +        goto fail; +    } +    if (!(e2 & DESC_S_MASK)) { +        goto fail; +    } +    rpl = selector & 3; +    dpl = (e2 >> DESC_DPL_SHIFT) & 3; +    cpl = env->hflags & HF_CPL_MASK; +    if (e2 & DESC_CS_MASK) { +        if (!(e2 & DESC_R_MASK)) { +            goto fail; +        } +        if (!(e2 & DESC_C_MASK)) { +            if (dpl < cpl || dpl < rpl) { +                goto fail; +            } +        } +    } else { +        if (dpl < cpl || dpl < rpl) { +        fail: +            CC_SRC = eflags & ~CC_Z; +            return; +        } +    } +    CC_SRC = eflags | CC_Z; +} + +void helper_verw(CPUX86State *env, target_ulong selector1) +{ +    uint32_t e1, e2, eflags, selector; +    int rpl, dpl, cpl; + +    selector = selector1 & 0xffff; +    eflags = cpu_cc_compute_all(env, CC_OP); +    if ((selector & 0xfffc) == 0) { +        goto fail; +    } +    if (load_segment(env, &e1, &e2, selector) != 0) { +        goto fail; +    } +    if (!(e2 & DESC_S_MASK)) { +        goto fail; +    } +    rpl = selector & 3; +    dpl = (e2 >> DESC_DPL_SHIFT) & 3; +    cpl = env->hflags & HF_CPL_MASK; +    if (e2 & DESC_CS_MASK) { +        goto fail; +    } else { +        if (dpl < cpl || dpl < rpl) { +            goto fail; +        } +        if (!(e2 & DESC_W_MASK)) { +        fail: +            CC_SRC = eflags & ~CC_Z; +            return; +        } +    } +    CC_SRC = eflags | CC_Z; +} + +#if defined(CONFIG_USER_ONLY) +void cpu_x86_load_seg(CPUX86State *env, int seg_reg, int selector) +{ +    if (!(env->cr[0] & CR0_PE_MASK) || (env->eflags & VM_MASK)) { +        int dpl = (env->eflags & VM_MASK) ? 3 : 0; +        selector &= 0xffff; +        cpu_x86_load_seg_cache(env, seg_reg, selector, +                               (selector << 4), 0xffff, +                               DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | +                               DESC_A_MASK | (dpl << DESC_DPL_SHIFT)); +    } else { +        helper_load_seg(env, seg_reg, selector); +    } +} +#endif + +/* check if Port I/O is allowed in TSS */ +static inline void check_io(CPUX86State *env, int addr, int size) +{ +    int io_offset, val, mask; + +    /* TSS must be a valid 32 bit one */ +    if (!(env->tr.flags & DESC_P_MASK) || +        ((env->tr.flags >> DESC_TYPE_SHIFT) & 0xf) != 9 || +        env->tr.limit < 103) { +        goto fail; +    } +    io_offset = cpu_lduw_kernel(env, env->tr.base + 0x66); +    io_offset += (addr >> 3); +    /* Note: the check needs two bytes */ +    if ((io_offset + 1) > env->tr.limit) { +        goto fail; +    } +    val = cpu_lduw_kernel(env, env->tr.base + io_offset); +    val >>= (addr & 7); +    mask = (1 << size) - 1; +    /* all bits must be zero to allow the I/O */ +    if ((val & mask) != 0) { +    fail: +        raise_exception_err(env, EXCP0D_GPF, 0); +    } +} + +void helper_check_iob(CPUX86State *env, uint32_t t0) +{ +    check_io(env, t0, 1); +} + +void helper_check_iow(CPUX86State *env, uint32_t t0) +{ +    check_io(env, t0, 2); +} + +void helper_check_iol(CPUX86State *env, uint32_t t0) +{ +    check_io(env, t0, 4); +} diff --git a/target-i386/shift_helper_template.h b/target-i386/shift_helper_template.h new file mode 100644 index 00000000..cf91a2d2 --- /dev/null +++ b/target-i386/shift_helper_template.h @@ -0,0 +1,108 @@ +/* + *  x86 shift helpers + * + *  Copyright (c) 2008 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#define DATA_BITS (1 << (3 + SHIFT)) +#define SHIFT_MASK (DATA_BITS - 1) +#if DATA_BITS <= 32 +#define SHIFT1_MASK 0x1f +#else +#define SHIFT1_MASK 0x3f +#endif + +#if DATA_BITS == 8 +#define SUFFIX b +#define DATA_MASK 0xff +#elif DATA_BITS == 16 +#define SUFFIX w +#define DATA_MASK 0xffff +#elif DATA_BITS == 32 +#define SUFFIX l +#define DATA_MASK 0xffffffff +#elif DATA_BITS == 64 +#define SUFFIX q +#define DATA_MASK 0xffffffffffffffffULL +#else +#error unhandled operand size +#endif + +target_ulong glue(helper_rcl, SUFFIX)(CPUX86State *env, target_ulong t0, +                                      target_ulong t1) +{ +    int count, eflags; +    target_ulong src; +    target_long res; + +    count = t1 & SHIFT1_MASK; +#if DATA_BITS == 16 +    count = rclw_table[count]; +#elif DATA_BITS == 8 +    count = rclb_table[count]; +#endif +    if (count) { +        eflags = env->cc_src; +        t0 &= DATA_MASK; +        src = t0; +        res = (t0 << count) | ((target_ulong)(eflags & CC_C) << (count - 1)); +        if (count > 1) { +            res |= t0 >> (DATA_BITS + 1 - count); +        } +        t0 = res; +        env->cc_src = (eflags & ~(CC_C | CC_O)) | +            (lshift(src ^ t0, 11 - (DATA_BITS - 1)) & CC_O) | +            ((src >> (DATA_BITS - count)) & CC_C); +    } +    return t0; +} + +target_ulong glue(helper_rcr, SUFFIX)(CPUX86State *env, target_ulong t0, +                                      target_ulong t1) +{ +    int count, eflags; +    target_ulong src; +    target_long res; + +    count = t1 & SHIFT1_MASK; +#if DATA_BITS == 16 +    count = rclw_table[count]; +#elif DATA_BITS == 8 +    count = rclb_table[count]; +#endif +    if (count) { +        eflags = env->cc_src; +        t0 &= DATA_MASK; +        src = t0; +        res = (t0 >> count) | +            ((target_ulong)(eflags & CC_C) << (DATA_BITS - count)); +        if (count > 1) { +            res |= t0 << (DATA_BITS + 1 - count); +        } +        t0 = res; +        env->cc_src = (eflags & ~(CC_C | CC_O)) | +            (lshift(src ^ t0, 11 - (DATA_BITS - 1)) & CC_O) | +            ((src >> (count - 1)) & CC_C); +    } +    return t0; +} + +#undef DATA_BITS +#undef SHIFT_MASK +#undef SHIFT1_MASK +#undef DATA_TYPE +#undef DATA_MASK +#undef SUFFIX diff --git a/target-i386/smm_helper.c b/target-i386/smm_helper.c new file mode 100644 index 00000000..02e24b92 --- /dev/null +++ b/target-i386/smm_helper.c @@ -0,0 +1,336 @@ +/* + *  x86 SMM helpers + * + *  Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "cpu.h" +#include "exec/helper-proto.h" + +/* SMM support */ + +#if defined(CONFIG_USER_ONLY) + +void do_smm_enter(X86CPU *cpu) +{ +} + +void helper_rsm(CPUX86State *env) +{ +} + +#else + +#ifdef TARGET_X86_64 +#define SMM_REVISION_ID 0x00020064 +#else +#define SMM_REVISION_ID 0x00020000 +#endif + +void cpu_smm_update(X86CPU *cpu) +{ +    CPUX86State *env = &cpu->env; +    bool smm_enabled = (env->hflags & HF_SMM_MASK); + +    if (cpu->smram) { +        memory_region_set_enabled(cpu->smram, smm_enabled); +    } +} + +void do_smm_enter(X86CPU *cpu) +{ +    CPUX86State *env = &cpu->env; +    CPUState *cs = CPU(cpu); +    target_ulong sm_state; +    SegmentCache *dt; +    int i, offset; + +    qemu_log_mask(CPU_LOG_INT, "SMM: enter\n"); +    log_cpu_state_mask(CPU_LOG_INT, CPU(cpu), CPU_DUMP_CCOP); + +    env->hflags |= HF_SMM_MASK; +    if (env->hflags2 & HF2_NMI_MASK) { +        env->hflags2 |= HF2_SMM_INSIDE_NMI_MASK; +    } else { +        env->hflags2 |= HF2_NMI_MASK; +    } +    cpu_smm_update(cpu); + +    sm_state = env->smbase + 0x8000; + +#ifdef TARGET_X86_64 +    for (i = 0; i < 6; i++) { +        dt = &env->segs[i]; +        offset = 0x7e00 + i * 16; +        x86_stw_phys(cs, sm_state + offset, dt->selector); +        x86_stw_phys(cs, sm_state + offset + 2, (dt->flags >> 8) & 0xf0ff); +        x86_stl_phys(cs, sm_state + offset + 4, dt->limit); +        x86_stq_phys(cs, sm_state + offset + 8, dt->base); +    } + +    x86_stq_phys(cs, sm_state + 0x7e68, env->gdt.base); +    x86_stl_phys(cs, sm_state + 0x7e64, env->gdt.limit); + +    x86_stw_phys(cs, sm_state + 0x7e70, env->ldt.selector); +    x86_stq_phys(cs, sm_state + 0x7e78, env->ldt.base); +    x86_stl_phys(cs, sm_state + 0x7e74, env->ldt.limit); +    x86_stw_phys(cs, sm_state + 0x7e72, (env->ldt.flags >> 8) & 0xf0ff); + +    x86_stq_phys(cs, sm_state + 0x7e88, env->idt.base); +    x86_stl_phys(cs, sm_state + 0x7e84, env->idt.limit); + +    x86_stw_phys(cs, sm_state + 0x7e90, env->tr.selector); +    x86_stq_phys(cs, sm_state + 0x7e98, env->tr.base); +    x86_stl_phys(cs, sm_state + 0x7e94, env->tr.limit); +    x86_stw_phys(cs, sm_state + 0x7e92, (env->tr.flags >> 8) & 0xf0ff); + +    x86_stq_phys(cs, sm_state + 0x7ed0, env->efer); + +    x86_stq_phys(cs, sm_state + 0x7ff8, env->regs[R_EAX]); +    x86_stq_phys(cs, sm_state + 0x7ff0, env->regs[R_ECX]); +    x86_stq_phys(cs, sm_state + 0x7fe8, env->regs[R_EDX]); +    x86_stq_phys(cs, sm_state + 0x7fe0, env->regs[R_EBX]); +    x86_stq_phys(cs, sm_state + 0x7fd8, env->regs[R_ESP]); +    x86_stq_phys(cs, sm_state + 0x7fd0, env->regs[R_EBP]); +    x86_stq_phys(cs, sm_state + 0x7fc8, env->regs[R_ESI]); +    x86_stq_phys(cs, sm_state + 0x7fc0, env->regs[R_EDI]); +    for (i = 8; i < 16; i++) { +        x86_stq_phys(cs, sm_state + 0x7ff8 - i * 8, env->regs[i]); +    } +    x86_stq_phys(cs, sm_state + 0x7f78, env->eip); +    x86_stl_phys(cs, sm_state + 0x7f70, cpu_compute_eflags(env)); +    x86_stl_phys(cs, sm_state + 0x7f68, env->dr[6]); +    x86_stl_phys(cs, sm_state + 0x7f60, env->dr[7]); + +    x86_stl_phys(cs, sm_state + 0x7f48, env->cr[4]); +    x86_stq_phys(cs, sm_state + 0x7f50, env->cr[3]); +    x86_stl_phys(cs, sm_state + 0x7f58, env->cr[0]); + +    x86_stl_phys(cs, sm_state + 0x7efc, SMM_REVISION_ID); +    x86_stl_phys(cs, sm_state + 0x7f00, env->smbase); +#else +    x86_stl_phys(cs, sm_state + 0x7ffc, env->cr[0]); +    x86_stl_phys(cs, sm_state + 0x7ff8, env->cr[3]); +    x86_stl_phys(cs, sm_state + 0x7ff4, cpu_compute_eflags(env)); +    x86_stl_phys(cs, sm_state + 0x7ff0, env->eip); +    x86_stl_phys(cs, sm_state + 0x7fec, env->regs[R_EDI]); +    x86_stl_phys(cs, sm_state + 0x7fe8, env->regs[R_ESI]); +    x86_stl_phys(cs, sm_state + 0x7fe4, env->regs[R_EBP]); +    x86_stl_phys(cs, sm_state + 0x7fe0, env->regs[R_ESP]); +    x86_stl_phys(cs, sm_state + 0x7fdc, env->regs[R_EBX]); +    x86_stl_phys(cs, sm_state + 0x7fd8, env->regs[R_EDX]); +    x86_stl_phys(cs, sm_state + 0x7fd4, env->regs[R_ECX]); +    x86_stl_phys(cs, sm_state + 0x7fd0, env->regs[R_EAX]); +    x86_stl_phys(cs, sm_state + 0x7fcc, env->dr[6]); +    x86_stl_phys(cs, sm_state + 0x7fc8, env->dr[7]); + +    x86_stl_phys(cs, sm_state + 0x7fc4, env->tr.selector); +    x86_stl_phys(cs, sm_state + 0x7f64, env->tr.base); +    x86_stl_phys(cs, sm_state + 0x7f60, env->tr.limit); +    x86_stl_phys(cs, sm_state + 0x7f5c, (env->tr.flags >> 8) & 0xf0ff); + +    x86_stl_phys(cs, sm_state + 0x7fc0, env->ldt.selector); +    x86_stl_phys(cs, sm_state + 0x7f80, env->ldt.base); +    x86_stl_phys(cs, sm_state + 0x7f7c, env->ldt.limit); +    x86_stl_phys(cs, sm_state + 0x7f78, (env->ldt.flags >> 8) & 0xf0ff); + +    x86_stl_phys(cs, sm_state + 0x7f74, env->gdt.base); +    x86_stl_phys(cs, sm_state + 0x7f70, env->gdt.limit); + +    x86_stl_phys(cs, sm_state + 0x7f58, env->idt.base); +    x86_stl_phys(cs, sm_state + 0x7f54, env->idt.limit); + +    for (i = 0; i < 6; i++) { +        dt = &env->segs[i]; +        if (i < 3) { +            offset = 0x7f84 + i * 12; +        } else { +            offset = 0x7f2c + (i - 3) * 12; +        } +        x86_stl_phys(cs, sm_state + 0x7fa8 + i * 4, dt->selector); +        x86_stl_phys(cs, sm_state + offset + 8, dt->base); +        x86_stl_phys(cs, sm_state + offset + 4, dt->limit); +        x86_stl_phys(cs, sm_state + offset, (dt->flags >> 8) & 0xf0ff); +    } +    x86_stl_phys(cs, sm_state + 0x7f14, env->cr[4]); + +    x86_stl_phys(cs, sm_state + 0x7efc, SMM_REVISION_ID); +    x86_stl_phys(cs, sm_state + 0x7ef8, env->smbase); +#endif +    /* init SMM cpu state */ + +#ifdef TARGET_X86_64 +    cpu_load_efer(env, 0); +#endif +    cpu_load_eflags(env, 0, ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | +                              DF_MASK)); +    env->eip = 0x00008000; +    cpu_x86_update_cr0(env, +                       env->cr[0] & ~(CR0_PE_MASK | CR0_EM_MASK | CR0_TS_MASK | +                                      CR0_PG_MASK)); +    cpu_x86_update_cr4(env, 0); +    env->dr[7] = 0x00000400; + +    cpu_x86_load_seg_cache(env, R_CS, (env->smbase >> 4) & 0xffff, env->smbase, +                           0xffffffff, +                           DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | +                           DESC_G_MASK | DESC_A_MASK); +    cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0xffffffff, +                           DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | +                           DESC_G_MASK | DESC_A_MASK); +    cpu_x86_load_seg_cache(env, R_ES, 0, 0, 0xffffffff, +                           DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | +                           DESC_G_MASK | DESC_A_MASK); +    cpu_x86_load_seg_cache(env, R_SS, 0, 0, 0xffffffff, +                           DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | +                           DESC_G_MASK | DESC_A_MASK); +    cpu_x86_load_seg_cache(env, R_FS, 0, 0, 0xffffffff, +                           DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | +                           DESC_G_MASK | DESC_A_MASK); +    cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0xffffffff, +                           DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | +                           DESC_G_MASK | DESC_A_MASK); +} + +void helper_rsm(CPUX86State *env) +{ +    X86CPU *cpu = x86_env_get_cpu(env); +    CPUState *cs = CPU(cpu); +    target_ulong sm_state; +    int i, offset; +    uint32_t val; + +    sm_state = env->smbase + 0x8000; +#ifdef TARGET_X86_64 +    cpu_load_efer(env, x86_ldq_phys(cs, sm_state + 0x7ed0)); + +    env->gdt.base = x86_ldq_phys(cs, sm_state + 0x7e68); +    env->gdt.limit = x86_ldl_phys(cs, sm_state + 0x7e64); + +    env->ldt.selector = x86_lduw_phys(cs, sm_state + 0x7e70); +    env->ldt.base = x86_ldq_phys(cs, sm_state + 0x7e78); +    env->ldt.limit = x86_ldl_phys(cs, sm_state + 0x7e74); +    env->ldt.flags = (x86_lduw_phys(cs, sm_state + 0x7e72) & 0xf0ff) << 8; + +    env->idt.base = x86_ldq_phys(cs, sm_state + 0x7e88); +    env->idt.limit = x86_ldl_phys(cs, sm_state + 0x7e84); + +    env->tr.selector = x86_lduw_phys(cs, sm_state + 0x7e90); +    env->tr.base = x86_ldq_phys(cs, sm_state + 0x7e98); +    env->tr.limit = x86_ldl_phys(cs, sm_state + 0x7e94); +    env->tr.flags = (x86_lduw_phys(cs, sm_state + 0x7e92) & 0xf0ff) << 8; + +    env->regs[R_EAX] = x86_ldq_phys(cs, sm_state + 0x7ff8); +    env->regs[R_ECX] = x86_ldq_phys(cs, sm_state + 0x7ff0); +    env->regs[R_EDX] = x86_ldq_phys(cs, sm_state + 0x7fe8); +    env->regs[R_EBX] = x86_ldq_phys(cs, sm_state + 0x7fe0); +    env->regs[R_ESP] = x86_ldq_phys(cs, sm_state + 0x7fd8); +    env->regs[R_EBP] = x86_ldq_phys(cs, sm_state + 0x7fd0); +    env->regs[R_ESI] = x86_ldq_phys(cs, sm_state + 0x7fc8); +    env->regs[R_EDI] = x86_ldq_phys(cs, sm_state + 0x7fc0); +    for (i = 8; i < 16; i++) { +        env->regs[i] = x86_ldq_phys(cs, sm_state + 0x7ff8 - i * 8); +    } +    env->eip = x86_ldq_phys(cs, sm_state + 0x7f78); +    cpu_load_eflags(env, x86_ldl_phys(cs, sm_state + 0x7f70), +                    ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK)); +    env->dr[6] = x86_ldl_phys(cs, sm_state + 0x7f68); +    env->dr[7] = x86_ldl_phys(cs, sm_state + 0x7f60); + +    cpu_x86_update_cr4(env, x86_ldl_phys(cs, sm_state + 0x7f48)); +    cpu_x86_update_cr3(env, x86_ldq_phys(cs, sm_state + 0x7f50)); +    cpu_x86_update_cr0(env, x86_ldl_phys(cs, sm_state + 0x7f58)); + +    for (i = 0; i < 6; i++) { +        offset = 0x7e00 + i * 16; +        cpu_x86_load_seg_cache(env, i, +                               x86_lduw_phys(cs, sm_state + offset), +                               x86_ldq_phys(cs, sm_state + offset + 8), +                               x86_ldl_phys(cs, sm_state + offset + 4), +                               (x86_lduw_phys(cs, sm_state + offset + 2) & +                                0xf0ff) << 8); +    } + +    val = x86_ldl_phys(cs, sm_state + 0x7efc); /* revision ID */ +    if (val & 0x20000) { +        env->smbase = x86_ldl_phys(cs, sm_state + 0x7f00) & ~0x7fff; +    } +#else +    cpu_x86_update_cr0(env, x86_ldl_phys(cs, sm_state + 0x7ffc)); +    cpu_x86_update_cr3(env, x86_ldl_phys(cs, sm_state + 0x7ff8)); +    cpu_load_eflags(env, x86_ldl_phys(cs, sm_state + 0x7ff4), +                    ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK)); +    env->eip = x86_ldl_phys(cs, sm_state + 0x7ff0); +    env->regs[R_EDI] = x86_ldl_phys(cs, sm_state + 0x7fec); +    env->regs[R_ESI] = x86_ldl_phys(cs, sm_state + 0x7fe8); +    env->regs[R_EBP] = x86_ldl_phys(cs, sm_state + 0x7fe4); +    env->regs[R_ESP] = x86_ldl_phys(cs, sm_state + 0x7fe0); +    env->regs[R_EBX] = x86_ldl_phys(cs, sm_state + 0x7fdc); +    env->regs[R_EDX] = x86_ldl_phys(cs, sm_state + 0x7fd8); +    env->regs[R_ECX] = x86_ldl_phys(cs, sm_state + 0x7fd4); +    env->regs[R_EAX] = x86_ldl_phys(cs, sm_state + 0x7fd0); +    env->dr[6] = x86_ldl_phys(cs, sm_state + 0x7fcc); +    env->dr[7] = x86_ldl_phys(cs, sm_state + 0x7fc8); + +    env->tr.selector = x86_ldl_phys(cs, sm_state + 0x7fc4) & 0xffff; +    env->tr.base = x86_ldl_phys(cs, sm_state + 0x7f64); +    env->tr.limit = x86_ldl_phys(cs, sm_state + 0x7f60); +    env->tr.flags = (x86_ldl_phys(cs, sm_state + 0x7f5c) & 0xf0ff) << 8; + +    env->ldt.selector = x86_ldl_phys(cs, sm_state + 0x7fc0) & 0xffff; +    env->ldt.base = x86_ldl_phys(cs, sm_state + 0x7f80); +    env->ldt.limit = x86_ldl_phys(cs, sm_state + 0x7f7c); +    env->ldt.flags = (x86_ldl_phys(cs, sm_state + 0x7f78) & 0xf0ff) << 8; + +    env->gdt.base = x86_ldl_phys(cs, sm_state + 0x7f74); +    env->gdt.limit = x86_ldl_phys(cs, sm_state + 0x7f70); + +    env->idt.base = x86_ldl_phys(cs, sm_state + 0x7f58); +    env->idt.limit = x86_ldl_phys(cs, sm_state + 0x7f54); + +    for (i = 0; i < 6; i++) { +        if (i < 3) { +            offset = 0x7f84 + i * 12; +        } else { +            offset = 0x7f2c + (i - 3) * 12; +        } +        cpu_x86_load_seg_cache(env, i, +                               x86_ldl_phys(cs, +                                        sm_state + 0x7fa8 + i * 4) & 0xffff, +                               x86_ldl_phys(cs, sm_state + offset + 8), +                               x86_ldl_phys(cs, sm_state + offset + 4), +                               (x86_ldl_phys(cs, +                                         sm_state + offset) & 0xf0ff) << 8); +    } +    cpu_x86_update_cr4(env, x86_ldl_phys(cs, sm_state + 0x7f14)); + +    val = x86_ldl_phys(cs, sm_state + 0x7efc); /* revision ID */ +    if (val & 0x20000) { +        env->smbase = x86_ldl_phys(cs, sm_state + 0x7ef8) & ~0x7fff; +    } +#endif +    if ((env->hflags2 & HF2_SMM_INSIDE_NMI_MASK) == 0) { +        env->hflags2 &= ~HF2_NMI_MASK; +    } +    env->hflags2 &= ~HF2_SMM_INSIDE_NMI_MASK; +    env->hflags &= ~HF_SMM_MASK; +    cpu_smm_update(cpu); + +    qemu_log_mask(CPU_LOG_INT, "SMM: after RSM\n"); +    log_cpu_state_mask(CPU_LOG_INT, CPU(cpu), CPU_DUMP_CCOP); +} + +#endif /* !CONFIG_USER_ONLY */ diff --git a/target-i386/svm.h b/target-i386/svm.h new file mode 100644 index 00000000..04193ed6 --- /dev/null +++ b/target-i386/svm.h @@ -0,0 +1,222 @@ +#ifndef __SVM_H +#define __SVM_H + +#define TLB_CONTROL_DO_NOTHING 0 +#define TLB_CONTROL_FLUSH_ALL_ASID 1 + +#define V_TPR_MASK 0x0f + +#define V_IRQ_SHIFT 8 +#define V_IRQ_MASK (1 << V_IRQ_SHIFT) + +#define V_INTR_PRIO_SHIFT 16 +#define V_INTR_PRIO_MASK (0x0f << V_INTR_PRIO_SHIFT) + +#define V_IGN_TPR_SHIFT 20 +#define V_IGN_TPR_MASK (1 << V_IGN_TPR_SHIFT) + +#define V_INTR_MASKING_SHIFT 24 +#define V_INTR_MASKING_MASK (1 << V_INTR_MASKING_SHIFT) + +#define SVM_INTERRUPT_SHADOW_MASK 1 + +#define SVM_IOIO_STR_SHIFT 2 +#define SVM_IOIO_REP_SHIFT 3 +#define SVM_IOIO_SIZE_SHIFT 4 +#define SVM_IOIO_ASIZE_SHIFT 7 + +#define SVM_IOIO_TYPE_MASK 1 +#define SVM_IOIO_STR_MASK (1 << SVM_IOIO_STR_SHIFT) +#define SVM_IOIO_REP_MASK (1 << SVM_IOIO_REP_SHIFT) +#define SVM_IOIO_SIZE_MASK (7 << SVM_IOIO_SIZE_SHIFT) +#define SVM_IOIO_ASIZE_MASK (7 << SVM_IOIO_ASIZE_SHIFT) + +#define SVM_EVTINJ_VEC_MASK 0xff + +#define SVM_EVTINJ_TYPE_SHIFT 8 +#define SVM_EVTINJ_TYPE_MASK (7 << SVM_EVTINJ_TYPE_SHIFT) + +#define SVM_EVTINJ_TYPE_INTR (0 << SVM_EVTINJ_TYPE_SHIFT) +#define SVM_EVTINJ_TYPE_NMI (2 << SVM_EVTINJ_TYPE_SHIFT) +#define SVM_EVTINJ_TYPE_EXEPT (3 << SVM_EVTINJ_TYPE_SHIFT) +#define SVM_EVTINJ_TYPE_SOFT (4 << SVM_EVTINJ_TYPE_SHIFT) + +#define SVM_EVTINJ_VALID (1 << 31) +#define SVM_EVTINJ_VALID_ERR (1 << 11) + +#define SVM_EXITINTINFO_VEC_MASK SVM_EVTINJ_VEC_MASK + +#define	SVM_EXITINTINFO_TYPE_INTR SVM_EVTINJ_TYPE_INTR +#define	SVM_EXITINTINFO_TYPE_NMI SVM_EVTINJ_TYPE_NMI +#define	SVM_EXITINTINFO_TYPE_EXEPT SVM_EVTINJ_TYPE_EXEPT +#define	SVM_EXITINTINFO_TYPE_SOFT SVM_EVTINJ_TYPE_SOFT + +#define SVM_EXITINTINFO_VALID SVM_EVTINJ_VALID +#define SVM_EXITINTINFO_VALID_ERR SVM_EVTINJ_VALID_ERR + +#define	SVM_EXIT_READ_CR0 	0x000 +#define	SVM_EXIT_READ_CR3 	0x003 +#define	SVM_EXIT_READ_CR4 	0x004 +#define	SVM_EXIT_READ_CR8 	0x008 +#define	SVM_EXIT_WRITE_CR0 	0x010 +#define	SVM_EXIT_WRITE_CR3 	0x013 +#define	SVM_EXIT_WRITE_CR4 	0x014 +#define	SVM_EXIT_WRITE_CR8 	0x018 +#define	SVM_EXIT_READ_DR0 	0x020 +#define	SVM_EXIT_READ_DR1 	0x021 +#define	SVM_EXIT_READ_DR2 	0x022 +#define	SVM_EXIT_READ_DR3 	0x023 +#define	SVM_EXIT_READ_DR4 	0x024 +#define	SVM_EXIT_READ_DR5 	0x025 +#define	SVM_EXIT_READ_DR6 	0x026 +#define	SVM_EXIT_READ_DR7 	0x027 +#define	SVM_EXIT_WRITE_DR0 	0x030 +#define	SVM_EXIT_WRITE_DR1 	0x031 +#define	SVM_EXIT_WRITE_DR2 	0x032 +#define	SVM_EXIT_WRITE_DR3 	0x033 +#define	SVM_EXIT_WRITE_DR4 	0x034 +#define	SVM_EXIT_WRITE_DR5 	0x035 +#define	SVM_EXIT_WRITE_DR6 	0x036 +#define	SVM_EXIT_WRITE_DR7 	0x037 +#define SVM_EXIT_EXCP_BASE      0x040 +#define SVM_EXIT_INTR		0x060 +#define SVM_EXIT_NMI		0x061 +#define SVM_EXIT_SMI		0x062 +#define SVM_EXIT_INIT		0x063 +#define SVM_EXIT_VINTR		0x064 +#define SVM_EXIT_CR0_SEL_WRITE	0x065 +#define SVM_EXIT_IDTR_READ	0x066 +#define SVM_EXIT_GDTR_READ	0x067 +#define SVM_EXIT_LDTR_READ	0x068 +#define SVM_EXIT_TR_READ	0x069 +#define SVM_EXIT_IDTR_WRITE	0x06a +#define SVM_EXIT_GDTR_WRITE	0x06b +#define SVM_EXIT_LDTR_WRITE	0x06c +#define SVM_EXIT_TR_WRITE	0x06d +#define SVM_EXIT_RDTSC		0x06e +#define SVM_EXIT_RDPMC		0x06f +#define SVM_EXIT_PUSHF		0x070 +#define SVM_EXIT_POPF		0x071 +#define SVM_EXIT_CPUID		0x072 +#define SVM_EXIT_RSM		0x073 +#define SVM_EXIT_IRET		0x074 +#define SVM_EXIT_SWINT		0x075 +#define SVM_EXIT_INVD		0x076 +#define SVM_EXIT_PAUSE		0x077 +#define SVM_EXIT_HLT		0x078 +#define SVM_EXIT_INVLPG		0x079 +#define SVM_EXIT_INVLPGA	0x07a +#define SVM_EXIT_IOIO		0x07b +#define SVM_EXIT_MSR		0x07c +#define SVM_EXIT_TASK_SWITCH	0x07d +#define SVM_EXIT_FERR_FREEZE	0x07e +#define SVM_EXIT_SHUTDOWN	0x07f +#define SVM_EXIT_VMRUN		0x080 +#define SVM_EXIT_VMMCALL	0x081 +#define SVM_EXIT_VMLOAD		0x082 +#define SVM_EXIT_VMSAVE		0x083 +#define SVM_EXIT_STGI		0x084 +#define SVM_EXIT_CLGI		0x085 +#define SVM_EXIT_SKINIT		0x086 +#define SVM_EXIT_RDTSCP		0x087 +#define SVM_EXIT_ICEBP		0x088 +#define SVM_EXIT_WBINVD		0x089 +/* only included in documentation, maybe wrong */ +#define SVM_EXIT_MONITOR	0x08a +#define SVM_EXIT_MWAIT		0x08b +#define SVM_EXIT_NPF  		0x400 + +#define SVM_EXIT_ERR		-1 + +#define SVM_CR0_SELECTIVE_MASK (1 << 3 | 1) /* TS and MP */ + +struct QEMU_PACKED vmcb_control_area { +	uint16_t intercept_cr_read; +	uint16_t intercept_cr_write; +	uint16_t intercept_dr_read; +	uint16_t intercept_dr_write; +	uint32_t intercept_exceptions; +	uint64_t intercept; +	uint8_t reserved_1[44]; +	uint64_t iopm_base_pa; +	uint64_t msrpm_base_pa; +	uint64_t tsc_offset; +	uint32_t asid; +	uint8_t tlb_ctl; +	uint8_t reserved_2[3]; +	uint32_t int_ctl; +	uint32_t int_vector; +	uint32_t int_state; +	uint8_t reserved_3[4]; +	uint64_t exit_code; +	uint64_t exit_info_1; +	uint64_t exit_info_2; +	uint32_t exit_int_info; +	uint32_t exit_int_info_err; +	uint64_t nested_ctl; +	uint8_t reserved_4[16]; +	uint32_t event_inj; +	uint32_t event_inj_err; +	uint64_t nested_cr3; +	uint64_t lbr_ctl; +	uint8_t reserved_5[832]; +}; + +struct QEMU_PACKED vmcb_seg { +	uint16_t selector; +	uint16_t attrib; +	uint32_t limit; +	uint64_t base; +}; + +struct QEMU_PACKED vmcb_save_area { +	struct vmcb_seg es; +	struct vmcb_seg cs; +	struct vmcb_seg ss; +	struct vmcb_seg ds; +	struct vmcb_seg fs; +	struct vmcb_seg gs; +	struct vmcb_seg gdtr; +	struct vmcb_seg ldtr; +	struct vmcb_seg idtr; +	struct vmcb_seg tr; +	uint8_t reserved_1[43]; +	uint8_t cpl; +	uint8_t reserved_2[4]; +	uint64_t efer; +	uint8_t reserved_3[112]; +	uint64_t cr4; +	uint64_t cr3; +	uint64_t cr0; +	uint64_t dr7; +	uint64_t dr6; +	uint64_t rflags; +	uint64_t rip; +	uint8_t reserved_4[88]; +	uint64_t rsp; +	uint8_t reserved_5[24]; +	uint64_t rax; +	uint64_t star; +	uint64_t lstar; +	uint64_t cstar; +	uint64_t sfmask; +	uint64_t kernel_gs_base; +	uint64_t sysenter_cs; +	uint64_t sysenter_esp; +	uint64_t sysenter_eip; +	uint64_t cr2; +	uint8_t reserved_6[32]; +	uint64_t g_pat; +	uint64_t dbgctl; +	uint64_t br_from; +	uint64_t br_to; +	uint64_t last_excp_from; +	uint64_t last_excp_to; +}; + +struct QEMU_PACKED vmcb { +	struct vmcb_control_area control; +	struct vmcb_save_area save; +}; + +#endif diff --git a/target-i386/svm_helper.c b/target-i386/svm_helper.c new file mode 100644 index 00000000..f1fabf54 --- /dev/null +++ b/target-i386/svm_helper.c @@ -0,0 +1,772 @@ +/* + *  x86 SVM helpers + * + *  Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "cpu.h" +#include "exec/cpu-all.h" +#include "exec/helper-proto.h" +#include "exec/cpu_ldst.h" + +/* Secure Virtual Machine helpers */ + +#if defined(CONFIG_USER_ONLY) + +void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) +{ +} + +void helper_vmmcall(CPUX86State *env) +{ +} + +void helper_vmload(CPUX86State *env, int aflag) +{ +} + +void helper_vmsave(CPUX86State *env, int aflag) +{ +} + +void helper_stgi(CPUX86State *env) +{ +} + +void helper_clgi(CPUX86State *env) +{ +} + +void helper_skinit(CPUX86State *env) +{ +} + +void helper_invlpga(CPUX86State *env, int aflag) +{ +} + +void helper_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1) +{ +} + +void cpu_vmexit(CPUX86State *nenv, uint32_t exit_code, uint64_t exit_info_1) +{ +} + +void helper_svm_check_intercept_param(CPUX86State *env, uint32_t type, +                                      uint64_t param) +{ +} + +void cpu_svm_check_intercept_param(CPUX86State *env, uint32_t type, +                                   uint64_t param) +{ +} + +void helper_svm_check_io(CPUX86State *env, uint32_t port, uint32_t param, +                         uint32_t next_eip_addend) +{ +} +#else + +static inline void svm_save_seg(CPUX86State *env, hwaddr addr, +                                const SegmentCache *sc) +{ +    CPUState *cs = CPU(x86_env_get_cpu(env)); + +    x86_stw_phys(cs, addr + offsetof(struct vmcb_seg, selector), +             sc->selector); +    x86_stq_phys(cs, addr + offsetof(struct vmcb_seg, base), +             sc->base); +    x86_stl_phys(cs, addr + offsetof(struct vmcb_seg, limit), +             sc->limit); +    x86_stw_phys(cs, addr + offsetof(struct vmcb_seg, attrib), +             ((sc->flags >> 8) & 0xff) | ((sc->flags >> 12) & 0x0f00)); +} + +static inline void svm_load_seg(CPUX86State *env, hwaddr addr, +                                SegmentCache *sc) +{ +    CPUState *cs = CPU(x86_env_get_cpu(env)); +    unsigned int flags; + +    sc->selector = x86_lduw_phys(cs, +                             addr + offsetof(struct vmcb_seg, selector)); +    sc->base = x86_ldq_phys(cs, addr + offsetof(struct vmcb_seg, base)); +    sc->limit = x86_ldl_phys(cs, addr + offsetof(struct vmcb_seg, limit)); +    flags = x86_lduw_phys(cs, addr + offsetof(struct vmcb_seg, attrib)); +    sc->flags = ((flags & 0xff) << 8) | ((flags & 0x0f00) << 12); +} + +static inline void svm_load_seg_cache(CPUX86State *env, hwaddr addr, +                                      int seg_reg) +{ +    SegmentCache sc1, *sc = &sc1; + +    svm_load_seg(env, addr, sc); +    cpu_x86_load_seg_cache(env, seg_reg, sc->selector, +                           sc->base, sc->limit, sc->flags); +} + +void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) +{ +    CPUState *cs = CPU(x86_env_get_cpu(env)); +    target_ulong addr; +    uint32_t event_inj; +    uint32_t int_ctl; + +    cpu_svm_check_intercept_param(env, SVM_EXIT_VMRUN, 0); + +    if (aflag == 2) { +        addr = env->regs[R_EAX]; +    } else { +        addr = (uint32_t)env->regs[R_EAX]; +    } + +    qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmrun! " TARGET_FMT_lx "\n", addr); + +    env->vm_vmcb = addr; + +    /* save the current CPU state in the hsave page */ +    x86_stq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.gdtr.base), +             env->gdt.base); +    x86_stl_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.gdtr.limit), +             env->gdt.limit); + +    x86_stq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.idtr.base), +             env->idt.base); +    x86_stl_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.idtr.limit), +             env->idt.limit); + +    x86_stq_phys(cs, +             env->vm_hsave + offsetof(struct vmcb, save.cr0), env->cr[0]); +    x86_stq_phys(cs, +             env->vm_hsave + offsetof(struct vmcb, save.cr2), env->cr[2]); +    x86_stq_phys(cs, +             env->vm_hsave + offsetof(struct vmcb, save.cr3), env->cr[3]); +    x86_stq_phys(cs, +             env->vm_hsave + offsetof(struct vmcb, save.cr4), env->cr[4]); +    x86_stq_phys(cs, +             env->vm_hsave + offsetof(struct vmcb, save.dr6), env->dr[6]); +    x86_stq_phys(cs, +             env->vm_hsave + offsetof(struct vmcb, save.dr7), env->dr[7]); + +    x86_stq_phys(cs, +             env->vm_hsave + offsetof(struct vmcb, save.efer), env->efer); +    x86_stq_phys(cs, +             env->vm_hsave + offsetof(struct vmcb, save.rflags), +             cpu_compute_eflags(env)); + +    svm_save_seg(env, env->vm_hsave + offsetof(struct vmcb, save.es), +                 &env->segs[R_ES]); +    svm_save_seg(env, env->vm_hsave + offsetof(struct vmcb, save.cs), +                 &env->segs[R_CS]); +    svm_save_seg(env, env->vm_hsave + offsetof(struct vmcb, save.ss), +                 &env->segs[R_SS]); +    svm_save_seg(env, env->vm_hsave + offsetof(struct vmcb, save.ds), +                 &env->segs[R_DS]); + +    x86_stq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.rip), +             env->eip + next_eip_addend); +    x86_stq_phys(cs, +             env->vm_hsave + offsetof(struct vmcb, save.rsp), env->regs[R_ESP]); +    x86_stq_phys(cs, +             env->vm_hsave + offsetof(struct vmcb, save.rax), env->regs[R_EAX]); + +    /* load the interception bitmaps so we do not need to access the +       vmcb in svm mode */ +    env->intercept = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, +                                                      control.intercept)); +    env->intercept_cr_read = x86_lduw_phys(cs, env->vm_vmcb + +                                       offsetof(struct vmcb, +                                                control.intercept_cr_read)); +    env->intercept_cr_write = x86_lduw_phys(cs, env->vm_vmcb + +                                        offsetof(struct vmcb, +                                                 control.intercept_cr_write)); +    env->intercept_dr_read = x86_lduw_phys(cs, env->vm_vmcb + +                                       offsetof(struct vmcb, +                                                control.intercept_dr_read)); +    env->intercept_dr_write = x86_lduw_phys(cs, env->vm_vmcb + +                                        offsetof(struct vmcb, +                                                 control.intercept_dr_write)); +    env->intercept_exceptions = x86_ldl_phys(cs, env->vm_vmcb + +                                         offsetof(struct vmcb, +                                                  control.intercept_exceptions +                                                  )); + +    /* enable intercepts */ +    env->hflags |= HF_SVMI_MASK; + +    env->tsc_offset = x86_ldq_phys(cs, env->vm_vmcb + +                               offsetof(struct vmcb, control.tsc_offset)); + +    env->gdt.base  = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, +                                                      save.gdtr.base)); +    env->gdt.limit = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, +                                                      save.gdtr.limit)); + +    env->idt.base  = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, +                                                      save.idtr.base)); +    env->idt.limit = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, +                                                      save.idtr.limit)); + +    /* clear exit_info_2 so we behave like the real hardware */ +    x86_stq_phys(cs, +             env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), 0); + +    cpu_x86_update_cr0(env, x86_ldq_phys(cs, +                                     env->vm_vmcb + offsetof(struct vmcb, +                                                             save.cr0))); +    cpu_x86_update_cr4(env, x86_ldq_phys(cs, +                                     env->vm_vmcb + offsetof(struct vmcb, +                                                             save.cr4))); +    cpu_x86_update_cr3(env, x86_ldq_phys(cs, +                                     env->vm_vmcb + offsetof(struct vmcb, +                                                             save.cr3))); +    env->cr[2] = x86_ldq_phys(cs, +                          env->vm_vmcb + offsetof(struct vmcb, save.cr2)); +    int_ctl = x86_ldl_phys(cs, +                       env->vm_vmcb + offsetof(struct vmcb, control.int_ctl)); +    env->hflags2 &= ~(HF2_HIF_MASK | HF2_VINTR_MASK); +    if (int_ctl & V_INTR_MASKING_MASK) { +        env->v_tpr = int_ctl & V_TPR_MASK; +        env->hflags2 |= HF2_VINTR_MASK; +        if (env->eflags & IF_MASK) { +            env->hflags2 |= HF2_HIF_MASK; +        } +    } + +    cpu_load_efer(env, +                  x86_ldq_phys(cs, +                           env->vm_vmcb + offsetof(struct vmcb, save.efer))); +    env->eflags = 0; +    cpu_load_eflags(env, x86_ldq_phys(cs, +                                  env->vm_vmcb + offsetof(struct vmcb, +                                                          save.rflags)), +                    ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK)); + +    svm_load_seg_cache(env, env->vm_vmcb + offsetof(struct vmcb, save.es), +                       R_ES); +    svm_load_seg_cache(env, env->vm_vmcb + offsetof(struct vmcb, save.cs), +                       R_CS); +    svm_load_seg_cache(env, env->vm_vmcb + offsetof(struct vmcb, save.ss), +                       R_SS); +    svm_load_seg_cache(env, env->vm_vmcb + offsetof(struct vmcb, save.ds), +                       R_DS); + +    env->eip = x86_ldq_phys(cs, +                        env->vm_vmcb + offsetof(struct vmcb, save.rip)); + +    env->regs[R_ESP] = x86_ldq_phys(cs, +                                env->vm_vmcb + offsetof(struct vmcb, save.rsp)); +    env->regs[R_EAX] = x86_ldq_phys(cs, +                                env->vm_vmcb + offsetof(struct vmcb, save.rax)); +    env->dr[7] = x86_ldq_phys(cs, +                          env->vm_vmcb + offsetof(struct vmcb, save.dr7)); +    env->dr[6] = x86_ldq_phys(cs, +                          env->vm_vmcb + offsetof(struct vmcb, save.dr6)); + +    /* FIXME: guest state consistency checks */ + +    switch (x86_ldub_phys(cs, +                      env->vm_vmcb + offsetof(struct vmcb, control.tlb_ctl))) { +    case TLB_CONTROL_DO_NOTHING: +        break; +    case TLB_CONTROL_FLUSH_ALL_ASID: +        /* FIXME: this is not 100% correct but should work for now */ +        tlb_flush(cs, 1); +        break; +    } + +    env->hflags2 |= HF2_GIF_MASK; + +    if (int_ctl & V_IRQ_MASK) { +        CPUState *cs = CPU(x86_env_get_cpu(env)); + +        cs->interrupt_request |= CPU_INTERRUPT_VIRQ; +    } + +    /* maybe we need to inject an event */ +    event_inj = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, +                                                 control.event_inj)); +    if (event_inj & SVM_EVTINJ_VALID) { +        uint8_t vector = event_inj & SVM_EVTINJ_VEC_MASK; +        uint16_t valid_err = event_inj & SVM_EVTINJ_VALID_ERR; +        uint32_t event_inj_err = x86_ldl_phys(cs, env->vm_vmcb + +                                          offsetof(struct vmcb, +                                                   control.event_inj_err)); + +        qemu_log_mask(CPU_LOG_TB_IN_ASM, "Injecting(%#hx): ", valid_err); +        /* FIXME: need to implement valid_err */ +        switch (event_inj & SVM_EVTINJ_TYPE_MASK) { +        case SVM_EVTINJ_TYPE_INTR: +            cs->exception_index = vector; +            env->error_code = event_inj_err; +            env->exception_is_int = 0; +            env->exception_next_eip = -1; +            qemu_log_mask(CPU_LOG_TB_IN_ASM, "INTR"); +            /* XXX: is it always correct? */ +            do_interrupt_x86_hardirq(env, vector, 1); +            break; +        case SVM_EVTINJ_TYPE_NMI: +            cs->exception_index = EXCP02_NMI; +            env->error_code = event_inj_err; +            env->exception_is_int = 0; +            env->exception_next_eip = env->eip; +            qemu_log_mask(CPU_LOG_TB_IN_ASM, "NMI"); +            cpu_loop_exit(cs); +            break; +        case SVM_EVTINJ_TYPE_EXEPT: +            cs->exception_index = vector; +            env->error_code = event_inj_err; +            env->exception_is_int = 0; +            env->exception_next_eip = -1; +            qemu_log_mask(CPU_LOG_TB_IN_ASM, "EXEPT"); +            cpu_loop_exit(cs); +            break; +        case SVM_EVTINJ_TYPE_SOFT: +            cs->exception_index = vector; +            env->error_code = event_inj_err; +            env->exception_is_int = 1; +            env->exception_next_eip = env->eip; +            qemu_log_mask(CPU_LOG_TB_IN_ASM, "SOFT"); +            cpu_loop_exit(cs); +            break; +        } +        qemu_log_mask(CPU_LOG_TB_IN_ASM, " %#x %#x\n", cs->exception_index, +                      env->error_code); +    } +} + +void helper_vmmcall(CPUX86State *env) +{ +    cpu_svm_check_intercept_param(env, SVM_EXIT_VMMCALL, 0); +    raise_exception(env, EXCP06_ILLOP); +} + +void helper_vmload(CPUX86State *env, int aflag) +{ +    CPUState *cs = CPU(x86_env_get_cpu(env)); +    target_ulong addr; + +    cpu_svm_check_intercept_param(env, SVM_EXIT_VMLOAD, 0); + +    if (aflag == 2) { +        addr = env->regs[R_EAX]; +    } else { +        addr = (uint32_t)env->regs[R_EAX]; +    } + +    qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmload! " TARGET_FMT_lx +                  "\nFS: %016" PRIx64 " | " TARGET_FMT_lx "\n", +                  addr, x86_ldq_phys(cs, addr + offsetof(struct vmcb, +                                                          save.fs.base)), +                  env->segs[R_FS].base); + +    svm_load_seg_cache(env, addr + offsetof(struct vmcb, save.fs), R_FS); +    svm_load_seg_cache(env, addr + offsetof(struct vmcb, save.gs), R_GS); +    svm_load_seg(env, addr + offsetof(struct vmcb, save.tr), &env->tr); +    svm_load_seg(env, addr + offsetof(struct vmcb, save.ldtr), &env->ldt); + +#ifdef TARGET_X86_64 +    env->kernelgsbase = x86_ldq_phys(cs, addr + offsetof(struct vmcb, +                                                 save.kernel_gs_base)); +    env->lstar = x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.lstar)); +    env->cstar = x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.cstar)); +    env->fmask = x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.sfmask)); +#endif +    env->star = x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.star)); +    env->sysenter_cs = x86_ldq_phys(cs, +                                addr + offsetof(struct vmcb, save.sysenter_cs)); +    env->sysenter_esp = x86_ldq_phys(cs, addr + offsetof(struct vmcb, +                                                 save.sysenter_esp)); +    env->sysenter_eip = x86_ldq_phys(cs, addr + offsetof(struct vmcb, +                                                 save.sysenter_eip)); +} + +void helper_vmsave(CPUX86State *env, int aflag) +{ +    CPUState *cs = CPU(x86_env_get_cpu(env)); +    target_ulong addr; + +    cpu_svm_check_intercept_param(env, SVM_EXIT_VMSAVE, 0); + +    if (aflag == 2) { +        addr = env->regs[R_EAX]; +    } else { +        addr = (uint32_t)env->regs[R_EAX]; +    } + +    qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmsave! " TARGET_FMT_lx +                  "\nFS: %016" PRIx64 " | " TARGET_FMT_lx "\n", +                  addr, x86_ldq_phys(cs, +                                 addr + offsetof(struct vmcb, save.fs.base)), +                  env->segs[R_FS].base); + +    svm_save_seg(env, addr + offsetof(struct vmcb, save.fs), +                 &env->segs[R_FS]); +    svm_save_seg(env, addr + offsetof(struct vmcb, save.gs), +                 &env->segs[R_GS]); +    svm_save_seg(env, addr + offsetof(struct vmcb, save.tr), +                 &env->tr); +    svm_save_seg(env, addr + offsetof(struct vmcb, save.ldtr), +                 &env->ldt); + +#ifdef TARGET_X86_64 +    x86_stq_phys(cs, addr + offsetof(struct vmcb, save.kernel_gs_base), +             env->kernelgsbase); +    x86_stq_phys(cs, addr + offsetof(struct vmcb, save.lstar), env->lstar); +    x86_stq_phys(cs, addr + offsetof(struct vmcb, save.cstar), env->cstar); +    x86_stq_phys(cs, addr + offsetof(struct vmcb, save.sfmask), env->fmask); +#endif +    x86_stq_phys(cs, addr + offsetof(struct vmcb, save.star), env->star); +    x86_stq_phys(cs, +             addr + offsetof(struct vmcb, save.sysenter_cs), env->sysenter_cs); +    x86_stq_phys(cs, addr + offsetof(struct vmcb, save.sysenter_esp), +             env->sysenter_esp); +    x86_stq_phys(cs, addr + offsetof(struct vmcb, save.sysenter_eip), +             env->sysenter_eip); +} + +void helper_stgi(CPUX86State *env) +{ +    cpu_svm_check_intercept_param(env, SVM_EXIT_STGI, 0); +    env->hflags2 |= HF2_GIF_MASK; +} + +void helper_clgi(CPUX86State *env) +{ +    cpu_svm_check_intercept_param(env, SVM_EXIT_CLGI, 0); +    env->hflags2 &= ~HF2_GIF_MASK; +} + +void helper_skinit(CPUX86State *env) +{ +    cpu_svm_check_intercept_param(env, SVM_EXIT_SKINIT, 0); +    /* XXX: not implemented */ +    raise_exception(env, EXCP06_ILLOP); +} + +void helper_invlpga(CPUX86State *env, int aflag) +{ +    X86CPU *cpu = x86_env_get_cpu(env); +    target_ulong addr; + +    cpu_svm_check_intercept_param(env, SVM_EXIT_INVLPGA, 0); + +    if (aflag == 2) { +        addr = env->regs[R_EAX]; +    } else { +        addr = (uint32_t)env->regs[R_EAX]; +    } + +    /* XXX: could use the ASID to see if it is needed to do the +       flush */ +    tlb_flush_page(CPU(cpu), addr); +} + +void helper_svm_check_intercept_param(CPUX86State *env, uint32_t type, +                                      uint64_t param) +{ +    CPUState *cs = CPU(x86_env_get_cpu(env)); + +    if (likely(!(env->hflags & HF_SVMI_MASK))) { +        return; +    } +    switch (type) { +    case SVM_EXIT_READ_CR0 ... SVM_EXIT_READ_CR0 + 8: +        if (env->intercept_cr_read & (1 << (type - SVM_EXIT_READ_CR0))) { +            helper_vmexit(env, type, param); +        } +        break; +    case SVM_EXIT_WRITE_CR0 ... SVM_EXIT_WRITE_CR0 + 8: +        if (env->intercept_cr_write & (1 << (type - SVM_EXIT_WRITE_CR0))) { +            helper_vmexit(env, type, param); +        } +        break; +    case SVM_EXIT_READ_DR0 ... SVM_EXIT_READ_DR0 + 7: +        if (env->intercept_dr_read & (1 << (type - SVM_EXIT_READ_DR0))) { +            helper_vmexit(env, type, param); +        } +        break; +    case SVM_EXIT_WRITE_DR0 ... SVM_EXIT_WRITE_DR0 + 7: +        if (env->intercept_dr_write & (1 << (type - SVM_EXIT_WRITE_DR0))) { +            helper_vmexit(env, type, param); +        } +        break; +    case SVM_EXIT_EXCP_BASE ... SVM_EXIT_EXCP_BASE + 31: +        if (env->intercept_exceptions & (1 << (type - SVM_EXIT_EXCP_BASE))) { +            helper_vmexit(env, type, param); +        } +        break; +    case SVM_EXIT_MSR: +        if (env->intercept & (1ULL << (SVM_EXIT_MSR - SVM_EXIT_INTR))) { +            /* FIXME: this should be read in at vmrun (faster this way?) */ +            uint64_t addr = x86_ldq_phys(cs, env->vm_vmcb + +                                     offsetof(struct vmcb, +                                              control.msrpm_base_pa)); +            uint32_t t0, t1; + +            switch ((uint32_t)env->regs[R_ECX]) { +            case 0 ... 0x1fff: +                t0 = (env->regs[R_ECX] * 2) % 8; +                t1 = (env->regs[R_ECX] * 2) / 8; +                break; +            case 0xc0000000 ... 0xc0001fff: +                t0 = (8192 + env->regs[R_ECX] - 0xc0000000) * 2; +                t1 = (t0 / 8); +                t0 %= 8; +                break; +            case 0xc0010000 ... 0xc0011fff: +                t0 = (16384 + env->regs[R_ECX] - 0xc0010000) * 2; +                t1 = (t0 / 8); +                t0 %= 8; +                break; +            default: +                helper_vmexit(env, type, param); +                t0 = 0; +                t1 = 0; +                break; +            } +            if (x86_ldub_phys(cs, addr + t1) & ((1 << param) << t0)) { +                helper_vmexit(env, type, param); +            } +        } +        break; +    default: +        if (env->intercept & (1ULL << (type - SVM_EXIT_INTR))) { +            helper_vmexit(env, type, param); +        } +        break; +    } +} + +void cpu_svm_check_intercept_param(CPUX86State *env, uint32_t type, +                                   uint64_t param) +{ +    helper_svm_check_intercept_param(env, type, param); +} + +void helper_svm_check_io(CPUX86State *env, uint32_t port, uint32_t param, +                         uint32_t next_eip_addend) +{ +    CPUState *cs = CPU(x86_env_get_cpu(env)); + +    if (env->intercept & (1ULL << (SVM_EXIT_IOIO - SVM_EXIT_INTR))) { +        /* FIXME: this should be read in at vmrun (faster this way?) */ +        uint64_t addr = x86_ldq_phys(cs, env->vm_vmcb + +                                 offsetof(struct vmcb, control.iopm_base_pa)); +        uint16_t mask = (1 << ((param >> 4) & 7)) - 1; + +        if (x86_lduw_phys(cs, addr + port / 8) & (mask << (port & 7))) { +            /* next env->eip */ +            x86_stq_phys(cs, +                     env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), +                     env->eip + next_eip_addend); +            helper_vmexit(env, SVM_EXIT_IOIO, param | (port << 16)); +        } +    } +} + +/* Note: currently only 32 bits of exit_code are used */ +void helper_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1) +{ +    CPUState *cs = CPU(x86_env_get_cpu(env)); +    uint32_t int_ctl; + +    qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmexit(%08x, %016" PRIx64 ", %016" +                  PRIx64 ", " TARGET_FMT_lx ")!\n", +                  exit_code, exit_info_1, +                  x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, +                                                   control.exit_info_2)), +                  env->eip); + +    if (env->hflags & HF_INHIBIT_IRQ_MASK) { +        x86_stl_phys(cs, +                 env->vm_vmcb + offsetof(struct vmcb, control.int_state), +                 SVM_INTERRUPT_SHADOW_MASK); +        env->hflags &= ~HF_INHIBIT_IRQ_MASK; +    } else { +        x86_stl_phys(cs, +                 env->vm_vmcb + offsetof(struct vmcb, control.int_state), 0); +    } + +    /* Save the VM state in the vmcb */ +    svm_save_seg(env, env->vm_vmcb + offsetof(struct vmcb, save.es), +                 &env->segs[R_ES]); +    svm_save_seg(env, env->vm_vmcb + offsetof(struct vmcb, save.cs), +                 &env->segs[R_CS]); +    svm_save_seg(env, env->vm_vmcb + offsetof(struct vmcb, save.ss), +                 &env->segs[R_SS]); +    svm_save_seg(env, env->vm_vmcb + offsetof(struct vmcb, save.ds), +                 &env->segs[R_DS]); + +    x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.gdtr.base), +             env->gdt.base); +    x86_stl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.gdtr.limit), +             env->gdt.limit); + +    x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.idtr.base), +             env->idt.base); +    x86_stl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.idtr.limit), +             env->idt.limit); + +    x86_stq_phys(cs, +             env->vm_vmcb + offsetof(struct vmcb, save.efer), env->efer); +    x86_stq_phys(cs, +             env->vm_vmcb + offsetof(struct vmcb, save.cr0), env->cr[0]); +    x86_stq_phys(cs, +             env->vm_vmcb + offsetof(struct vmcb, save.cr2), env->cr[2]); +    x86_stq_phys(cs, +             env->vm_vmcb + offsetof(struct vmcb, save.cr3), env->cr[3]); +    x86_stq_phys(cs, +             env->vm_vmcb + offsetof(struct vmcb, save.cr4), env->cr[4]); + +    int_ctl = x86_ldl_phys(cs, +                       env->vm_vmcb + offsetof(struct vmcb, control.int_ctl)); +    int_ctl &= ~(V_TPR_MASK | V_IRQ_MASK); +    int_ctl |= env->v_tpr & V_TPR_MASK; +    if (cs->interrupt_request & CPU_INTERRUPT_VIRQ) { +        int_ctl |= V_IRQ_MASK; +    } +    x86_stl_phys(cs, +             env->vm_vmcb + offsetof(struct vmcb, control.int_ctl), int_ctl); + +    x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.rflags), +             cpu_compute_eflags(env)); +    x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.rip), +             env->eip); +    x86_stq_phys(cs, +             env->vm_vmcb + offsetof(struct vmcb, save.rsp), env->regs[R_ESP]); +    x86_stq_phys(cs, +             env->vm_vmcb + offsetof(struct vmcb, save.rax), env->regs[R_EAX]); +    x86_stq_phys(cs, +             env->vm_vmcb + offsetof(struct vmcb, save.dr7), env->dr[7]); +    x86_stq_phys(cs, +             env->vm_vmcb + offsetof(struct vmcb, save.dr6), env->dr[6]); +    x86_stb_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.cpl), +             env->hflags & HF_CPL_MASK); + +    /* Reload the host state from vm_hsave */ +    env->hflags2 &= ~(HF2_HIF_MASK | HF2_VINTR_MASK); +    env->hflags &= ~HF_SVMI_MASK; +    env->intercept = 0; +    env->intercept_exceptions = 0; +    cs->interrupt_request &= ~CPU_INTERRUPT_VIRQ; +    env->tsc_offset = 0; + +    env->gdt.base  = x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, +                                                       save.gdtr.base)); +    env->gdt.limit = x86_ldl_phys(cs, env->vm_hsave + offsetof(struct vmcb, +                                                       save.gdtr.limit)); + +    env->idt.base  = x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, +                                                       save.idtr.base)); +    env->idt.limit = x86_ldl_phys(cs, env->vm_hsave + offsetof(struct vmcb, +                                                       save.idtr.limit)); + +    cpu_x86_update_cr0(env, x86_ldq_phys(cs, +                                     env->vm_hsave + offsetof(struct vmcb, +                                                              save.cr0)) | +                       CR0_PE_MASK); +    cpu_x86_update_cr4(env, x86_ldq_phys(cs, +                                     env->vm_hsave + offsetof(struct vmcb, +                                                              save.cr4))); +    cpu_x86_update_cr3(env, x86_ldq_phys(cs, +                                     env->vm_hsave + offsetof(struct vmcb, +                                                              save.cr3))); +    /* we need to set the efer after the crs so the hidden flags get +       set properly */ +    cpu_load_efer(env, x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, +                                                         save.efer))); +    env->eflags = 0; +    cpu_load_eflags(env, x86_ldq_phys(cs, +                                  env->vm_hsave + offsetof(struct vmcb, +                                                           save.rflags)), +                    ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK | +                      VM_MASK)); + +    svm_load_seg_cache(env, env->vm_hsave + offsetof(struct vmcb, save.es), +                       R_ES); +    svm_load_seg_cache(env, env->vm_hsave + offsetof(struct vmcb, save.cs), +                       R_CS); +    svm_load_seg_cache(env, env->vm_hsave + offsetof(struct vmcb, save.ss), +                       R_SS); +    svm_load_seg_cache(env, env->vm_hsave + offsetof(struct vmcb, save.ds), +                       R_DS); + +    env->eip = x86_ldq_phys(cs, +                        env->vm_hsave + offsetof(struct vmcb, save.rip)); +    env->regs[R_ESP] = x86_ldq_phys(cs, env->vm_hsave + +                                offsetof(struct vmcb, save.rsp)); +    env->regs[R_EAX] = x86_ldq_phys(cs, env->vm_hsave + +                                offsetof(struct vmcb, save.rax)); + +    env->dr[6] = x86_ldq_phys(cs, +                          env->vm_hsave + offsetof(struct vmcb, save.dr6)); +    env->dr[7] = x86_ldq_phys(cs, +                          env->vm_hsave + offsetof(struct vmcb, save.dr7)); + +    /* other setups */ +    x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.exit_code), +             exit_code); +    x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.exit_info_1), +             exit_info_1); + +    x86_stl_phys(cs, +             env->vm_vmcb + offsetof(struct vmcb, control.exit_int_info), +             x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, +                                              control.event_inj))); +    x86_stl_phys(cs, +             env->vm_vmcb + offsetof(struct vmcb, control.exit_int_info_err), +             x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, +                                              control.event_inj_err))); +    x86_stl_phys(cs, +             env->vm_vmcb + offsetof(struct vmcb, control.event_inj), 0); + +    env->hflags2 &= ~HF2_GIF_MASK; +    /* FIXME: Resets the current ASID register to zero (host ASID). */ + +    /* Clears the V_IRQ and V_INTR_MASKING bits inside the processor. */ + +    /* Clears the TSC_OFFSET inside the processor. */ + +    /* If the host is in PAE mode, the processor reloads the host's PDPEs +       from the page table indicated the host's CR3. If the PDPEs contain +       illegal state, the processor causes a shutdown. */ + +    /* Disables all breakpoints in the host DR7 register. */ + +    /* Checks the reloaded host state for consistency. */ + +    /* If the host's rIP reloaded by #VMEXIT is outside the limit of the +       host's code segment or non-canonical (in the case of long mode), a +       #GP fault is delivered inside the host. */ + +    /* remove any pending exception */ +    cs->exception_index = -1; +    env->error_code = 0; +    env->old_exception = -1; + +    cpu_loop_exit(cs); +} + +void cpu_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1) +{ +    helper_vmexit(env, exit_code, exit_info_1); +} + +#endif diff --git a/target-i386/translate.c b/target-i386/translate.c new file mode 100644 index 00000000..82e2245b --- /dev/null +++ b/target-i386/translate.c @@ -0,0 +1,8139 @@ +/* + *  i386 translation + * + *  Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> +#include <signal.h> + +#include "qemu/host-utils.h" +#include "cpu.h" +#include "disas/disas.h" +#include "tcg-op.h" +#include "exec/cpu_ldst.h" + +#include "exec/helper-proto.h" +#include "exec/helper-gen.h" + +#include "trace-tcg.h" + + +#define PREFIX_REPZ   0x01 +#define PREFIX_REPNZ  0x02 +#define PREFIX_LOCK   0x04 +#define PREFIX_DATA   0x08 +#define PREFIX_ADR    0x10 +#define PREFIX_VEX    0x20 + +#ifdef TARGET_X86_64 +#define CODE64(s) ((s)->code64) +#define REX_X(s) ((s)->rex_x) +#define REX_B(s) ((s)->rex_b) +#else +#define CODE64(s) 0 +#define REX_X(s) 0 +#define REX_B(s) 0 +#endif + +#ifdef TARGET_X86_64 +# define ctztl  ctz64 +# define clztl  clz64 +#else +# define ctztl  ctz32 +# define clztl  clz32 +#endif + +//#define MACRO_TEST   1 + +/* global register indexes */ +static TCGv_ptr cpu_env; +static TCGv cpu_A0; +static TCGv cpu_cc_dst, cpu_cc_src, cpu_cc_src2, cpu_cc_srcT; +static TCGv_i32 cpu_cc_op; +static TCGv cpu_regs[CPU_NB_REGS]; +/* local temps */ +static TCGv cpu_T[2]; +/* local register indexes (only used inside old micro ops) */ +static TCGv cpu_tmp0, cpu_tmp4; +static TCGv_ptr cpu_ptr0, cpu_ptr1; +static TCGv_i32 cpu_tmp2_i32, cpu_tmp3_i32; +static TCGv_i64 cpu_tmp1_i64; + +static uint8_t gen_opc_cc_op[OPC_BUF_SIZE]; + +#include "exec/gen-icount.h" + +#ifdef TARGET_X86_64 +static int x86_64_hregs; +#endif + +typedef struct DisasContext { +    /* current insn context */ +    int override; /* -1 if no override */ +    int prefix; +    TCGMemOp aflag; +    TCGMemOp dflag; +    target_ulong pc; /* pc = eip + cs_base */ +    int is_jmp; /* 1 = means jump (stop translation), 2 means CPU +                   static state change (stop translation) */ +    /* current block context */ +    target_ulong cs_base; /* base of CS segment */ +    int pe;     /* protected mode */ +    int code32; /* 32 bit code segment */ +#ifdef TARGET_X86_64 +    int lma;    /* long mode active */ +    int code64; /* 64 bit code segment */ +    int rex_x, rex_b; +#endif +    int vex_l;  /* vex vector length */ +    int vex_v;  /* vex vvvv register, without 1's compliment.  */ +    int ss32;   /* 32 bit stack segment */ +    CCOp cc_op;  /* current CC operation */ +    bool cc_op_dirty; +    int addseg; /* non zero if either DS/ES/SS have a non zero base */ +    int f_st;   /* currently unused */ +    int vm86;   /* vm86 mode */ +    int cpl; +    int iopl; +    int tf;     /* TF cpu flag */ +    int singlestep_enabled; /* "hardware" single step enabled */ +    int jmp_opt; /* use direct block chaining for direct jumps */ +    int repz_opt; /* optimize jumps within repz instructions */ +    int mem_index; /* select memory access functions */ +    uint64_t flags; /* all execution flags */ +    struct TranslationBlock *tb; +    int popl_esp_hack; /* for correct popl with esp base handling */ +    int rip_offset; /* only used in x86_64, but left for simplicity */ +    int cpuid_features; +    int cpuid_ext_features; +    int cpuid_ext2_features; +    int cpuid_ext3_features; +    int cpuid_7_0_ebx_features; +} DisasContext; + +static void gen_eob(DisasContext *s); +static void gen_jmp(DisasContext *s, target_ulong eip); +static void gen_jmp_tb(DisasContext *s, target_ulong eip, int tb_num); +static void gen_op(DisasContext *s1, int op, TCGMemOp ot, int d); + +/* i386 arith/logic operations */ +enum { +    OP_ADDL, +    OP_ORL, +    OP_ADCL, +    OP_SBBL, +    OP_ANDL, +    OP_SUBL, +    OP_XORL, +    OP_CMPL, +}; + +/* i386 shift ops */ +enum { +    OP_ROL, +    OP_ROR, +    OP_RCL, +    OP_RCR, +    OP_SHL, +    OP_SHR, +    OP_SHL1, /* undocumented */ +    OP_SAR = 7, +}; + +enum { +    JCC_O, +    JCC_B, +    JCC_Z, +    JCC_BE, +    JCC_S, +    JCC_P, +    JCC_L, +    JCC_LE, +}; + +enum { +    /* I386 int registers */ +    OR_EAX,   /* MUST be even numbered */ +    OR_ECX, +    OR_EDX, +    OR_EBX, +    OR_ESP, +    OR_EBP, +    OR_ESI, +    OR_EDI, + +    OR_TMP0 = 16,    /* temporary operand register */ +    OR_TMP1, +    OR_A0, /* temporary register used when doing address evaluation */ +}; + +enum { +    USES_CC_DST  = 1, +    USES_CC_SRC  = 2, +    USES_CC_SRC2 = 4, +    USES_CC_SRCT = 8, +}; + +/* Bit set if the global variable is live after setting CC_OP to X.  */ +static const uint8_t cc_op_live[CC_OP_NB] = { +    [CC_OP_DYNAMIC] = USES_CC_DST | USES_CC_SRC | USES_CC_SRC2, +    [CC_OP_EFLAGS] = USES_CC_SRC, +    [CC_OP_MULB ... CC_OP_MULQ] = USES_CC_DST | USES_CC_SRC, +    [CC_OP_ADDB ... CC_OP_ADDQ] = USES_CC_DST | USES_CC_SRC, +    [CC_OP_ADCB ... CC_OP_ADCQ] = USES_CC_DST | USES_CC_SRC | USES_CC_SRC2, +    [CC_OP_SUBB ... CC_OP_SUBQ] = USES_CC_DST | USES_CC_SRC | USES_CC_SRCT, +    [CC_OP_SBBB ... CC_OP_SBBQ] = USES_CC_DST | USES_CC_SRC | USES_CC_SRC2, +    [CC_OP_LOGICB ... CC_OP_LOGICQ] = USES_CC_DST, +    [CC_OP_INCB ... CC_OP_INCQ] = USES_CC_DST | USES_CC_SRC, +    [CC_OP_DECB ... CC_OP_DECQ] = USES_CC_DST | USES_CC_SRC, +    [CC_OP_SHLB ... CC_OP_SHLQ] = USES_CC_DST | USES_CC_SRC, +    [CC_OP_SARB ... CC_OP_SARQ] = USES_CC_DST | USES_CC_SRC, +    [CC_OP_BMILGB ... CC_OP_BMILGQ] = USES_CC_DST | USES_CC_SRC, +    [CC_OP_ADCX] = USES_CC_DST | USES_CC_SRC, +    [CC_OP_ADOX] = USES_CC_SRC | USES_CC_SRC2, +    [CC_OP_ADCOX] = USES_CC_DST | USES_CC_SRC | USES_CC_SRC2, +    [CC_OP_CLR] = 0, +}; + +static void set_cc_op(DisasContext *s, CCOp op) +{ +    int dead; + +    if (s->cc_op == op) { +        return; +    } + +    /* Discard CC computation that will no longer be used.  */ +    dead = cc_op_live[s->cc_op] & ~cc_op_live[op]; +    if (dead & USES_CC_DST) { +        tcg_gen_discard_tl(cpu_cc_dst); +    } +    if (dead & USES_CC_SRC) { +        tcg_gen_discard_tl(cpu_cc_src); +    } +    if (dead & USES_CC_SRC2) { +        tcg_gen_discard_tl(cpu_cc_src2); +    } +    if (dead & USES_CC_SRCT) { +        tcg_gen_discard_tl(cpu_cc_srcT); +    } + +    if (op == CC_OP_DYNAMIC) { +        /* The DYNAMIC setting is translator only, and should never be +           stored.  Thus we always consider it clean.  */ +        s->cc_op_dirty = false; +    } else { +        /* Discard any computed CC_OP value (see shifts).  */ +        if (s->cc_op == CC_OP_DYNAMIC) { +            tcg_gen_discard_i32(cpu_cc_op); +        } +        s->cc_op_dirty = true; +    } +    s->cc_op = op; +} + +static void gen_update_cc_op(DisasContext *s) +{ +    if (s->cc_op_dirty) { +        tcg_gen_movi_i32(cpu_cc_op, s->cc_op); +        s->cc_op_dirty = false; +    } +} + +#ifdef TARGET_X86_64 + +#define NB_OP_SIZES 4 + +#else /* !TARGET_X86_64 */ + +#define NB_OP_SIZES 3 + +#endif /* !TARGET_X86_64 */ + +#if defined(HOST_WORDS_BIGENDIAN) +#define REG_B_OFFSET (sizeof(target_ulong) - 1) +#define REG_H_OFFSET (sizeof(target_ulong) - 2) +#define REG_W_OFFSET (sizeof(target_ulong) - 2) +#define REG_L_OFFSET (sizeof(target_ulong) - 4) +#define REG_LH_OFFSET (sizeof(target_ulong) - 8) +#else +#define REG_B_OFFSET 0 +#define REG_H_OFFSET 1 +#define REG_W_OFFSET 0 +#define REG_L_OFFSET 0 +#define REG_LH_OFFSET 4 +#endif + +/* In instruction encodings for byte register accesses the + * register number usually indicates "low 8 bits of register N"; + * however there are some special cases where N 4..7 indicates + * [AH, CH, DH, BH], ie "bits 15..8 of register N-4". Return + * true for this special case, false otherwise. + */ +static inline bool byte_reg_is_xH(int reg) +{ +    if (reg < 4) { +        return false; +    } +#ifdef TARGET_X86_64 +    if (reg >= 8 || x86_64_hregs) { +        return false; +    } +#endif +    return true; +} + +/* Select the size of a push/pop operation.  */ +static inline TCGMemOp mo_pushpop(DisasContext *s, TCGMemOp ot) +{ +    if (CODE64(s)) { +        return ot == MO_16 ? MO_16 : MO_64; +    } else { +        return ot; +    } +} + +/* Select only size 64 else 32.  Used for SSE operand sizes.  */ +static inline TCGMemOp mo_64_32(TCGMemOp ot) +{ +#ifdef TARGET_X86_64 +    return ot == MO_64 ? MO_64 : MO_32; +#else +    return MO_32; +#endif +} + +/* Select size 8 if lsb of B is clear, else OT.  Used for decoding +   byte vs word opcodes.  */ +static inline TCGMemOp mo_b_d(int b, TCGMemOp ot) +{ +    return b & 1 ? ot : MO_8; +} + +/* Select size 8 if lsb of B is clear, else OT capped at 32. +   Used for decoding operand size of port opcodes.  */ +static inline TCGMemOp mo_b_d32(int b, TCGMemOp ot) +{ +    return b & 1 ? (ot == MO_16 ? MO_16 : MO_32) : MO_8; +} + +static void gen_op_mov_reg_v(TCGMemOp ot, int reg, TCGv t0) +{ +    switch(ot) { +    case MO_8: +        if (!byte_reg_is_xH(reg)) { +            tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], t0, 0, 8); +        } else { +            tcg_gen_deposit_tl(cpu_regs[reg - 4], cpu_regs[reg - 4], t0, 8, 8); +        } +        break; +    case MO_16: +        tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], t0, 0, 16); +        break; +    case MO_32: +        /* For x86_64, this sets the higher half of register to zero. +           For i386, this is equivalent to a mov. */ +        tcg_gen_ext32u_tl(cpu_regs[reg], t0); +        break; +#ifdef TARGET_X86_64 +    case MO_64: +        tcg_gen_mov_tl(cpu_regs[reg], t0); +        break; +#endif +    default: +        tcg_abort(); +    } +} + +static inline void gen_op_mov_v_reg(TCGMemOp ot, TCGv t0, int reg) +{ +    if (ot == MO_8 && byte_reg_is_xH(reg)) { +        tcg_gen_shri_tl(t0, cpu_regs[reg - 4], 8); +        tcg_gen_ext8u_tl(t0, t0); +    } else { +        tcg_gen_mov_tl(t0, cpu_regs[reg]); +    } +} + +static inline void gen_op_movl_A0_reg(int reg) +{ +    tcg_gen_mov_tl(cpu_A0, cpu_regs[reg]); +} + +static inline void gen_op_addl_A0_im(int32_t val) +{ +    tcg_gen_addi_tl(cpu_A0, cpu_A0, val); +#ifdef TARGET_X86_64 +    tcg_gen_andi_tl(cpu_A0, cpu_A0, 0xffffffff); +#endif +} + +#ifdef TARGET_X86_64 +static inline void gen_op_addq_A0_im(int64_t val) +{ +    tcg_gen_addi_tl(cpu_A0, cpu_A0, val); +} +#endif +     +static void gen_add_A0_im(DisasContext *s, int val) +{ +#ifdef TARGET_X86_64 +    if (CODE64(s)) +        gen_op_addq_A0_im(val); +    else +#endif +        gen_op_addl_A0_im(val); +} + +static inline void gen_op_jmp_v(TCGv dest) +{ +    tcg_gen_st_tl(dest, cpu_env, offsetof(CPUX86State, eip)); +} + +static inline void gen_op_add_reg_im(TCGMemOp size, int reg, int32_t val) +{ +    tcg_gen_addi_tl(cpu_tmp0, cpu_regs[reg], val); +    gen_op_mov_reg_v(size, reg, cpu_tmp0); +} + +static inline void gen_op_add_reg_T0(TCGMemOp size, int reg) +{ +    tcg_gen_add_tl(cpu_tmp0, cpu_regs[reg], cpu_T[0]); +    gen_op_mov_reg_v(size, reg, cpu_tmp0); +} + +static inline void gen_op_addl_A0_reg_sN(int shift, int reg) +{ +    tcg_gen_mov_tl(cpu_tmp0, cpu_regs[reg]); +    if (shift != 0) +        tcg_gen_shli_tl(cpu_tmp0, cpu_tmp0, shift); +    tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0); +    /* For x86_64, this sets the higher half of register to zero. +       For i386, this is equivalent to a nop. */ +    tcg_gen_ext32u_tl(cpu_A0, cpu_A0); +} + +static inline void gen_op_movl_A0_seg(int reg) +{ +    tcg_gen_ld32u_tl(cpu_A0, cpu_env, offsetof(CPUX86State, segs[reg].base) + REG_L_OFFSET); +} + +static inline void gen_op_addl_A0_seg(DisasContext *s, int reg) +{ +    tcg_gen_ld_tl(cpu_tmp0, cpu_env, offsetof(CPUX86State, segs[reg].base)); +#ifdef TARGET_X86_64 +    if (CODE64(s)) { +        tcg_gen_andi_tl(cpu_A0, cpu_A0, 0xffffffff); +        tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0); +    } else { +        tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0); +        tcg_gen_andi_tl(cpu_A0, cpu_A0, 0xffffffff); +    } +#else +    tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0); +#endif +} + +#ifdef TARGET_X86_64 +static inline void gen_op_movq_A0_seg(int reg) +{ +    tcg_gen_ld_tl(cpu_A0, cpu_env, offsetof(CPUX86State, segs[reg].base)); +} + +static inline void gen_op_addq_A0_seg(int reg) +{ +    tcg_gen_ld_tl(cpu_tmp0, cpu_env, offsetof(CPUX86State, segs[reg].base)); +    tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0); +} + +static inline void gen_op_movq_A0_reg(int reg) +{ +    tcg_gen_mov_tl(cpu_A0, cpu_regs[reg]); +} + +static inline void gen_op_addq_A0_reg_sN(int shift, int reg) +{ +    tcg_gen_mov_tl(cpu_tmp0, cpu_regs[reg]); +    if (shift != 0) +        tcg_gen_shli_tl(cpu_tmp0, cpu_tmp0, shift); +    tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0); +} +#endif + +static inline void gen_op_ld_v(DisasContext *s, int idx, TCGv t0, TCGv a0) +{ +    tcg_gen_qemu_ld_tl(t0, a0, s->mem_index, idx | MO_LE); +} + +static inline void gen_op_st_v(DisasContext *s, int idx, TCGv t0, TCGv a0) +{ +    tcg_gen_qemu_st_tl(t0, a0, s->mem_index, idx | MO_LE); +} + +static inline void gen_op_st_rm_T0_A0(DisasContext *s, int idx, int d) +{ +    if (d == OR_TMP0) { +        gen_op_st_v(s, idx, cpu_T[0], cpu_A0); +    } else { +        gen_op_mov_reg_v(idx, d, cpu_T[0]); +    } +} + +static inline void gen_jmp_im(target_ulong pc) +{ +    tcg_gen_movi_tl(cpu_tmp0, pc); +    gen_op_jmp_v(cpu_tmp0); +} + +static inline void gen_string_movl_A0_ESI(DisasContext *s) +{ +    int override; + +    override = s->override; +    switch (s->aflag) { +#ifdef TARGET_X86_64 +    case MO_64: +        if (override >= 0) { +            gen_op_movq_A0_seg(override); +            gen_op_addq_A0_reg_sN(0, R_ESI); +        } else { +            gen_op_movq_A0_reg(R_ESI); +        } +        break; +#endif +    case MO_32: +        /* 32 bit address */ +        if (s->addseg && override < 0) +            override = R_DS; +        if (override >= 0) { +            gen_op_movl_A0_seg(override); +            gen_op_addl_A0_reg_sN(0, R_ESI); +        } else { +            gen_op_movl_A0_reg(R_ESI); +        } +        break; +    case MO_16: +        /* 16 address, always override */ +        if (override < 0) +            override = R_DS; +        tcg_gen_ext16u_tl(cpu_A0, cpu_regs[R_ESI]); +        gen_op_addl_A0_seg(s, override); +        break; +    default: +        tcg_abort(); +    } +} + +static inline void gen_string_movl_A0_EDI(DisasContext *s) +{ +    switch (s->aflag) { +#ifdef TARGET_X86_64 +    case MO_64: +        gen_op_movq_A0_reg(R_EDI); +        break; +#endif +    case MO_32: +        if (s->addseg) { +            gen_op_movl_A0_seg(R_ES); +            gen_op_addl_A0_reg_sN(0, R_EDI); +        } else { +            gen_op_movl_A0_reg(R_EDI); +        } +        break; +    case MO_16: +        tcg_gen_ext16u_tl(cpu_A0, cpu_regs[R_EDI]); +        gen_op_addl_A0_seg(s, R_ES); +        break; +    default: +        tcg_abort(); +    } +} + +static inline void gen_op_movl_T0_Dshift(TCGMemOp ot) +{ +    tcg_gen_ld32s_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, df)); +    tcg_gen_shli_tl(cpu_T[0], cpu_T[0], ot); +}; + +static TCGv gen_ext_tl(TCGv dst, TCGv src, TCGMemOp size, bool sign) +{ +    switch (size) { +    case MO_8: +        if (sign) { +            tcg_gen_ext8s_tl(dst, src); +        } else { +            tcg_gen_ext8u_tl(dst, src); +        } +        return dst; +    case MO_16: +        if (sign) { +            tcg_gen_ext16s_tl(dst, src); +        } else { +            tcg_gen_ext16u_tl(dst, src); +        } +        return dst; +#ifdef TARGET_X86_64 +    case MO_32: +        if (sign) { +            tcg_gen_ext32s_tl(dst, src); +        } else { +            tcg_gen_ext32u_tl(dst, src); +        } +        return dst; +#endif +    default: +        return src; +    } +} + +static void gen_extu(TCGMemOp ot, TCGv reg) +{ +    gen_ext_tl(reg, reg, ot, false); +} + +static void gen_exts(TCGMemOp ot, TCGv reg) +{ +    gen_ext_tl(reg, reg, ot, true); +} + +static inline void gen_op_jnz_ecx(TCGMemOp size, TCGLabel *label1) +{ +    tcg_gen_mov_tl(cpu_tmp0, cpu_regs[R_ECX]); +    gen_extu(size, cpu_tmp0); +    tcg_gen_brcondi_tl(TCG_COND_NE, cpu_tmp0, 0, label1); +} + +static inline void gen_op_jz_ecx(TCGMemOp size, TCGLabel *label1) +{ +    tcg_gen_mov_tl(cpu_tmp0, cpu_regs[R_ECX]); +    gen_extu(size, cpu_tmp0); +    tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_tmp0, 0, label1); +} + +static void gen_helper_in_func(TCGMemOp ot, TCGv v, TCGv_i32 n) +{ +    switch (ot) { +    case MO_8: +        gen_helper_inb(v, cpu_env, n); +        break; +    case MO_16: +        gen_helper_inw(v, cpu_env, n); +        break; +    case MO_32: +        gen_helper_inl(v, cpu_env, n); +        break; +    default: +        tcg_abort(); +    } +} + +static void gen_helper_out_func(TCGMemOp ot, TCGv_i32 v, TCGv_i32 n) +{ +    switch (ot) { +    case MO_8: +        gen_helper_outb(cpu_env, v, n); +        break; +    case MO_16: +        gen_helper_outw(cpu_env, v, n); +        break; +    case MO_32: +        gen_helper_outl(cpu_env, v, n); +        break; +    default: +        tcg_abort(); +    } +} + +static void gen_check_io(DisasContext *s, TCGMemOp ot, target_ulong cur_eip, +                         uint32_t svm_flags) +{ +    int state_saved; +    target_ulong next_eip; + +    state_saved = 0; +    if (s->pe && (s->cpl > s->iopl || s->vm86)) { +        gen_update_cc_op(s); +        gen_jmp_im(cur_eip); +        state_saved = 1; +        tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); +        switch (ot) { +        case MO_8: +            gen_helper_check_iob(cpu_env, cpu_tmp2_i32); +            break; +        case MO_16: +            gen_helper_check_iow(cpu_env, cpu_tmp2_i32); +            break; +        case MO_32: +            gen_helper_check_iol(cpu_env, cpu_tmp2_i32); +            break; +        default: +            tcg_abort(); +        } +    } +    if(s->flags & HF_SVMI_MASK) { +        if (!state_saved) { +            gen_update_cc_op(s); +            gen_jmp_im(cur_eip); +        } +        svm_flags |= (1 << (4 + ot)); +        next_eip = s->pc - s->cs_base; +        tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); +        gen_helper_svm_check_io(cpu_env, cpu_tmp2_i32, +                                tcg_const_i32(svm_flags), +                                tcg_const_i32(next_eip - cur_eip)); +    } +} + +static inline void gen_movs(DisasContext *s, TCGMemOp ot) +{ +    gen_string_movl_A0_ESI(s); +    gen_op_ld_v(s, ot, cpu_T[0], cpu_A0); +    gen_string_movl_A0_EDI(s); +    gen_op_st_v(s, ot, cpu_T[0], cpu_A0); +    gen_op_movl_T0_Dshift(ot); +    gen_op_add_reg_T0(s->aflag, R_ESI); +    gen_op_add_reg_T0(s->aflag, R_EDI); +} + +static void gen_op_update1_cc(void) +{ +    tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); +} + +static void gen_op_update2_cc(void) +{ +    tcg_gen_mov_tl(cpu_cc_src, cpu_T[1]); +    tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); +} + +static void gen_op_update3_cc(TCGv reg) +{ +    tcg_gen_mov_tl(cpu_cc_src2, reg); +    tcg_gen_mov_tl(cpu_cc_src, cpu_T[1]); +    tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); +} + +static inline void gen_op_testl_T0_T1_cc(void) +{ +    tcg_gen_and_tl(cpu_cc_dst, cpu_T[0], cpu_T[1]); +} + +static void gen_op_update_neg_cc(void) +{ +    tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); +    tcg_gen_neg_tl(cpu_cc_src, cpu_T[0]); +    tcg_gen_movi_tl(cpu_cc_srcT, 0); +} + +/* compute all eflags to cc_src */ +static void gen_compute_eflags(DisasContext *s) +{ +    TCGv zero, dst, src1, src2; +    int live, dead; + +    if (s->cc_op == CC_OP_EFLAGS) { +        return; +    } +    if (s->cc_op == CC_OP_CLR) { +        tcg_gen_movi_tl(cpu_cc_src, CC_Z | CC_P); +        set_cc_op(s, CC_OP_EFLAGS); +        return; +    } + +    TCGV_UNUSED(zero); +    dst = cpu_cc_dst; +    src1 = cpu_cc_src; +    src2 = cpu_cc_src2; + +    /* Take care to not read values that are not live.  */ +    live = cc_op_live[s->cc_op] & ~USES_CC_SRCT; +    dead = live ^ (USES_CC_DST | USES_CC_SRC | USES_CC_SRC2); +    if (dead) { +        zero = tcg_const_tl(0); +        if (dead & USES_CC_DST) { +            dst = zero; +        } +        if (dead & USES_CC_SRC) { +            src1 = zero; +        } +        if (dead & USES_CC_SRC2) { +            src2 = zero; +        } +    } + +    gen_update_cc_op(s); +    gen_helper_cc_compute_all(cpu_cc_src, dst, src1, src2, cpu_cc_op); +    set_cc_op(s, CC_OP_EFLAGS); + +    if (dead) { +        tcg_temp_free(zero); +    } +} + +typedef struct CCPrepare { +    TCGCond cond; +    TCGv reg; +    TCGv reg2; +    target_ulong imm; +    target_ulong mask; +    bool use_reg2; +    bool no_setcond; +} CCPrepare; + +/* compute eflags.C to reg */ +static CCPrepare gen_prepare_eflags_c(DisasContext *s, TCGv reg) +{ +    TCGv t0, t1; +    int size, shift; + +    switch (s->cc_op) { +    case CC_OP_SUBB ... CC_OP_SUBQ: +        /* (DATA_TYPE)CC_SRCT < (DATA_TYPE)CC_SRC */ +        size = s->cc_op - CC_OP_SUBB; +        t1 = gen_ext_tl(cpu_tmp0, cpu_cc_src, size, false); +        /* If no temporary was used, be careful not to alias t1 and t0.  */ +        t0 = TCGV_EQUAL(t1, cpu_cc_src) ? cpu_tmp0 : reg; +        tcg_gen_mov_tl(t0, cpu_cc_srcT); +        gen_extu(size, t0); +        goto add_sub; + +    case CC_OP_ADDB ... CC_OP_ADDQ: +        /* (DATA_TYPE)CC_DST < (DATA_TYPE)CC_SRC */ +        size = s->cc_op - CC_OP_ADDB; +        t1 = gen_ext_tl(cpu_tmp0, cpu_cc_src, size, false); +        t0 = gen_ext_tl(reg, cpu_cc_dst, size, false); +    add_sub: +        return (CCPrepare) { .cond = TCG_COND_LTU, .reg = t0, +                             .reg2 = t1, .mask = -1, .use_reg2 = true }; + +    case CC_OP_LOGICB ... CC_OP_LOGICQ: +    case CC_OP_CLR: +        return (CCPrepare) { .cond = TCG_COND_NEVER, .mask = -1 }; + +    case CC_OP_INCB ... CC_OP_INCQ: +    case CC_OP_DECB ... CC_OP_DECQ: +        return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, +                             .mask = -1, .no_setcond = true }; + +    case CC_OP_SHLB ... CC_OP_SHLQ: +        /* (CC_SRC >> (DATA_BITS - 1)) & 1 */ +        size = s->cc_op - CC_OP_SHLB; +        shift = (8 << size) - 1; +        return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, +                             .mask = (target_ulong)1 << shift }; + +    case CC_OP_MULB ... CC_OP_MULQ: +        return (CCPrepare) { .cond = TCG_COND_NE, +                             .reg = cpu_cc_src, .mask = -1 }; + +    case CC_OP_BMILGB ... CC_OP_BMILGQ: +        size = s->cc_op - CC_OP_BMILGB; +        t0 = gen_ext_tl(reg, cpu_cc_src, size, false); +        return (CCPrepare) { .cond = TCG_COND_EQ, .reg = t0, .mask = -1 }; + +    case CC_OP_ADCX: +    case CC_OP_ADCOX: +        return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_dst, +                             .mask = -1, .no_setcond = true }; + +    case CC_OP_EFLAGS: +    case CC_OP_SARB ... CC_OP_SARQ: +        /* CC_SRC & 1 */ +        return (CCPrepare) { .cond = TCG_COND_NE, +                             .reg = cpu_cc_src, .mask = CC_C }; + +    default: +       /* The need to compute only C from CC_OP_DYNAMIC is important +          in efficiently implementing e.g. INC at the start of a TB.  */ +       gen_update_cc_op(s); +       gen_helper_cc_compute_c(reg, cpu_cc_dst, cpu_cc_src, +                               cpu_cc_src2, cpu_cc_op); +       return (CCPrepare) { .cond = TCG_COND_NE, .reg = reg, +                            .mask = -1, .no_setcond = true }; +    } +} + +/* compute eflags.P to reg */ +static CCPrepare gen_prepare_eflags_p(DisasContext *s, TCGv reg) +{ +    gen_compute_eflags(s); +    return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, +                         .mask = CC_P }; +} + +/* compute eflags.S to reg */ +static CCPrepare gen_prepare_eflags_s(DisasContext *s, TCGv reg) +{ +    switch (s->cc_op) { +    case CC_OP_DYNAMIC: +        gen_compute_eflags(s); +        /* FALLTHRU */ +    case CC_OP_EFLAGS: +    case CC_OP_ADCX: +    case CC_OP_ADOX: +    case CC_OP_ADCOX: +        return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, +                             .mask = CC_S }; +    case CC_OP_CLR: +        return (CCPrepare) { .cond = TCG_COND_NEVER, .mask = -1 }; +    default: +        { +            TCGMemOp size = (s->cc_op - CC_OP_ADDB) & 3; +            TCGv t0 = gen_ext_tl(reg, cpu_cc_dst, size, true); +            return (CCPrepare) { .cond = TCG_COND_LT, .reg = t0, .mask = -1 }; +        } +    } +} + +/* compute eflags.O to reg */ +static CCPrepare gen_prepare_eflags_o(DisasContext *s, TCGv reg) +{ +    switch (s->cc_op) { +    case CC_OP_ADOX: +    case CC_OP_ADCOX: +        return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src2, +                             .mask = -1, .no_setcond = true }; +    case CC_OP_CLR: +        return (CCPrepare) { .cond = TCG_COND_NEVER, .mask = -1 }; +    default: +        gen_compute_eflags(s); +        return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, +                             .mask = CC_O }; +    } +} + +/* compute eflags.Z to reg */ +static CCPrepare gen_prepare_eflags_z(DisasContext *s, TCGv reg) +{ +    switch (s->cc_op) { +    case CC_OP_DYNAMIC: +        gen_compute_eflags(s); +        /* FALLTHRU */ +    case CC_OP_EFLAGS: +    case CC_OP_ADCX: +    case CC_OP_ADOX: +    case CC_OP_ADCOX: +        return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, +                             .mask = CC_Z }; +    case CC_OP_CLR: +        return (CCPrepare) { .cond = TCG_COND_ALWAYS, .mask = -1 }; +    default: +        { +            TCGMemOp size = (s->cc_op - CC_OP_ADDB) & 3; +            TCGv t0 = gen_ext_tl(reg, cpu_cc_dst, size, false); +            return (CCPrepare) { .cond = TCG_COND_EQ, .reg = t0, .mask = -1 }; +        } +    } +} + +/* perform a conditional store into register 'reg' according to jump opcode +   value 'b'. In the fast case, T0 is guaranted not to be used. */ +static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg) +{ +    int inv, jcc_op, cond; +    TCGMemOp size; +    CCPrepare cc; +    TCGv t0; + +    inv = b & 1; +    jcc_op = (b >> 1) & 7; + +    switch (s->cc_op) { +    case CC_OP_SUBB ... CC_OP_SUBQ: +        /* We optimize relational operators for the cmp/jcc case.  */ +        size = s->cc_op - CC_OP_SUBB; +        switch (jcc_op) { +        case JCC_BE: +            tcg_gen_mov_tl(cpu_tmp4, cpu_cc_srcT); +            gen_extu(size, cpu_tmp4); +            t0 = gen_ext_tl(cpu_tmp0, cpu_cc_src, size, false); +            cc = (CCPrepare) { .cond = TCG_COND_LEU, .reg = cpu_tmp4, +                               .reg2 = t0, .mask = -1, .use_reg2 = true }; +            break; + +        case JCC_L: +            cond = TCG_COND_LT; +            goto fast_jcc_l; +        case JCC_LE: +            cond = TCG_COND_LE; +        fast_jcc_l: +            tcg_gen_mov_tl(cpu_tmp4, cpu_cc_srcT); +            gen_exts(size, cpu_tmp4); +            t0 = gen_ext_tl(cpu_tmp0, cpu_cc_src, size, true); +            cc = (CCPrepare) { .cond = cond, .reg = cpu_tmp4, +                               .reg2 = t0, .mask = -1, .use_reg2 = true }; +            break; + +        default: +            goto slow_jcc; +        } +        break; + +    default: +    slow_jcc: +        /* This actually generates good code for JC, JZ and JS.  */ +        switch (jcc_op) { +        case JCC_O: +            cc = gen_prepare_eflags_o(s, reg); +            break; +        case JCC_B: +            cc = gen_prepare_eflags_c(s, reg); +            break; +        case JCC_Z: +            cc = gen_prepare_eflags_z(s, reg); +            break; +        case JCC_BE: +            gen_compute_eflags(s); +            cc = (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, +                               .mask = CC_Z | CC_C }; +            break; +        case JCC_S: +            cc = gen_prepare_eflags_s(s, reg); +            break; +        case JCC_P: +            cc = gen_prepare_eflags_p(s, reg); +            break; +        case JCC_L: +            gen_compute_eflags(s); +            if (TCGV_EQUAL(reg, cpu_cc_src)) { +                reg = cpu_tmp0; +            } +            tcg_gen_shri_tl(reg, cpu_cc_src, 4); /* CC_O -> CC_S */ +            tcg_gen_xor_tl(reg, reg, cpu_cc_src); +            cc = (CCPrepare) { .cond = TCG_COND_NE, .reg = reg, +                               .mask = CC_S }; +            break; +        default: +        case JCC_LE: +            gen_compute_eflags(s); +            if (TCGV_EQUAL(reg, cpu_cc_src)) { +                reg = cpu_tmp0; +            } +            tcg_gen_shri_tl(reg, cpu_cc_src, 4); /* CC_O -> CC_S */ +            tcg_gen_xor_tl(reg, reg, cpu_cc_src); +            cc = (CCPrepare) { .cond = TCG_COND_NE, .reg = reg, +                               .mask = CC_S | CC_Z }; +            break; +        } +        break; +    } + +    if (inv) { +        cc.cond = tcg_invert_cond(cc.cond); +    } +    return cc; +} + +static void gen_setcc1(DisasContext *s, int b, TCGv reg) +{ +    CCPrepare cc = gen_prepare_cc(s, b, reg); + +    if (cc.no_setcond) { +        if (cc.cond == TCG_COND_EQ) { +            tcg_gen_xori_tl(reg, cc.reg, 1); +        } else { +            tcg_gen_mov_tl(reg, cc.reg); +        } +        return; +    } + +    if (cc.cond == TCG_COND_NE && !cc.use_reg2 && cc.imm == 0 && +        cc.mask != 0 && (cc.mask & (cc.mask - 1)) == 0) { +        tcg_gen_shri_tl(reg, cc.reg, ctztl(cc.mask)); +        tcg_gen_andi_tl(reg, reg, 1); +        return; +    } +    if (cc.mask != -1) { +        tcg_gen_andi_tl(reg, cc.reg, cc.mask); +        cc.reg = reg; +    } +    if (cc.use_reg2) { +        tcg_gen_setcond_tl(cc.cond, reg, cc.reg, cc.reg2); +    } else { +        tcg_gen_setcondi_tl(cc.cond, reg, cc.reg, cc.imm); +    } +} + +static inline void gen_compute_eflags_c(DisasContext *s, TCGv reg) +{ +    gen_setcc1(s, JCC_B << 1, reg); +} + +/* generate a conditional jump to label 'l1' according to jump opcode +   value 'b'. In the fast case, T0 is guaranted not to be used. */ +static inline void gen_jcc1_noeob(DisasContext *s, int b, TCGLabel *l1) +{ +    CCPrepare cc = gen_prepare_cc(s, b, cpu_T[0]); + +    if (cc.mask != -1) { +        tcg_gen_andi_tl(cpu_T[0], cc.reg, cc.mask); +        cc.reg = cpu_T[0]; +    } +    if (cc.use_reg2) { +        tcg_gen_brcond_tl(cc.cond, cc.reg, cc.reg2, l1); +    } else { +        tcg_gen_brcondi_tl(cc.cond, cc.reg, cc.imm, l1); +    } +} + +/* Generate a conditional jump to label 'l1' according to jump opcode +   value 'b'. In the fast case, T0 is guaranted not to be used. +   A translation block must end soon.  */ +static inline void gen_jcc1(DisasContext *s, int b, TCGLabel *l1) +{ +    CCPrepare cc = gen_prepare_cc(s, b, cpu_T[0]); + +    gen_update_cc_op(s); +    if (cc.mask != -1) { +        tcg_gen_andi_tl(cpu_T[0], cc.reg, cc.mask); +        cc.reg = cpu_T[0]; +    } +    set_cc_op(s, CC_OP_DYNAMIC); +    if (cc.use_reg2) { +        tcg_gen_brcond_tl(cc.cond, cc.reg, cc.reg2, l1); +    } else { +        tcg_gen_brcondi_tl(cc.cond, cc.reg, cc.imm, l1); +    } +} + +/* XXX: does not work with gdbstub "ice" single step - not a +   serious problem */ +static TCGLabel *gen_jz_ecx_string(DisasContext *s, target_ulong next_eip) +{ +    TCGLabel *l1 = gen_new_label(); +    TCGLabel *l2 = gen_new_label(); +    gen_op_jnz_ecx(s->aflag, l1); +    gen_set_label(l2); +    gen_jmp_tb(s, next_eip, 1); +    gen_set_label(l1); +    return l2; +} + +static inline void gen_stos(DisasContext *s, TCGMemOp ot) +{ +    gen_op_mov_v_reg(MO_32, cpu_T[0], R_EAX); +    gen_string_movl_A0_EDI(s); +    gen_op_st_v(s, ot, cpu_T[0], cpu_A0); +    gen_op_movl_T0_Dshift(ot); +    gen_op_add_reg_T0(s->aflag, R_EDI); +} + +static inline void gen_lods(DisasContext *s, TCGMemOp ot) +{ +    gen_string_movl_A0_ESI(s); +    gen_op_ld_v(s, ot, cpu_T[0], cpu_A0); +    gen_op_mov_reg_v(ot, R_EAX, cpu_T[0]); +    gen_op_movl_T0_Dshift(ot); +    gen_op_add_reg_T0(s->aflag, R_ESI); +} + +static inline void gen_scas(DisasContext *s, TCGMemOp ot) +{ +    gen_string_movl_A0_EDI(s); +    gen_op_ld_v(s, ot, cpu_T[1], cpu_A0); +    gen_op(s, OP_CMPL, ot, R_EAX); +    gen_op_movl_T0_Dshift(ot); +    gen_op_add_reg_T0(s->aflag, R_EDI); +} + +static inline void gen_cmps(DisasContext *s, TCGMemOp ot) +{ +    gen_string_movl_A0_EDI(s); +    gen_op_ld_v(s, ot, cpu_T[1], cpu_A0); +    gen_string_movl_A0_ESI(s); +    gen_op(s, OP_CMPL, ot, OR_TMP0); +    gen_op_movl_T0_Dshift(ot); +    gen_op_add_reg_T0(s->aflag, R_ESI); +    gen_op_add_reg_T0(s->aflag, R_EDI); +} + +static inline void gen_ins(DisasContext *s, TCGMemOp ot) +{ +    if (s->tb->cflags & CF_USE_ICOUNT) { +        gen_io_start(); +    } +    gen_string_movl_A0_EDI(s); +    /* Note: we must do this dummy write first to be restartable in +       case of page fault. */ +    tcg_gen_movi_tl(cpu_T[0], 0); +    gen_op_st_v(s, ot, cpu_T[0], cpu_A0); +    tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_regs[R_EDX]); +    tcg_gen_andi_i32(cpu_tmp2_i32, cpu_tmp2_i32, 0xffff); +    gen_helper_in_func(ot, cpu_T[0], cpu_tmp2_i32); +    gen_op_st_v(s, ot, cpu_T[0], cpu_A0); +    gen_op_movl_T0_Dshift(ot); +    gen_op_add_reg_T0(s->aflag, R_EDI); +    if (s->tb->cflags & CF_USE_ICOUNT) { +        gen_io_end(); +    } +} + +static inline void gen_outs(DisasContext *s, TCGMemOp ot) +{ +    if (s->tb->cflags & CF_USE_ICOUNT) { +        gen_io_start(); +    } +    gen_string_movl_A0_ESI(s); +    gen_op_ld_v(s, ot, cpu_T[0], cpu_A0); + +    tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_regs[R_EDX]); +    tcg_gen_andi_i32(cpu_tmp2_i32, cpu_tmp2_i32, 0xffff); +    tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_T[0]); +    gen_helper_out_func(ot, cpu_tmp2_i32, cpu_tmp3_i32); + +    gen_op_movl_T0_Dshift(ot); +    gen_op_add_reg_T0(s->aflag, R_ESI); +    if (s->tb->cflags & CF_USE_ICOUNT) { +        gen_io_end(); +    } +} + +/* same method as Valgrind : we generate jumps to current or next +   instruction */ +#define GEN_REPZ(op)                                                          \ +static inline void gen_repz_ ## op(DisasContext *s, TCGMemOp ot,              \ +                                 target_ulong cur_eip, target_ulong next_eip) \ +{                                                                             \ +    TCGLabel *l2;                                                             \ +    gen_update_cc_op(s);                                                      \ +    l2 = gen_jz_ecx_string(s, next_eip);                                      \ +    gen_ ## op(s, ot);                                                        \ +    gen_op_add_reg_im(s->aflag, R_ECX, -1);                                   \ +    /* a loop would cause two single step exceptions if ECX = 1               \ +       before rep string_insn */                                              \ +    if (s->repz_opt)                                                          \ +        gen_op_jz_ecx(s->aflag, l2);                                          \ +    gen_jmp(s, cur_eip);                                                      \ +} + +#define GEN_REPZ2(op)                                                         \ +static inline void gen_repz_ ## op(DisasContext *s, TCGMemOp ot,              \ +                                   target_ulong cur_eip,                      \ +                                   target_ulong next_eip,                     \ +                                   int nz)                                    \ +{                                                                             \ +    TCGLabel *l2;                                                             \ +    gen_update_cc_op(s);                                                      \ +    l2 = gen_jz_ecx_string(s, next_eip);                                      \ +    gen_ ## op(s, ot);                                                        \ +    gen_op_add_reg_im(s->aflag, R_ECX, -1);                                   \ +    gen_update_cc_op(s);                                                      \ +    gen_jcc1(s, (JCC_Z << 1) | (nz ^ 1), l2);                                 \ +    if (s->repz_opt)                                                          \ +        gen_op_jz_ecx(s->aflag, l2);                                          \ +    gen_jmp(s, cur_eip);                                                      \ +} + +GEN_REPZ(movs) +GEN_REPZ(stos) +GEN_REPZ(lods) +GEN_REPZ(ins) +GEN_REPZ(outs) +GEN_REPZ2(scas) +GEN_REPZ2(cmps) + +static void gen_helper_fp_arith_ST0_FT0(int op) +{ +    switch (op) { +    case 0: +        gen_helper_fadd_ST0_FT0(cpu_env); +        break; +    case 1: +        gen_helper_fmul_ST0_FT0(cpu_env); +        break; +    case 2: +        gen_helper_fcom_ST0_FT0(cpu_env); +        break; +    case 3: +        gen_helper_fcom_ST0_FT0(cpu_env); +        break; +    case 4: +        gen_helper_fsub_ST0_FT0(cpu_env); +        break; +    case 5: +        gen_helper_fsubr_ST0_FT0(cpu_env); +        break; +    case 6: +        gen_helper_fdiv_ST0_FT0(cpu_env); +        break; +    case 7: +        gen_helper_fdivr_ST0_FT0(cpu_env); +        break; +    } +} + +/* NOTE the exception in "r" op ordering */ +static void gen_helper_fp_arith_STN_ST0(int op, int opreg) +{ +    TCGv_i32 tmp = tcg_const_i32(opreg); +    switch (op) { +    case 0: +        gen_helper_fadd_STN_ST0(cpu_env, tmp); +        break; +    case 1: +        gen_helper_fmul_STN_ST0(cpu_env, tmp); +        break; +    case 4: +        gen_helper_fsubr_STN_ST0(cpu_env, tmp); +        break; +    case 5: +        gen_helper_fsub_STN_ST0(cpu_env, tmp); +        break; +    case 6: +        gen_helper_fdivr_STN_ST0(cpu_env, tmp); +        break; +    case 7: +        gen_helper_fdiv_STN_ST0(cpu_env, tmp); +        break; +    } +} + +/* if d == OR_TMP0, it means memory operand (address in A0) */ +static void gen_op(DisasContext *s1, int op, TCGMemOp ot, int d) +{ +    if (d != OR_TMP0) { +        gen_op_mov_v_reg(ot, cpu_T[0], d); +    } else { +        gen_op_ld_v(s1, ot, cpu_T[0], cpu_A0); +    } +    switch(op) { +    case OP_ADCL: +        gen_compute_eflags_c(s1, cpu_tmp4); +        tcg_gen_add_tl(cpu_T[0], cpu_T[0], cpu_T[1]); +        tcg_gen_add_tl(cpu_T[0], cpu_T[0], cpu_tmp4); +        gen_op_st_rm_T0_A0(s1, ot, d); +        gen_op_update3_cc(cpu_tmp4); +        set_cc_op(s1, CC_OP_ADCB + ot); +        break; +    case OP_SBBL: +        gen_compute_eflags_c(s1, cpu_tmp4); +        tcg_gen_sub_tl(cpu_T[0], cpu_T[0], cpu_T[1]); +        tcg_gen_sub_tl(cpu_T[0], cpu_T[0], cpu_tmp4); +        gen_op_st_rm_T0_A0(s1, ot, d); +        gen_op_update3_cc(cpu_tmp4); +        set_cc_op(s1, CC_OP_SBBB + ot); +        break; +    case OP_ADDL: +        tcg_gen_add_tl(cpu_T[0], cpu_T[0], cpu_T[1]); +        gen_op_st_rm_T0_A0(s1, ot, d); +        gen_op_update2_cc(); +        set_cc_op(s1, CC_OP_ADDB + ot); +        break; +    case OP_SUBL: +        tcg_gen_mov_tl(cpu_cc_srcT, cpu_T[0]); +        tcg_gen_sub_tl(cpu_T[0], cpu_T[0], cpu_T[1]); +        gen_op_st_rm_T0_A0(s1, ot, d); +        gen_op_update2_cc(); +        set_cc_op(s1, CC_OP_SUBB + ot); +        break; +    default: +    case OP_ANDL: +        tcg_gen_and_tl(cpu_T[0], cpu_T[0], cpu_T[1]); +        gen_op_st_rm_T0_A0(s1, ot, d); +        gen_op_update1_cc(); +        set_cc_op(s1, CC_OP_LOGICB + ot); +        break; +    case OP_ORL: +        tcg_gen_or_tl(cpu_T[0], cpu_T[0], cpu_T[1]); +        gen_op_st_rm_T0_A0(s1, ot, d); +        gen_op_update1_cc(); +        set_cc_op(s1, CC_OP_LOGICB + ot); +        break; +    case OP_XORL: +        tcg_gen_xor_tl(cpu_T[0], cpu_T[0], cpu_T[1]); +        gen_op_st_rm_T0_A0(s1, ot, d); +        gen_op_update1_cc(); +        set_cc_op(s1, CC_OP_LOGICB + ot); +        break; +    case OP_CMPL: +        tcg_gen_mov_tl(cpu_cc_src, cpu_T[1]); +        tcg_gen_mov_tl(cpu_cc_srcT, cpu_T[0]); +        tcg_gen_sub_tl(cpu_cc_dst, cpu_T[0], cpu_T[1]); +        set_cc_op(s1, CC_OP_SUBB + ot); +        break; +    } +} + +/* if d == OR_TMP0, it means memory operand (address in A0) */ +static void gen_inc(DisasContext *s1, TCGMemOp ot, int d, int c) +{ +    if (d != OR_TMP0) { +        gen_op_mov_v_reg(ot, cpu_T[0], d); +    } else { +        gen_op_ld_v(s1, ot, cpu_T[0], cpu_A0); +    } +    gen_compute_eflags_c(s1, cpu_cc_src); +    if (c > 0) { +        tcg_gen_addi_tl(cpu_T[0], cpu_T[0], 1); +        set_cc_op(s1, CC_OP_INCB + ot); +    } else { +        tcg_gen_addi_tl(cpu_T[0], cpu_T[0], -1); +        set_cc_op(s1, CC_OP_DECB + ot); +    } +    gen_op_st_rm_T0_A0(s1, ot, d); +    tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); +} + +static void gen_shift_flags(DisasContext *s, TCGMemOp ot, TCGv result, +                            TCGv shm1, TCGv count, bool is_right) +{ +    TCGv_i32 z32, s32, oldop; +    TCGv z_tl; + +    /* Store the results into the CC variables.  If we know that the +       variable must be dead, store unconditionally.  Otherwise we'll +       need to not disrupt the current contents.  */ +    z_tl = tcg_const_tl(0); +    if (cc_op_live[s->cc_op] & USES_CC_DST) { +        tcg_gen_movcond_tl(TCG_COND_NE, cpu_cc_dst, count, z_tl, +                           result, cpu_cc_dst); +    } else { +        tcg_gen_mov_tl(cpu_cc_dst, result); +    } +    if (cc_op_live[s->cc_op] & USES_CC_SRC) { +        tcg_gen_movcond_tl(TCG_COND_NE, cpu_cc_src, count, z_tl, +                           shm1, cpu_cc_src); +    } else { +        tcg_gen_mov_tl(cpu_cc_src, shm1); +    } +    tcg_temp_free(z_tl); + +    /* Get the two potential CC_OP values into temporaries.  */ +    tcg_gen_movi_i32(cpu_tmp2_i32, (is_right ? CC_OP_SARB : CC_OP_SHLB) + ot); +    if (s->cc_op == CC_OP_DYNAMIC) { +        oldop = cpu_cc_op; +    } else { +        tcg_gen_movi_i32(cpu_tmp3_i32, s->cc_op); +        oldop = cpu_tmp3_i32; +    } + +    /* Conditionally store the CC_OP value.  */ +    z32 = tcg_const_i32(0); +    s32 = tcg_temp_new_i32(); +    tcg_gen_trunc_tl_i32(s32, count); +    tcg_gen_movcond_i32(TCG_COND_NE, cpu_cc_op, s32, z32, cpu_tmp2_i32, oldop); +    tcg_temp_free_i32(z32); +    tcg_temp_free_i32(s32); + +    /* The CC_OP value is no longer predictable.  */ +    set_cc_op(s, CC_OP_DYNAMIC); +} + +static void gen_shift_rm_T1(DisasContext *s, TCGMemOp ot, int op1, +                            int is_right, int is_arith) +{ +    target_ulong mask = (ot == MO_64 ? 0x3f : 0x1f); + +    /* load */ +    if (op1 == OR_TMP0) { +        gen_op_ld_v(s, ot, cpu_T[0], cpu_A0); +    } else { +        gen_op_mov_v_reg(ot, cpu_T[0], op1); +    } + +    tcg_gen_andi_tl(cpu_T[1], cpu_T[1], mask); +    tcg_gen_subi_tl(cpu_tmp0, cpu_T[1], 1); + +    if (is_right) { +        if (is_arith) { +            gen_exts(ot, cpu_T[0]); +            tcg_gen_sar_tl(cpu_tmp0, cpu_T[0], cpu_tmp0); +            tcg_gen_sar_tl(cpu_T[0], cpu_T[0], cpu_T[1]); +        } else { +            gen_extu(ot, cpu_T[0]); +            tcg_gen_shr_tl(cpu_tmp0, cpu_T[0], cpu_tmp0); +            tcg_gen_shr_tl(cpu_T[0], cpu_T[0], cpu_T[1]); +        } +    } else { +        tcg_gen_shl_tl(cpu_tmp0, cpu_T[0], cpu_tmp0); +        tcg_gen_shl_tl(cpu_T[0], cpu_T[0], cpu_T[1]); +    } + +    /* store */ +    gen_op_st_rm_T0_A0(s, ot, op1); + +    gen_shift_flags(s, ot, cpu_T[0], cpu_tmp0, cpu_T[1], is_right); +} + +static void gen_shift_rm_im(DisasContext *s, TCGMemOp ot, int op1, int op2, +                            int is_right, int is_arith) +{ +    int mask = (ot == MO_64 ? 0x3f : 0x1f); + +    /* load */ +    if (op1 == OR_TMP0) +        gen_op_ld_v(s, ot, cpu_T[0], cpu_A0); +    else +        gen_op_mov_v_reg(ot, cpu_T[0], op1); + +    op2 &= mask; +    if (op2 != 0) { +        if (is_right) { +            if (is_arith) { +                gen_exts(ot, cpu_T[0]); +                tcg_gen_sari_tl(cpu_tmp4, cpu_T[0], op2 - 1); +                tcg_gen_sari_tl(cpu_T[0], cpu_T[0], op2); +            } else { +                gen_extu(ot, cpu_T[0]); +                tcg_gen_shri_tl(cpu_tmp4, cpu_T[0], op2 - 1); +                tcg_gen_shri_tl(cpu_T[0], cpu_T[0], op2); +            } +        } else { +            tcg_gen_shli_tl(cpu_tmp4, cpu_T[0], op2 - 1); +            tcg_gen_shli_tl(cpu_T[0], cpu_T[0], op2); +        } +    } + +    /* store */ +    gen_op_st_rm_T0_A0(s, ot, op1); + +    /* update eflags if non zero shift */ +    if (op2 != 0) { +        tcg_gen_mov_tl(cpu_cc_src, cpu_tmp4); +        tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); +        set_cc_op(s, (is_right ? CC_OP_SARB : CC_OP_SHLB) + ot); +    } +} + +static void gen_rot_rm_T1(DisasContext *s, TCGMemOp ot, int op1, int is_right) +{ +    target_ulong mask = (ot == MO_64 ? 0x3f : 0x1f); +    TCGv_i32 t0, t1; + +    /* load */ +    if (op1 == OR_TMP0) { +        gen_op_ld_v(s, ot, cpu_T[0], cpu_A0); +    } else { +        gen_op_mov_v_reg(ot, cpu_T[0], op1); +    } + +    tcg_gen_andi_tl(cpu_T[1], cpu_T[1], mask); + +    switch (ot) { +    case MO_8: +        /* Replicate the 8-bit input so that a 32-bit rotate works.  */ +        tcg_gen_ext8u_tl(cpu_T[0], cpu_T[0]); +        tcg_gen_muli_tl(cpu_T[0], cpu_T[0], 0x01010101); +        goto do_long; +    case MO_16: +        /* Replicate the 16-bit input so that a 32-bit rotate works.  */ +        tcg_gen_deposit_tl(cpu_T[0], cpu_T[0], cpu_T[0], 16, 16); +        goto do_long; +    do_long: +#ifdef TARGET_X86_64 +    case MO_32: +        tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); +        tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_T[1]); +        if (is_right) { +            tcg_gen_rotr_i32(cpu_tmp2_i32, cpu_tmp2_i32, cpu_tmp3_i32); +        } else { +            tcg_gen_rotl_i32(cpu_tmp2_i32, cpu_tmp2_i32, cpu_tmp3_i32); +        } +        tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32); +        break; +#endif +    default: +        if (is_right) { +            tcg_gen_rotr_tl(cpu_T[0], cpu_T[0], cpu_T[1]); +        } else { +            tcg_gen_rotl_tl(cpu_T[0], cpu_T[0], cpu_T[1]); +        } +        break; +    } + +    /* store */ +    gen_op_st_rm_T0_A0(s, ot, op1); + +    /* We'll need the flags computed into CC_SRC.  */ +    gen_compute_eflags(s); + +    /* The value that was "rotated out" is now present at the other end +       of the word.  Compute C into CC_DST and O into CC_SRC2.  Note that +       since we've computed the flags into CC_SRC, these variables are +       currently dead.  */ +    if (is_right) { +        tcg_gen_shri_tl(cpu_cc_src2, cpu_T[0], mask - 1); +        tcg_gen_shri_tl(cpu_cc_dst, cpu_T[0], mask); +        tcg_gen_andi_tl(cpu_cc_dst, cpu_cc_dst, 1); +    } else { +        tcg_gen_shri_tl(cpu_cc_src2, cpu_T[0], mask); +        tcg_gen_andi_tl(cpu_cc_dst, cpu_T[0], 1); +    } +    tcg_gen_andi_tl(cpu_cc_src2, cpu_cc_src2, 1); +    tcg_gen_xor_tl(cpu_cc_src2, cpu_cc_src2, cpu_cc_dst); + +    /* Now conditionally store the new CC_OP value.  If the shift count +       is 0 we keep the CC_OP_EFLAGS setting so that only CC_SRC is live. +       Otherwise reuse CC_OP_ADCOX which have the C and O flags split out +       exactly as we computed above.  */ +    t0 = tcg_const_i32(0); +    t1 = tcg_temp_new_i32(); +    tcg_gen_trunc_tl_i32(t1, cpu_T[1]); +    tcg_gen_movi_i32(cpu_tmp2_i32, CC_OP_ADCOX);  +    tcg_gen_movi_i32(cpu_tmp3_i32, CC_OP_EFLAGS); +    tcg_gen_movcond_i32(TCG_COND_NE, cpu_cc_op, t1, t0, +                        cpu_tmp2_i32, cpu_tmp3_i32); +    tcg_temp_free_i32(t0); +    tcg_temp_free_i32(t1); + +    /* The CC_OP value is no longer predictable.  */  +    set_cc_op(s, CC_OP_DYNAMIC); +} + +static void gen_rot_rm_im(DisasContext *s, TCGMemOp ot, int op1, int op2, +                          int is_right) +{ +    int mask = (ot == MO_64 ? 0x3f : 0x1f); +    int shift; + +    /* load */ +    if (op1 == OR_TMP0) { +        gen_op_ld_v(s, ot, cpu_T[0], cpu_A0); +    } else { +        gen_op_mov_v_reg(ot, cpu_T[0], op1); +    } + +    op2 &= mask; +    if (op2 != 0) { +        switch (ot) { +#ifdef TARGET_X86_64 +        case MO_32: +            tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); +            if (is_right) { +                tcg_gen_rotri_i32(cpu_tmp2_i32, cpu_tmp2_i32, op2); +            } else { +                tcg_gen_rotli_i32(cpu_tmp2_i32, cpu_tmp2_i32, op2); +            } +            tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32); +            break; +#endif +        default: +            if (is_right) { +                tcg_gen_rotri_tl(cpu_T[0], cpu_T[0], op2); +            } else { +                tcg_gen_rotli_tl(cpu_T[0], cpu_T[0], op2); +            } +            break; +        case MO_8: +            mask = 7; +            goto do_shifts; +        case MO_16: +            mask = 15; +        do_shifts: +            shift = op2 & mask; +            if (is_right) { +                shift = mask + 1 - shift; +            } +            gen_extu(ot, cpu_T[0]); +            tcg_gen_shli_tl(cpu_tmp0, cpu_T[0], shift); +            tcg_gen_shri_tl(cpu_T[0], cpu_T[0], mask + 1 - shift); +            tcg_gen_or_tl(cpu_T[0], cpu_T[0], cpu_tmp0); +            break; +        } +    } + +    /* store */ +    gen_op_st_rm_T0_A0(s, ot, op1); + +    if (op2 != 0) { +        /* Compute the flags into CC_SRC.  */ +        gen_compute_eflags(s); + +        /* The value that was "rotated out" is now present at the other end +           of the word.  Compute C into CC_DST and O into CC_SRC2.  Note that +           since we've computed the flags into CC_SRC, these variables are +           currently dead.  */ +        if (is_right) { +            tcg_gen_shri_tl(cpu_cc_src2, cpu_T[0], mask - 1); +            tcg_gen_shri_tl(cpu_cc_dst, cpu_T[0], mask); +            tcg_gen_andi_tl(cpu_cc_dst, cpu_cc_dst, 1); +        } else { +            tcg_gen_shri_tl(cpu_cc_src2, cpu_T[0], mask); +            tcg_gen_andi_tl(cpu_cc_dst, cpu_T[0], 1); +        } +        tcg_gen_andi_tl(cpu_cc_src2, cpu_cc_src2, 1); +        tcg_gen_xor_tl(cpu_cc_src2, cpu_cc_src2, cpu_cc_dst); +        set_cc_op(s, CC_OP_ADCOX); +    } +} + +/* XXX: add faster immediate = 1 case */ +static void gen_rotc_rm_T1(DisasContext *s, TCGMemOp ot, int op1, +                           int is_right) +{ +    gen_compute_eflags(s); +    assert(s->cc_op == CC_OP_EFLAGS); + +    /* load */ +    if (op1 == OR_TMP0) +        gen_op_ld_v(s, ot, cpu_T[0], cpu_A0); +    else +        gen_op_mov_v_reg(ot, cpu_T[0], op1); +     +    if (is_right) { +        switch (ot) { +        case MO_8: +            gen_helper_rcrb(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); +            break; +        case MO_16: +            gen_helper_rcrw(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); +            break; +        case MO_32: +            gen_helper_rcrl(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); +            break; +#ifdef TARGET_X86_64 +        case MO_64: +            gen_helper_rcrq(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); +            break; +#endif +        default: +            tcg_abort(); +        } +    } else { +        switch (ot) { +        case MO_8: +            gen_helper_rclb(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); +            break; +        case MO_16: +            gen_helper_rclw(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); +            break; +        case MO_32: +            gen_helper_rcll(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); +            break; +#ifdef TARGET_X86_64 +        case MO_64: +            gen_helper_rclq(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); +            break; +#endif +        default: +            tcg_abort(); +        } +    } +    /* store */ +    gen_op_st_rm_T0_A0(s, ot, op1); +} + +/* XXX: add faster immediate case */ +static void gen_shiftd_rm_T1(DisasContext *s, TCGMemOp ot, int op1, +                             bool is_right, TCGv count_in) +{ +    target_ulong mask = (ot == MO_64 ? 63 : 31); +    TCGv count; + +    /* load */ +    if (op1 == OR_TMP0) { +        gen_op_ld_v(s, ot, cpu_T[0], cpu_A0); +    } else { +        gen_op_mov_v_reg(ot, cpu_T[0], op1); +    } + +    count = tcg_temp_new(); +    tcg_gen_andi_tl(count, count_in, mask); + +    switch (ot) { +    case MO_16: +        /* Note: we implement the Intel behaviour for shift count > 16. +           This means "shrdw C, B, A" shifts A:B:A >> C.  Build the B:A +           portion by constructing it as a 32-bit value.  */ +        if (is_right) { +            tcg_gen_deposit_tl(cpu_tmp0, cpu_T[0], cpu_T[1], 16, 16); +            tcg_gen_mov_tl(cpu_T[1], cpu_T[0]); +            tcg_gen_mov_tl(cpu_T[0], cpu_tmp0); +        } else { +            tcg_gen_deposit_tl(cpu_T[1], cpu_T[0], cpu_T[1], 16, 16); +        } +        /* FALLTHRU */ +#ifdef TARGET_X86_64 +    case MO_32: +        /* Concatenate the two 32-bit values and use a 64-bit shift.  */ +        tcg_gen_subi_tl(cpu_tmp0, count, 1); +        if (is_right) { +            tcg_gen_concat_tl_i64(cpu_T[0], cpu_T[0], cpu_T[1]); +            tcg_gen_shr_i64(cpu_tmp0, cpu_T[0], cpu_tmp0); +            tcg_gen_shr_i64(cpu_T[0], cpu_T[0], count); +        } else { +            tcg_gen_concat_tl_i64(cpu_T[0], cpu_T[1], cpu_T[0]); +            tcg_gen_shl_i64(cpu_tmp0, cpu_T[0], cpu_tmp0); +            tcg_gen_shl_i64(cpu_T[0], cpu_T[0], count); +            tcg_gen_shri_i64(cpu_tmp0, cpu_tmp0, 32); +            tcg_gen_shri_i64(cpu_T[0], cpu_T[0], 32); +        } +        break; +#endif +    default: +        tcg_gen_subi_tl(cpu_tmp0, count, 1); +        if (is_right) { +            tcg_gen_shr_tl(cpu_tmp0, cpu_T[0], cpu_tmp0); + +            tcg_gen_subfi_tl(cpu_tmp4, mask + 1, count); +            tcg_gen_shr_tl(cpu_T[0], cpu_T[0], count); +            tcg_gen_shl_tl(cpu_T[1], cpu_T[1], cpu_tmp4); +        } else { +            tcg_gen_shl_tl(cpu_tmp0, cpu_T[0], cpu_tmp0); +            if (ot == MO_16) { +                /* Only needed if count > 16, for Intel behaviour.  */ +                tcg_gen_subfi_tl(cpu_tmp4, 33, count); +                tcg_gen_shr_tl(cpu_tmp4, cpu_T[1], cpu_tmp4); +                tcg_gen_or_tl(cpu_tmp0, cpu_tmp0, cpu_tmp4); +            } + +            tcg_gen_subfi_tl(cpu_tmp4, mask + 1, count); +            tcg_gen_shl_tl(cpu_T[0], cpu_T[0], count); +            tcg_gen_shr_tl(cpu_T[1], cpu_T[1], cpu_tmp4); +        } +        tcg_gen_movi_tl(cpu_tmp4, 0); +        tcg_gen_movcond_tl(TCG_COND_EQ, cpu_T[1], count, cpu_tmp4, +                           cpu_tmp4, cpu_T[1]); +        tcg_gen_or_tl(cpu_T[0], cpu_T[0], cpu_T[1]); +        break; +    } + +    /* store */ +    gen_op_st_rm_T0_A0(s, ot, op1); + +    gen_shift_flags(s, ot, cpu_T[0], cpu_tmp0, count, is_right); +    tcg_temp_free(count); +} + +static void gen_shift(DisasContext *s1, int op, TCGMemOp ot, int d, int s) +{ +    if (s != OR_TMP1) +        gen_op_mov_v_reg(ot, cpu_T[1], s); +    switch(op) { +    case OP_ROL: +        gen_rot_rm_T1(s1, ot, d, 0); +        break; +    case OP_ROR: +        gen_rot_rm_T1(s1, ot, d, 1); +        break; +    case OP_SHL: +    case OP_SHL1: +        gen_shift_rm_T1(s1, ot, d, 0, 0); +        break; +    case OP_SHR: +        gen_shift_rm_T1(s1, ot, d, 1, 0); +        break; +    case OP_SAR: +        gen_shift_rm_T1(s1, ot, d, 1, 1); +        break; +    case OP_RCL: +        gen_rotc_rm_T1(s1, ot, d, 0); +        break; +    case OP_RCR: +        gen_rotc_rm_T1(s1, ot, d, 1); +        break; +    } +} + +static void gen_shifti(DisasContext *s1, int op, TCGMemOp ot, int d, int c) +{ +    switch(op) { +    case OP_ROL: +        gen_rot_rm_im(s1, ot, d, c, 0); +        break; +    case OP_ROR: +        gen_rot_rm_im(s1, ot, d, c, 1); +        break; +    case OP_SHL: +    case OP_SHL1: +        gen_shift_rm_im(s1, ot, d, c, 0, 0); +        break; +    case OP_SHR: +        gen_shift_rm_im(s1, ot, d, c, 1, 0); +        break; +    case OP_SAR: +        gen_shift_rm_im(s1, ot, d, c, 1, 1); +        break; +    default: +        /* currently not optimized */ +        tcg_gen_movi_tl(cpu_T[1], c); +        gen_shift(s1, op, ot, d, OR_TMP1); +        break; +    } +} + +static void gen_lea_modrm(CPUX86State *env, DisasContext *s, int modrm) +{ +    target_long disp; +    int havesib; +    int base; +    int index; +    int scale; +    int mod, rm, code, override, must_add_seg; +    TCGv sum; + +    override = s->override; +    must_add_seg = s->addseg; +    if (override >= 0) +        must_add_seg = 1; +    mod = (modrm >> 6) & 3; +    rm = modrm & 7; + +    switch (s->aflag) { +    case MO_64: +    case MO_32: +        havesib = 0; +        base = rm; +        index = -1; +        scale = 0; + +        if (base == 4) { +            havesib = 1; +            code = cpu_ldub_code(env, s->pc++); +            scale = (code >> 6) & 3; +            index = ((code >> 3) & 7) | REX_X(s); +            if (index == 4) { +                index = -1;  /* no index */ +            } +            base = (code & 7); +        } +        base |= REX_B(s); + +        switch (mod) { +        case 0: +            if ((base & 7) == 5) { +                base = -1; +                disp = (int32_t)cpu_ldl_code(env, s->pc); +                s->pc += 4; +                if (CODE64(s) && !havesib) { +                    disp += s->pc + s->rip_offset; +                } +            } else { +                disp = 0; +            } +            break; +        case 1: +            disp = (int8_t)cpu_ldub_code(env, s->pc++); +            break; +        default: +        case 2: +            disp = (int32_t)cpu_ldl_code(env, s->pc); +            s->pc += 4; +            break; +        } + +        /* For correct popl handling with esp.  */ +        if (base == R_ESP && s->popl_esp_hack) { +            disp += s->popl_esp_hack; +        } + +        /* Compute the address, with a minimum number of TCG ops.  */ +        TCGV_UNUSED(sum); +        if (index >= 0) { +            if (scale == 0) { +                sum = cpu_regs[index]; +            } else { +                tcg_gen_shli_tl(cpu_A0, cpu_regs[index], scale); +                sum = cpu_A0; +            } +            if (base >= 0) { +                tcg_gen_add_tl(cpu_A0, sum, cpu_regs[base]); +                sum = cpu_A0; +            } +        } else if (base >= 0) { +            sum = cpu_regs[base]; +        } +        if (TCGV_IS_UNUSED(sum)) { +            tcg_gen_movi_tl(cpu_A0, disp); +        } else { +            tcg_gen_addi_tl(cpu_A0, sum, disp); +        } + +        if (must_add_seg) { +            if (override < 0) { +                if (base == R_EBP || base == R_ESP) { +                    override = R_SS; +                } else { +                    override = R_DS; +                } +            } + +            tcg_gen_ld_tl(cpu_tmp0, cpu_env, +                          offsetof(CPUX86State, segs[override].base)); +            if (CODE64(s)) { +                if (s->aflag == MO_32) { +                    tcg_gen_ext32u_tl(cpu_A0, cpu_A0); +                } +                tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0); +                return; +            } + +            tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0); +        } + +        if (s->aflag == MO_32) { +            tcg_gen_ext32u_tl(cpu_A0, cpu_A0); +        } +        break; + +    case MO_16: +        switch (mod) { +        case 0: +            if (rm == 6) { +                disp = cpu_lduw_code(env, s->pc); +                s->pc += 2; +                tcg_gen_movi_tl(cpu_A0, disp); +                rm = 0; /* avoid SS override */ +                goto no_rm; +            } else { +                disp = 0; +            } +            break; +        case 1: +            disp = (int8_t)cpu_ldub_code(env, s->pc++); +            break; +        default: +        case 2: +            disp = (int16_t)cpu_lduw_code(env, s->pc); +            s->pc += 2; +            break; +        } + +        sum = cpu_A0; +        switch (rm) { +        case 0: +            tcg_gen_add_tl(cpu_A0, cpu_regs[R_EBX], cpu_regs[R_ESI]); +            break; +        case 1: +            tcg_gen_add_tl(cpu_A0, cpu_regs[R_EBX], cpu_regs[R_EDI]); +            break; +        case 2: +            tcg_gen_add_tl(cpu_A0, cpu_regs[R_EBP], cpu_regs[R_ESI]); +            break; +        case 3: +            tcg_gen_add_tl(cpu_A0, cpu_regs[R_EBP], cpu_regs[R_EDI]); +            break; +        case 4: +            sum = cpu_regs[R_ESI]; +            break; +        case 5: +            sum = cpu_regs[R_EDI]; +            break; +        case 6: +            sum = cpu_regs[R_EBP]; +            break; +        default: +        case 7: +            sum = cpu_regs[R_EBX]; +            break; +        } +        tcg_gen_addi_tl(cpu_A0, sum, disp); +        tcg_gen_ext16u_tl(cpu_A0, cpu_A0); +    no_rm: +        if (must_add_seg) { +            if (override < 0) { +                if (rm == 2 || rm == 3 || rm == 6) { +                    override = R_SS; +                } else { +                    override = R_DS; +                } +            } +            gen_op_addl_A0_seg(s, override); +        } +        break; + +    default: +        tcg_abort(); +    } +} + +static void gen_nop_modrm(CPUX86State *env, DisasContext *s, int modrm) +{ +    int mod, rm, base, code; + +    mod = (modrm >> 6) & 3; +    if (mod == 3) +        return; +    rm = modrm & 7; + +    switch (s->aflag) { +    case MO_64: +    case MO_32: +        base = rm; + +        if (base == 4) { +            code = cpu_ldub_code(env, s->pc++); +            base = (code & 7); +        } + +        switch (mod) { +        case 0: +            if (base == 5) { +                s->pc += 4; +            } +            break; +        case 1: +            s->pc++; +            break; +        default: +        case 2: +            s->pc += 4; +            break; +        } +        break; + +    case MO_16: +        switch (mod) { +        case 0: +            if (rm == 6) { +                s->pc += 2; +            } +            break; +        case 1: +            s->pc++; +            break; +        default: +        case 2: +            s->pc += 2; +            break; +        } +        break; + +    default: +        tcg_abort(); +    } +} + +/* used for LEA and MOV AX, mem */ +static void gen_add_A0_ds_seg(DisasContext *s) +{ +    int override, must_add_seg; +    must_add_seg = s->addseg; +    override = R_DS; +    if (s->override >= 0) { +        override = s->override; +        must_add_seg = 1; +    } +    if (must_add_seg) { +#ifdef TARGET_X86_64 +        if (CODE64(s)) { +            gen_op_addq_A0_seg(override); +        } else +#endif +        { +            gen_op_addl_A0_seg(s, override); +        } +    } +} + +/* generate modrm memory load or store of 'reg'. TMP0 is used if reg == +   OR_TMP0 */ +static void gen_ldst_modrm(CPUX86State *env, DisasContext *s, int modrm, +                           TCGMemOp ot, int reg, int is_store) +{ +    int mod, rm; + +    mod = (modrm >> 6) & 3; +    rm = (modrm & 7) | REX_B(s); +    if (mod == 3) { +        if (is_store) { +            if (reg != OR_TMP0) +                gen_op_mov_v_reg(ot, cpu_T[0], reg); +            gen_op_mov_reg_v(ot, rm, cpu_T[0]); +        } else { +            gen_op_mov_v_reg(ot, cpu_T[0], rm); +            if (reg != OR_TMP0) +                gen_op_mov_reg_v(ot, reg, cpu_T[0]); +        } +    } else { +        gen_lea_modrm(env, s, modrm); +        if (is_store) { +            if (reg != OR_TMP0) +                gen_op_mov_v_reg(ot, cpu_T[0], reg); +            gen_op_st_v(s, ot, cpu_T[0], cpu_A0); +        } else { +            gen_op_ld_v(s, ot, cpu_T[0], cpu_A0); +            if (reg != OR_TMP0) +                gen_op_mov_reg_v(ot, reg, cpu_T[0]); +        } +    } +} + +static inline uint32_t insn_get(CPUX86State *env, DisasContext *s, TCGMemOp ot) +{ +    uint32_t ret; + +    switch (ot) { +    case MO_8: +        ret = cpu_ldub_code(env, s->pc); +        s->pc++; +        break; +    case MO_16: +        ret = cpu_lduw_code(env, s->pc); +        s->pc += 2; +        break; +    case MO_32: +#ifdef TARGET_X86_64 +    case MO_64: +#endif +        ret = cpu_ldl_code(env, s->pc); +        s->pc += 4; +        break; +    default: +        tcg_abort(); +    } +    return ret; +} + +static inline int insn_const_size(TCGMemOp ot) +{ +    if (ot <= MO_32) { +        return 1 << ot; +    } else { +        return 4; +    } +} + +static inline void gen_goto_tb(DisasContext *s, int tb_num, target_ulong eip) +{ +    TranslationBlock *tb; +    target_ulong pc; + +    pc = s->cs_base + eip; +    tb = s->tb; +    /* NOTE: we handle the case where the TB spans two pages here */ +    if ((pc & TARGET_PAGE_MASK) == (tb->pc & TARGET_PAGE_MASK) || +        (pc & TARGET_PAGE_MASK) == ((s->pc - 1) & TARGET_PAGE_MASK))  { +        /* jump to same page: we can use a direct jump */ +        tcg_gen_goto_tb(tb_num); +        gen_jmp_im(eip); +        tcg_gen_exit_tb((uintptr_t)tb + tb_num); +    } else { +        /* jump to another page: currently not optimized */ +        gen_jmp_im(eip); +        gen_eob(s); +    } +} + +static inline void gen_jcc(DisasContext *s, int b, +                           target_ulong val, target_ulong next_eip) +{ +    TCGLabel *l1, *l2; + +    if (s->jmp_opt) { +        l1 = gen_new_label(); +        gen_jcc1(s, b, l1); + +        gen_goto_tb(s, 0, next_eip); + +        gen_set_label(l1); +        gen_goto_tb(s, 1, val); +        s->is_jmp = DISAS_TB_JUMP; +    } else { +        l1 = gen_new_label(); +        l2 = gen_new_label(); +        gen_jcc1(s, b, l1); + +        gen_jmp_im(next_eip); +        tcg_gen_br(l2); + +        gen_set_label(l1); +        gen_jmp_im(val); +        gen_set_label(l2); +        gen_eob(s); +    } +} + +static void gen_cmovcc1(CPUX86State *env, DisasContext *s, TCGMemOp ot, int b, +                        int modrm, int reg) +{ +    CCPrepare cc; + +    gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + +    cc = gen_prepare_cc(s, b, cpu_T[1]); +    if (cc.mask != -1) { +        TCGv t0 = tcg_temp_new(); +        tcg_gen_andi_tl(t0, cc.reg, cc.mask); +        cc.reg = t0; +    } +    if (!cc.use_reg2) { +        cc.reg2 = tcg_const_tl(cc.imm); +    } + +    tcg_gen_movcond_tl(cc.cond, cpu_T[0], cc.reg, cc.reg2, +                       cpu_T[0], cpu_regs[reg]); +    gen_op_mov_reg_v(ot, reg, cpu_T[0]); + +    if (cc.mask != -1) { +        tcg_temp_free(cc.reg); +    } +    if (!cc.use_reg2) { +        tcg_temp_free(cc.reg2); +    } +} + +static inline void gen_op_movl_T0_seg(int seg_reg) +{ +    tcg_gen_ld32u_tl(cpu_T[0], cpu_env,  +                     offsetof(CPUX86State,segs[seg_reg].selector)); +} + +static inline void gen_op_movl_seg_T0_vm(int seg_reg) +{ +    tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 0xffff); +    tcg_gen_st32_tl(cpu_T[0], cpu_env,  +                    offsetof(CPUX86State,segs[seg_reg].selector)); +    tcg_gen_shli_tl(cpu_T[0], cpu_T[0], 4); +    tcg_gen_st_tl(cpu_T[0], cpu_env,  +                  offsetof(CPUX86State,segs[seg_reg].base)); +} + +/* move T0 to seg_reg and compute if the CPU state may change. Never +   call this function with seg_reg == R_CS */ +static void gen_movl_seg_T0(DisasContext *s, int seg_reg, target_ulong cur_eip) +{ +    if (s->pe && !s->vm86) { +        /* XXX: optimize by finding processor state dynamically */ +        gen_update_cc_op(s); +        gen_jmp_im(cur_eip); +        tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); +        gen_helper_load_seg(cpu_env, tcg_const_i32(seg_reg), cpu_tmp2_i32); +        /* abort translation because the addseg value may change or +           because ss32 may change. For R_SS, translation must always +           stop as a special handling must be done to disable hardware +           interrupts for the next instruction */ +        if (seg_reg == R_SS || (s->code32 && seg_reg < R_FS)) +            s->is_jmp = DISAS_TB_JUMP; +    } else { +        gen_op_movl_seg_T0_vm(seg_reg); +        if (seg_reg == R_SS) +            s->is_jmp = DISAS_TB_JUMP; +    } +} + +static inline int svm_is_rep(int prefixes) +{ +    return ((prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) ? 8 : 0); +} + +static inline void +gen_svm_check_intercept_param(DisasContext *s, target_ulong pc_start, +                              uint32_t type, uint64_t param) +{ +    /* no SVM activated; fast case */ +    if (likely(!(s->flags & HF_SVMI_MASK))) +        return; +    gen_update_cc_op(s); +    gen_jmp_im(pc_start - s->cs_base); +    gen_helper_svm_check_intercept_param(cpu_env, tcg_const_i32(type), +                                         tcg_const_i64(param)); +} + +static inline void +gen_svm_check_intercept(DisasContext *s, target_ulong pc_start, uint64_t type) +{ +    gen_svm_check_intercept_param(s, pc_start, type, 0); +} + +static inline void gen_stack_update(DisasContext *s, int addend) +{ +#ifdef TARGET_X86_64 +    if (CODE64(s)) { +        gen_op_add_reg_im(MO_64, R_ESP, addend); +    } else +#endif +    if (s->ss32) { +        gen_op_add_reg_im(MO_32, R_ESP, addend); +    } else { +        gen_op_add_reg_im(MO_16, R_ESP, addend); +    } +} + +/* Generate a push. It depends on ss32, addseg and dflag.  */ +static void gen_push_v(DisasContext *s, TCGv val) +{ +    TCGMemOp a_ot, d_ot = mo_pushpop(s, s->dflag); +    int size = 1 << d_ot; +    TCGv new_esp = cpu_A0; + +    tcg_gen_subi_tl(cpu_A0, cpu_regs[R_ESP], size); + +    if (CODE64(s)) { +        a_ot = MO_64; +    } else if (s->ss32) { +        a_ot = MO_32; +        if (s->addseg) { +            new_esp = cpu_tmp4; +            tcg_gen_mov_tl(new_esp, cpu_A0); +            gen_op_addl_A0_seg(s, R_SS); +        } else { +            tcg_gen_ext32u_tl(cpu_A0, cpu_A0); +        } +    } else { +        a_ot = MO_16; +        new_esp = cpu_tmp4; +        tcg_gen_ext16u_tl(cpu_A0, cpu_A0); +        tcg_gen_mov_tl(new_esp, cpu_A0); +        gen_op_addl_A0_seg(s, R_SS); +    } + +    gen_op_st_v(s, d_ot, val, cpu_A0); +    gen_op_mov_reg_v(a_ot, R_ESP, new_esp); +} + +/* two step pop is necessary for precise exceptions */ +static TCGMemOp gen_pop_T0(DisasContext *s) +{ +    TCGMemOp d_ot = mo_pushpop(s, s->dflag); +    TCGv addr = cpu_A0; + +    if (CODE64(s)) { +        addr = cpu_regs[R_ESP]; +    } else if (!s->ss32) { +        tcg_gen_ext16u_tl(cpu_A0, cpu_regs[R_ESP]); +        gen_op_addl_A0_seg(s, R_SS); +    } else if (s->addseg) { +        tcg_gen_mov_tl(cpu_A0, cpu_regs[R_ESP]); +        gen_op_addl_A0_seg(s, R_SS); +    } else { +        tcg_gen_ext32u_tl(cpu_A0, cpu_regs[R_ESP]); +    } + +    gen_op_ld_v(s, d_ot, cpu_T[0], addr); +    return d_ot; +} + +static void gen_pop_update(DisasContext *s, TCGMemOp ot) +{ +    gen_stack_update(s, 1 << ot); +} + +static void gen_stack_A0(DisasContext *s) +{ +    gen_op_movl_A0_reg(R_ESP); +    if (!s->ss32) +        tcg_gen_ext16u_tl(cpu_A0, cpu_A0); +    tcg_gen_mov_tl(cpu_T[1], cpu_A0); +    if (s->addseg) +        gen_op_addl_A0_seg(s, R_SS); +} + +/* NOTE: wrap around in 16 bit not fully handled */ +static void gen_pusha(DisasContext *s) +{ +    int i; +    gen_op_movl_A0_reg(R_ESP); +    gen_op_addl_A0_im(-8 << s->dflag); +    if (!s->ss32) +        tcg_gen_ext16u_tl(cpu_A0, cpu_A0); +    tcg_gen_mov_tl(cpu_T[1], cpu_A0); +    if (s->addseg) +        gen_op_addl_A0_seg(s, R_SS); +    for(i = 0;i < 8; i++) { +        gen_op_mov_v_reg(MO_32, cpu_T[0], 7 - i); +        gen_op_st_v(s, s->dflag, cpu_T[0], cpu_A0); +        gen_op_addl_A0_im(1 << s->dflag); +    } +    gen_op_mov_reg_v(MO_16 + s->ss32, R_ESP, cpu_T[1]); +} + +/* NOTE: wrap around in 16 bit not fully handled */ +static void gen_popa(DisasContext *s) +{ +    int i; +    gen_op_movl_A0_reg(R_ESP); +    if (!s->ss32) +        tcg_gen_ext16u_tl(cpu_A0, cpu_A0); +    tcg_gen_mov_tl(cpu_T[1], cpu_A0); +    tcg_gen_addi_tl(cpu_T[1], cpu_T[1], 8 << s->dflag); +    if (s->addseg) +        gen_op_addl_A0_seg(s, R_SS); +    for(i = 0;i < 8; i++) { +        /* ESP is not reloaded */ +        if (i != 3) { +            gen_op_ld_v(s, s->dflag, cpu_T[0], cpu_A0); +            gen_op_mov_reg_v(s->dflag, 7 - i, cpu_T[0]); +        } +        gen_op_addl_A0_im(1 << s->dflag); +    } +    gen_op_mov_reg_v(MO_16 + s->ss32, R_ESP, cpu_T[1]); +} + +static void gen_enter(DisasContext *s, int esp_addend, int level) +{ +    TCGMemOp ot = mo_pushpop(s, s->dflag); +    int opsize = 1 << ot; + +    level &= 0x1f; +#ifdef TARGET_X86_64 +    if (CODE64(s)) { +        gen_op_movl_A0_reg(R_ESP); +        gen_op_addq_A0_im(-opsize); +        tcg_gen_mov_tl(cpu_T[1], cpu_A0); + +        /* push bp */ +        gen_op_mov_v_reg(MO_32, cpu_T[0], R_EBP); +        gen_op_st_v(s, ot, cpu_T[0], cpu_A0); +        if (level) { +            /* XXX: must save state */ +            gen_helper_enter64_level(cpu_env, tcg_const_i32(level), +                                     tcg_const_i32((ot == MO_64)), +                                     cpu_T[1]); +        } +        gen_op_mov_reg_v(ot, R_EBP, cpu_T[1]); +        tcg_gen_addi_tl(cpu_T[1], cpu_T[1], -esp_addend + (-opsize * level)); +        gen_op_mov_reg_v(MO_64, R_ESP, cpu_T[1]); +    } else +#endif +    { +        gen_op_movl_A0_reg(R_ESP); +        gen_op_addl_A0_im(-opsize); +        if (!s->ss32) +            tcg_gen_ext16u_tl(cpu_A0, cpu_A0); +        tcg_gen_mov_tl(cpu_T[1], cpu_A0); +        if (s->addseg) +            gen_op_addl_A0_seg(s, R_SS); +        /* push bp */ +        gen_op_mov_v_reg(MO_32, cpu_T[0], R_EBP); +        gen_op_st_v(s, ot, cpu_T[0], cpu_A0); +        if (level) { +            /* XXX: must save state */ +            gen_helper_enter_level(cpu_env, tcg_const_i32(level), +                                   tcg_const_i32(s->dflag - 1), +                                   cpu_T[1]); +        } +        gen_op_mov_reg_v(ot, R_EBP, cpu_T[1]); +        tcg_gen_addi_tl(cpu_T[1], cpu_T[1], -esp_addend + (-opsize * level)); +        gen_op_mov_reg_v(MO_16 + s->ss32, R_ESP, cpu_T[1]); +    } +} + +static void gen_exception(DisasContext *s, int trapno, target_ulong cur_eip) +{ +    gen_update_cc_op(s); +    gen_jmp_im(cur_eip); +    gen_helper_raise_exception(cpu_env, tcg_const_i32(trapno)); +    s->is_jmp = DISAS_TB_JUMP; +} + +/* an interrupt is different from an exception because of the +   privilege checks */ +static void gen_interrupt(DisasContext *s, int intno, +                          target_ulong cur_eip, target_ulong next_eip) +{ +    gen_update_cc_op(s); +    gen_jmp_im(cur_eip); +    gen_helper_raise_interrupt(cpu_env, tcg_const_i32(intno), +                               tcg_const_i32(next_eip - cur_eip)); +    s->is_jmp = DISAS_TB_JUMP; +} + +static void gen_debug(DisasContext *s, target_ulong cur_eip) +{ +    gen_update_cc_op(s); +    gen_jmp_im(cur_eip); +    gen_helper_debug(cpu_env); +    s->is_jmp = DISAS_TB_JUMP; +} + +/* generate a generic end of block. Trace exception is also generated +   if needed */ +static void gen_eob(DisasContext *s) +{ +    gen_update_cc_op(s); +    if (s->tb->flags & HF_INHIBIT_IRQ_MASK) { +        gen_helper_reset_inhibit_irq(cpu_env); +    } +    if (s->tb->flags & HF_RF_MASK) { +        gen_helper_reset_rf(cpu_env); +    } +    if (s->singlestep_enabled) { +        gen_helper_debug(cpu_env); +    } else if (s->tf) { +        gen_helper_single_step(cpu_env); +    } else { +        tcg_gen_exit_tb(0); +    } +    s->is_jmp = DISAS_TB_JUMP; +} + +/* generate a jump to eip. No segment change must happen before as a +   direct call to the next block may occur */ +static void gen_jmp_tb(DisasContext *s, target_ulong eip, int tb_num) +{ +    gen_update_cc_op(s); +    set_cc_op(s, CC_OP_DYNAMIC); +    if (s->jmp_opt) { +        gen_goto_tb(s, tb_num, eip); +        s->is_jmp = DISAS_TB_JUMP; +    } else { +        gen_jmp_im(eip); +        gen_eob(s); +    } +} + +static void gen_jmp(DisasContext *s, target_ulong eip) +{ +    gen_jmp_tb(s, eip, 0); +} + +static inline void gen_ldq_env_A0(DisasContext *s, int offset) +{ +    tcg_gen_qemu_ld_i64(cpu_tmp1_i64, cpu_A0, s->mem_index, MO_LEQ); +    tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, offset); +} + +static inline void gen_stq_env_A0(DisasContext *s, int offset) +{ +    tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, offset); +    tcg_gen_qemu_st_i64(cpu_tmp1_i64, cpu_A0, s->mem_index, MO_LEQ); +} + +static inline void gen_ldo_env_A0(DisasContext *s, int offset) +{ +    int mem_index = s->mem_index; +    tcg_gen_qemu_ld_i64(cpu_tmp1_i64, cpu_A0, mem_index, MO_LEQ); +    tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, offset + offsetof(XMMReg, XMM_Q(0))); +    tcg_gen_addi_tl(cpu_tmp0, cpu_A0, 8); +    tcg_gen_qemu_ld_i64(cpu_tmp1_i64, cpu_tmp0, mem_index, MO_LEQ); +    tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, offset + offsetof(XMMReg, XMM_Q(1))); +} + +static inline void gen_sto_env_A0(DisasContext *s, int offset) +{ +    int mem_index = s->mem_index; +    tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, offset + offsetof(XMMReg, XMM_Q(0))); +    tcg_gen_qemu_st_i64(cpu_tmp1_i64, cpu_A0, mem_index, MO_LEQ); +    tcg_gen_addi_tl(cpu_tmp0, cpu_A0, 8); +    tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, offset + offsetof(XMMReg, XMM_Q(1))); +    tcg_gen_qemu_st_i64(cpu_tmp1_i64, cpu_tmp0, mem_index, MO_LEQ); +} + +static inline void gen_op_movo(int d_offset, int s_offset) +{ +    tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, s_offset + offsetof(XMMReg, XMM_Q(0))); +    tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, d_offset + offsetof(XMMReg, XMM_Q(0))); +    tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, s_offset + offsetof(XMMReg, XMM_Q(1))); +    tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, d_offset + offsetof(XMMReg, XMM_Q(1))); +} + +static inline void gen_op_movq(int d_offset, int s_offset) +{ +    tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, s_offset); +    tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, d_offset); +} + +static inline void gen_op_movl(int d_offset, int s_offset) +{ +    tcg_gen_ld_i32(cpu_tmp2_i32, cpu_env, s_offset); +    tcg_gen_st_i32(cpu_tmp2_i32, cpu_env, d_offset); +} + +static inline void gen_op_movq_env_0(int d_offset) +{ +    tcg_gen_movi_i64(cpu_tmp1_i64, 0); +    tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, d_offset); +} + +typedef void (*SSEFunc_i_ep)(TCGv_i32 val, TCGv_ptr env, TCGv_ptr reg); +typedef void (*SSEFunc_l_ep)(TCGv_i64 val, TCGv_ptr env, TCGv_ptr reg); +typedef void (*SSEFunc_0_epi)(TCGv_ptr env, TCGv_ptr reg, TCGv_i32 val); +typedef void (*SSEFunc_0_epl)(TCGv_ptr env, TCGv_ptr reg, TCGv_i64 val); +typedef void (*SSEFunc_0_epp)(TCGv_ptr env, TCGv_ptr reg_a, TCGv_ptr reg_b); +typedef void (*SSEFunc_0_eppi)(TCGv_ptr env, TCGv_ptr reg_a, TCGv_ptr reg_b, +                               TCGv_i32 val); +typedef void (*SSEFunc_0_ppi)(TCGv_ptr reg_a, TCGv_ptr reg_b, TCGv_i32 val); +typedef void (*SSEFunc_0_eppt)(TCGv_ptr env, TCGv_ptr reg_a, TCGv_ptr reg_b, +                               TCGv val); + +#define SSE_SPECIAL ((void *)1) +#define SSE_DUMMY ((void *)2) + +#define MMX_OP2(x) { gen_helper_ ## x ## _mmx, gen_helper_ ## x ## _xmm } +#define SSE_FOP(x) { gen_helper_ ## x ## ps, gen_helper_ ## x ## pd, \ +                     gen_helper_ ## x ## ss, gen_helper_ ## x ## sd, } + +static const SSEFunc_0_epp sse_op_table1[256][4] = { +    /* 3DNow! extensions */ +    [0x0e] = { SSE_DUMMY }, /* femms */ +    [0x0f] = { SSE_DUMMY }, /* pf... */ +    /* pure SSE operations */ +    [0x10] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movups, movupd, movss, movsd */ +    [0x11] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movups, movupd, movss, movsd */ +    [0x12] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movlps, movlpd, movsldup, movddup */ +    [0x13] = { SSE_SPECIAL, SSE_SPECIAL },  /* movlps, movlpd */ +    [0x14] = { gen_helper_punpckldq_xmm, gen_helper_punpcklqdq_xmm }, +    [0x15] = { gen_helper_punpckhdq_xmm, gen_helper_punpckhqdq_xmm }, +    [0x16] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL },  /* movhps, movhpd, movshdup */ +    [0x17] = { SSE_SPECIAL, SSE_SPECIAL },  /* movhps, movhpd */ + +    [0x28] = { SSE_SPECIAL, SSE_SPECIAL },  /* movaps, movapd */ +    [0x29] = { SSE_SPECIAL, SSE_SPECIAL },  /* movaps, movapd */ +    [0x2a] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* cvtpi2ps, cvtpi2pd, cvtsi2ss, cvtsi2sd */ +    [0x2b] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movntps, movntpd, movntss, movntsd */ +    [0x2c] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* cvttps2pi, cvttpd2pi, cvttsd2si, cvttss2si */ +    [0x2d] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* cvtps2pi, cvtpd2pi, cvtsd2si, cvtss2si */ +    [0x2e] = { gen_helper_ucomiss, gen_helper_ucomisd }, +    [0x2f] = { gen_helper_comiss, gen_helper_comisd }, +    [0x50] = { SSE_SPECIAL, SSE_SPECIAL }, /* movmskps, movmskpd */ +    [0x51] = SSE_FOP(sqrt), +    [0x52] = { gen_helper_rsqrtps, NULL, gen_helper_rsqrtss, NULL }, +    [0x53] = { gen_helper_rcpps, NULL, gen_helper_rcpss, NULL }, +    [0x54] = { gen_helper_pand_xmm, gen_helper_pand_xmm }, /* andps, andpd */ +    [0x55] = { gen_helper_pandn_xmm, gen_helper_pandn_xmm }, /* andnps, andnpd */ +    [0x56] = { gen_helper_por_xmm, gen_helper_por_xmm }, /* orps, orpd */ +    [0x57] = { gen_helper_pxor_xmm, gen_helper_pxor_xmm }, /* xorps, xorpd */ +    [0x58] = SSE_FOP(add), +    [0x59] = SSE_FOP(mul), +    [0x5a] = { gen_helper_cvtps2pd, gen_helper_cvtpd2ps, +               gen_helper_cvtss2sd, gen_helper_cvtsd2ss }, +    [0x5b] = { gen_helper_cvtdq2ps, gen_helper_cvtps2dq, gen_helper_cvttps2dq }, +    [0x5c] = SSE_FOP(sub), +    [0x5d] = SSE_FOP(min), +    [0x5e] = SSE_FOP(div), +    [0x5f] = SSE_FOP(max), + +    [0xc2] = SSE_FOP(cmpeq), +    [0xc6] = { (SSEFunc_0_epp)gen_helper_shufps, +               (SSEFunc_0_epp)gen_helper_shufpd }, /* XXX: casts */ + +    /* SSSE3, SSE4, MOVBE, CRC32, BMI1, BMI2, ADX.  */ +    [0x38] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, +    [0x3a] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, + +    /* MMX ops and their SSE extensions */ +    [0x60] = MMX_OP2(punpcklbw), +    [0x61] = MMX_OP2(punpcklwd), +    [0x62] = MMX_OP2(punpckldq), +    [0x63] = MMX_OP2(packsswb), +    [0x64] = MMX_OP2(pcmpgtb), +    [0x65] = MMX_OP2(pcmpgtw), +    [0x66] = MMX_OP2(pcmpgtl), +    [0x67] = MMX_OP2(packuswb), +    [0x68] = MMX_OP2(punpckhbw), +    [0x69] = MMX_OP2(punpckhwd), +    [0x6a] = MMX_OP2(punpckhdq), +    [0x6b] = MMX_OP2(packssdw), +    [0x6c] = { NULL, gen_helper_punpcklqdq_xmm }, +    [0x6d] = { NULL, gen_helper_punpckhqdq_xmm }, +    [0x6e] = { SSE_SPECIAL, SSE_SPECIAL }, /* movd mm, ea */ +    [0x6f] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movq, movdqa, , movqdu */ +    [0x70] = { (SSEFunc_0_epp)gen_helper_pshufw_mmx, +               (SSEFunc_0_epp)gen_helper_pshufd_xmm, +               (SSEFunc_0_epp)gen_helper_pshufhw_xmm, +               (SSEFunc_0_epp)gen_helper_pshuflw_xmm }, /* XXX: casts */ +    [0x71] = { SSE_SPECIAL, SSE_SPECIAL }, /* shiftw */ +    [0x72] = { SSE_SPECIAL, SSE_SPECIAL }, /* shiftd */ +    [0x73] = { SSE_SPECIAL, SSE_SPECIAL }, /* shiftq */ +    [0x74] = MMX_OP2(pcmpeqb), +    [0x75] = MMX_OP2(pcmpeqw), +    [0x76] = MMX_OP2(pcmpeql), +    [0x77] = { SSE_DUMMY }, /* emms */ +    [0x78] = { NULL, SSE_SPECIAL, NULL, SSE_SPECIAL }, /* extrq_i, insertq_i */ +    [0x79] = { NULL, gen_helper_extrq_r, NULL, gen_helper_insertq_r }, +    [0x7c] = { NULL, gen_helper_haddpd, NULL, gen_helper_haddps }, +    [0x7d] = { NULL, gen_helper_hsubpd, NULL, gen_helper_hsubps }, +    [0x7e] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movd, movd, , movq */ +    [0x7f] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movq, movdqa, movdqu */ +    [0xc4] = { SSE_SPECIAL, SSE_SPECIAL }, /* pinsrw */ +    [0xc5] = { SSE_SPECIAL, SSE_SPECIAL }, /* pextrw */ +    [0xd0] = { NULL, gen_helper_addsubpd, NULL, gen_helper_addsubps }, +    [0xd1] = MMX_OP2(psrlw), +    [0xd2] = MMX_OP2(psrld), +    [0xd3] = MMX_OP2(psrlq), +    [0xd4] = MMX_OP2(paddq), +    [0xd5] = MMX_OP2(pmullw), +    [0xd6] = { NULL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, +    [0xd7] = { SSE_SPECIAL, SSE_SPECIAL }, /* pmovmskb */ +    [0xd8] = MMX_OP2(psubusb), +    [0xd9] = MMX_OP2(psubusw), +    [0xda] = MMX_OP2(pminub), +    [0xdb] = MMX_OP2(pand), +    [0xdc] = MMX_OP2(paddusb), +    [0xdd] = MMX_OP2(paddusw), +    [0xde] = MMX_OP2(pmaxub), +    [0xdf] = MMX_OP2(pandn), +    [0xe0] = MMX_OP2(pavgb), +    [0xe1] = MMX_OP2(psraw), +    [0xe2] = MMX_OP2(psrad), +    [0xe3] = MMX_OP2(pavgw), +    [0xe4] = MMX_OP2(pmulhuw), +    [0xe5] = MMX_OP2(pmulhw), +    [0xe6] = { NULL, gen_helper_cvttpd2dq, gen_helper_cvtdq2pd, gen_helper_cvtpd2dq }, +    [0xe7] = { SSE_SPECIAL , SSE_SPECIAL },  /* movntq, movntq */ +    [0xe8] = MMX_OP2(psubsb), +    [0xe9] = MMX_OP2(psubsw), +    [0xea] = MMX_OP2(pminsw), +    [0xeb] = MMX_OP2(por), +    [0xec] = MMX_OP2(paddsb), +    [0xed] = MMX_OP2(paddsw), +    [0xee] = MMX_OP2(pmaxsw), +    [0xef] = MMX_OP2(pxor), +    [0xf0] = { NULL, NULL, NULL, SSE_SPECIAL }, /* lddqu */ +    [0xf1] = MMX_OP2(psllw), +    [0xf2] = MMX_OP2(pslld), +    [0xf3] = MMX_OP2(psllq), +    [0xf4] = MMX_OP2(pmuludq), +    [0xf5] = MMX_OP2(pmaddwd), +    [0xf6] = MMX_OP2(psadbw), +    [0xf7] = { (SSEFunc_0_epp)gen_helper_maskmov_mmx, +               (SSEFunc_0_epp)gen_helper_maskmov_xmm }, /* XXX: casts */ +    [0xf8] = MMX_OP2(psubb), +    [0xf9] = MMX_OP2(psubw), +    [0xfa] = MMX_OP2(psubl), +    [0xfb] = MMX_OP2(psubq), +    [0xfc] = MMX_OP2(paddb), +    [0xfd] = MMX_OP2(paddw), +    [0xfe] = MMX_OP2(paddl), +}; + +static const SSEFunc_0_epp sse_op_table2[3 * 8][2] = { +    [0 + 2] = MMX_OP2(psrlw), +    [0 + 4] = MMX_OP2(psraw), +    [0 + 6] = MMX_OP2(psllw), +    [8 + 2] = MMX_OP2(psrld), +    [8 + 4] = MMX_OP2(psrad), +    [8 + 6] = MMX_OP2(pslld), +    [16 + 2] = MMX_OP2(psrlq), +    [16 + 3] = { NULL, gen_helper_psrldq_xmm }, +    [16 + 6] = MMX_OP2(psllq), +    [16 + 7] = { NULL, gen_helper_pslldq_xmm }, +}; + +static const SSEFunc_0_epi sse_op_table3ai[] = { +    gen_helper_cvtsi2ss, +    gen_helper_cvtsi2sd +}; + +#ifdef TARGET_X86_64 +static const SSEFunc_0_epl sse_op_table3aq[] = { +    gen_helper_cvtsq2ss, +    gen_helper_cvtsq2sd +}; +#endif + +static const SSEFunc_i_ep sse_op_table3bi[] = { +    gen_helper_cvttss2si, +    gen_helper_cvtss2si, +    gen_helper_cvttsd2si, +    gen_helper_cvtsd2si +}; + +#ifdef TARGET_X86_64 +static const SSEFunc_l_ep sse_op_table3bq[] = { +    gen_helper_cvttss2sq, +    gen_helper_cvtss2sq, +    gen_helper_cvttsd2sq, +    gen_helper_cvtsd2sq +}; +#endif + +static const SSEFunc_0_epp sse_op_table4[8][4] = { +    SSE_FOP(cmpeq), +    SSE_FOP(cmplt), +    SSE_FOP(cmple), +    SSE_FOP(cmpunord), +    SSE_FOP(cmpneq), +    SSE_FOP(cmpnlt), +    SSE_FOP(cmpnle), +    SSE_FOP(cmpord), +}; + +static const SSEFunc_0_epp sse_op_table5[256] = { +    [0x0c] = gen_helper_pi2fw, +    [0x0d] = gen_helper_pi2fd, +    [0x1c] = gen_helper_pf2iw, +    [0x1d] = gen_helper_pf2id, +    [0x8a] = gen_helper_pfnacc, +    [0x8e] = gen_helper_pfpnacc, +    [0x90] = gen_helper_pfcmpge, +    [0x94] = gen_helper_pfmin, +    [0x96] = gen_helper_pfrcp, +    [0x97] = gen_helper_pfrsqrt, +    [0x9a] = gen_helper_pfsub, +    [0x9e] = gen_helper_pfadd, +    [0xa0] = gen_helper_pfcmpgt, +    [0xa4] = gen_helper_pfmax, +    [0xa6] = gen_helper_movq, /* pfrcpit1; no need to actually increase precision */ +    [0xa7] = gen_helper_movq, /* pfrsqit1 */ +    [0xaa] = gen_helper_pfsubr, +    [0xae] = gen_helper_pfacc, +    [0xb0] = gen_helper_pfcmpeq, +    [0xb4] = gen_helper_pfmul, +    [0xb6] = gen_helper_movq, /* pfrcpit2 */ +    [0xb7] = gen_helper_pmulhrw_mmx, +    [0xbb] = gen_helper_pswapd, +    [0xbf] = gen_helper_pavgb_mmx /* pavgusb */ +}; + +struct SSEOpHelper_epp { +    SSEFunc_0_epp op[2]; +    uint32_t ext_mask; +}; + +struct SSEOpHelper_eppi { +    SSEFunc_0_eppi op[2]; +    uint32_t ext_mask; +}; + +#define SSSE3_OP(x) { MMX_OP2(x), CPUID_EXT_SSSE3 } +#define SSE41_OP(x) { { NULL, gen_helper_ ## x ## _xmm }, CPUID_EXT_SSE41 } +#define SSE42_OP(x) { { NULL, gen_helper_ ## x ## _xmm }, CPUID_EXT_SSE42 } +#define SSE41_SPECIAL { { NULL, SSE_SPECIAL }, CPUID_EXT_SSE41 } +#define PCLMULQDQ_OP(x) { { NULL, gen_helper_ ## x ## _xmm }, \ +        CPUID_EXT_PCLMULQDQ } +#define AESNI_OP(x) { { NULL, gen_helper_ ## x ## _xmm }, CPUID_EXT_AES } + +static const struct SSEOpHelper_epp sse_op_table6[256] = { +    [0x00] = SSSE3_OP(pshufb), +    [0x01] = SSSE3_OP(phaddw), +    [0x02] = SSSE3_OP(phaddd), +    [0x03] = SSSE3_OP(phaddsw), +    [0x04] = SSSE3_OP(pmaddubsw), +    [0x05] = SSSE3_OP(phsubw), +    [0x06] = SSSE3_OP(phsubd), +    [0x07] = SSSE3_OP(phsubsw), +    [0x08] = SSSE3_OP(psignb), +    [0x09] = SSSE3_OP(psignw), +    [0x0a] = SSSE3_OP(psignd), +    [0x0b] = SSSE3_OP(pmulhrsw), +    [0x10] = SSE41_OP(pblendvb), +    [0x14] = SSE41_OP(blendvps), +    [0x15] = SSE41_OP(blendvpd), +    [0x17] = SSE41_OP(ptest), +    [0x1c] = SSSE3_OP(pabsb), +    [0x1d] = SSSE3_OP(pabsw), +    [0x1e] = SSSE3_OP(pabsd), +    [0x20] = SSE41_OP(pmovsxbw), +    [0x21] = SSE41_OP(pmovsxbd), +    [0x22] = SSE41_OP(pmovsxbq), +    [0x23] = SSE41_OP(pmovsxwd), +    [0x24] = SSE41_OP(pmovsxwq), +    [0x25] = SSE41_OP(pmovsxdq), +    [0x28] = SSE41_OP(pmuldq), +    [0x29] = SSE41_OP(pcmpeqq), +    [0x2a] = SSE41_SPECIAL, /* movntqda */ +    [0x2b] = SSE41_OP(packusdw), +    [0x30] = SSE41_OP(pmovzxbw), +    [0x31] = SSE41_OP(pmovzxbd), +    [0x32] = SSE41_OP(pmovzxbq), +    [0x33] = SSE41_OP(pmovzxwd), +    [0x34] = SSE41_OP(pmovzxwq), +    [0x35] = SSE41_OP(pmovzxdq), +    [0x37] = SSE42_OP(pcmpgtq), +    [0x38] = SSE41_OP(pminsb), +    [0x39] = SSE41_OP(pminsd), +    [0x3a] = SSE41_OP(pminuw), +    [0x3b] = SSE41_OP(pminud), +    [0x3c] = SSE41_OP(pmaxsb), +    [0x3d] = SSE41_OP(pmaxsd), +    [0x3e] = SSE41_OP(pmaxuw), +    [0x3f] = SSE41_OP(pmaxud), +    [0x40] = SSE41_OP(pmulld), +    [0x41] = SSE41_OP(phminposuw), +    [0xdb] = AESNI_OP(aesimc), +    [0xdc] = AESNI_OP(aesenc), +    [0xdd] = AESNI_OP(aesenclast), +    [0xde] = AESNI_OP(aesdec), +    [0xdf] = AESNI_OP(aesdeclast), +}; + +static const struct SSEOpHelper_eppi sse_op_table7[256] = { +    [0x08] = SSE41_OP(roundps), +    [0x09] = SSE41_OP(roundpd), +    [0x0a] = SSE41_OP(roundss), +    [0x0b] = SSE41_OP(roundsd), +    [0x0c] = SSE41_OP(blendps), +    [0x0d] = SSE41_OP(blendpd), +    [0x0e] = SSE41_OP(pblendw), +    [0x0f] = SSSE3_OP(palignr), +    [0x14] = SSE41_SPECIAL, /* pextrb */ +    [0x15] = SSE41_SPECIAL, /* pextrw */ +    [0x16] = SSE41_SPECIAL, /* pextrd/pextrq */ +    [0x17] = SSE41_SPECIAL, /* extractps */ +    [0x20] = SSE41_SPECIAL, /* pinsrb */ +    [0x21] = SSE41_SPECIAL, /* insertps */ +    [0x22] = SSE41_SPECIAL, /* pinsrd/pinsrq */ +    [0x40] = SSE41_OP(dpps), +    [0x41] = SSE41_OP(dppd), +    [0x42] = SSE41_OP(mpsadbw), +    [0x44] = PCLMULQDQ_OP(pclmulqdq), +    [0x60] = SSE42_OP(pcmpestrm), +    [0x61] = SSE42_OP(pcmpestri), +    [0x62] = SSE42_OP(pcmpistrm), +    [0x63] = SSE42_OP(pcmpistri), +    [0xdf] = AESNI_OP(aeskeygenassist), +}; + +static void gen_sse(CPUX86State *env, DisasContext *s, int b, +                    target_ulong pc_start, int rex_r) +{ +    int b1, op1_offset, op2_offset, is_xmm, val; +    int modrm, mod, rm, reg; +    SSEFunc_0_epp sse_fn_epp; +    SSEFunc_0_eppi sse_fn_eppi; +    SSEFunc_0_ppi sse_fn_ppi; +    SSEFunc_0_eppt sse_fn_eppt; +    TCGMemOp ot; + +    b &= 0xff; +    if (s->prefix & PREFIX_DATA) +        b1 = 1; +    else if (s->prefix & PREFIX_REPZ) +        b1 = 2; +    else if (s->prefix & PREFIX_REPNZ) +        b1 = 3; +    else +        b1 = 0; +    sse_fn_epp = sse_op_table1[b][b1]; +    if (!sse_fn_epp) { +        goto illegal_op; +    } +    if ((b <= 0x5f && b >= 0x10) || b == 0xc6 || b == 0xc2) { +        is_xmm = 1; +    } else { +        if (b1 == 0) { +            /* MMX case */ +            is_xmm = 0; +        } else { +            is_xmm = 1; +        } +    } +    /* simple MMX/SSE operation */ +    if (s->flags & HF_TS_MASK) { +        gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); +        return; +    } +    if (s->flags & HF_EM_MASK) { +    illegal_op: +        gen_exception(s, EXCP06_ILLOP, pc_start - s->cs_base); +        return; +    } +    if (is_xmm && !(s->flags & HF_OSFXSR_MASK)) +        if ((b != 0x38 && b != 0x3a) || (s->prefix & PREFIX_DATA)) +            goto illegal_op; +    if (b == 0x0e) { +        if (!(s->cpuid_ext2_features & CPUID_EXT2_3DNOW)) +            goto illegal_op; +        /* femms */ +        gen_helper_emms(cpu_env); +        return; +    } +    if (b == 0x77) { +        /* emms */ +        gen_helper_emms(cpu_env); +        return; +    } +    /* prepare MMX state (XXX: optimize by storing fptt and fptags in +       the static cpu state) */ +    if (!is_xmm) { +        gen_helper_enter_mmx(cpu_env); +    } + +    modrm = cpu_ldub_code(env, s->pc++); +    reg = ((modrm >> 3) & 7); +    if (is_xmm) +        reg |= rex_r; +    mod = (modrm >> 6) & 3; +    if (sse_fn_epp == SSE_SPECIAL) { +        b |= (b1 << 8); +        switch(b) { +        case 0x0e7: /* movntq */ +            if (mod == 3) +                goto illegal_op; +            gen_lea_modrm(env, s, modrm); +            gen_stq_env_A0(s, offsetof(CPUX86State, fpregs[reg].mmx)); +            break; +        case 0x1e7: /* movntdq */ +        case 0x02b: /* movntps */ +        case 0x12b: /* movntps */ +            if (mod == 3) +                goto illegal_op; +            gen_lea_modrm(env, s, modrm); +            gen_sto_env_A0(s, offsetof(CPUX86State, xmm_regs[reg])); +            break; +        case 0x3f0: /* lddqu */ +            if (mod == 3) +                goto illegal_op; +            gen_lea_modrm(env, s, modrm); +            gen_ldo_env_A0(s, offsetof(CPUX86State, xmm_regs[reg])); +            break; +        case 0x22b: /* movntss */ +        case 0x32b: /* movntsd */ +            if (mod == 3) +                goto illegal_op; +            gen_lea_modrm(env, s, modrm); +            if (b1 & 1) { +                gen_stq_env_A0(s, offsetof(CPUX86State, +                                           xmm_regs[reg].XMM_Q(0))); +            } else { +                tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, +                    xmm_regs[reg].XMM_L(0))); +                gen_op_st_v(s, MO_32, cpu_T[0], cpu_A0); +            } +            break; +        case 0x6e: /* movd mm, ea */ +#ifdef TARGET_X86_64 +            if (s->dflag == MO_64) { +                gen_ldst_modrm(env, s, modrm, MO_64, OR_TMP0, 0); +                tcg_gen_st_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,fpregs[reg].mmx)); +            } else +#endif +            { +                gen_ldst_modrm(env, s, modrm, MO_32, OR_TMP0, 0); +                tcg_gen_addi_ptr(cpu_ptr0, cpu_env,  +                                 offsetof(CPUX86State,fpregs[reg].mmx)); +                tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); +                gen_helper_movl_mm_T0_mmx(cpu_ptr0, cpu_tmp2_i32); +            } +            break; +        case 0x16e: /* movd xmm, ea */ +#ifdef TARGET_X86_64 +            if (s->dflag == MO_64) { +                gen_ldst_modrm(env, s, modrm, MO_64, OR_TMP0, 0); +                tcg_gen_addi_ptr(cpu_ptr0, cpu_env,  +                                 offsetof(CPUX86State,xmm_regs[reg])); +                gen_helper_movq_mm_T0_xmm(cpu_ptr0, cpu_T[0]); +            } else +#endif +            { +                gen_ldst_modrm(env, s, modrm, MO_32, OR_TMP0, 0); +                tcg_gen_addi_ptr(cpu_ptr0, cpu_env,  +                                 offsetof(CPUX86State,xmm_regs[reg])); +                tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); +                gen_helper_movl_mm_T0_xmm(cpu_ptr0, cpu_tmp2_i32); +            } +            break; +        case 0x6f: /* movq mm, ea */ +            if (mod != 3) { +                gen_lea_modrm(env, s, modrm); +                gen_ldq_env_A0(s, offsetof(CPUX86State, fpregs[reg].mmx)); +            } else { +                rm = (modrm & 7); +                tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, +                               offsetof(CPUX86State,fpregs[rm].mmx)); +                tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, +                               offsetof(CPUX86State,fpregs[reg].mmx)); +            } +            break; +        case 0x010: /* movups */ +        case 0x110: /* movupd */ +        case 0x028: /* movaps */ +        case 0x128: /* movapd */ +        case 0x16f: /* movdqa xmm, ea */ +        case 0x26f: /* movdqu xmm, ea */ +            if (mod != 3) { +                gen_lea_modrm(env, s, modrm); +                gen_ldo_env_A0(s, offsetof(CPUX86State, xmm_regs[reg])); +            } else { +                rm = (modrm & 7) | REX_B(s); +                gen_op_movo(offsetof(CPUX86State,xmm_regs[reg]), +                            offsetof(CPUX86State,xmm_regs[rm])); +            } +            break; +        case 0x210: /* movss xmm, ea */ +            if (mod != 3) { +                gen_lea_modrm(env, s, modrm); +                gen_op_ld_v(s, MO_32, cpu_T[0], cpu_A0); +                tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(0))); +                tcg_gen_movi_tl(cpu_T[0], 0); +                tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(1))); +                tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(2))); +                tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(3))); +            } else { +                rm = (modrm & 7) | REX_B(s); +                gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(0)), +                            offsetof(CPUX86State,xmm_regs[rm].XMM_L(0))); +            } +            break; +        case 0x310: /* movsd xmm, ea */ +            if (mod != 3) { +                gen_lea_modrm(env, s, modrm); +                gen_ldq_env_A0(s, offsetof(CPUX86State, +                                           xmm_regs[reg].XMM_Q(0))); +                tcg_gen_movi_tl(cpu_T[0], 0); +                tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(2))); +                tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(3))); +            } else { +                rm = (modrm & 7) | REX_B(s); +                gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)), +                            offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0))); +            } +            break; +        case 0x012: /* movlps */ +        case 0x112: /* movlpd */ +            if (mod != 3) { +                gen_lea_modrm(env, s, modrm); +                gen_ldq_env_A0(s, offsetof(CPUX86State, +                                           xmm_regs[reg].XMM_Q(0))); +            } else { +                /* movhlps */ +                rm = (modrm & 7) | REX_B(s); +                gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)), +                            offsetof(CPUX86State,xmm_regs[rm].XMM_Q(1))); +            } +            break; +        case 0x212: /* movsldup */ +            if (mod != 3) { +                gen_lea_modrm(env, s, modrm); +                gen_ldo_env_A0(s, offsetof(CPUX86State, xmm_regs[reg])); +            } else { +                rm = (modrm & 7) | REX_B(s); +                gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(0)), +                            offsetof(CPUX86State,xmm_regs[rm].XMM_L(0))); +                gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(2)), +                            offsetof(CPUX86State,xmm_regs[rm].XMM_L(2))); +            } +            gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(1)), +                        offsetof(CPUX86State,xmm_regs[reg].XMM_L(0))); +            gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(3)), +                        offsetof(CPUX86State,xmm_regs[reg].XMM_L(2))); +            break; +        case 0x312: /* movddup */ +            if (mod != 3) { +                gen_lea_modrm(env, s, modrm); +                gen_ldq_env_A0(s, offsetof(CPUX86State, +                                           xmm_regs[reg].XMM_Q(0))); +            } else { +                rm = (modrm & 7) | REX_B(s); +                gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)), +                            offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0))); +            } +            gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(1)), +                        offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0))); +            break; +        case 0x016: /* movhps */ +        case 0x116: /* movhpd */ +            if (mod != 3) { +                gen_lea_modrm(env, s, modrm); +                gen_ldq_env_A0(s, offsetof(CPUX86State, +                                           xmm_regs[reg].XMM_Q(1))); +            } else { +                /* movlhps */ +                rm = (modrm & 7) | REX_B(s); +                gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(1)), +                            offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0))); +            } +            break; +        case 0x216: /* movshdup */ +            if (mod != 3) { +                gen_lea_modrm(env, s, modrm); +                gen_ldo_env_A0(s, offsetof(CPUX86State, xmm_regs[reg])); +            } else { +                rm = (modrm & 7) | REX_B(s); +                gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(1)), +                            offsetof(CPUX86State,xmm_regs[rm].XMM_L(1))); +                gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(3)), +                            offsetof(CPUX86State,xmm_regs[rm].XMM_L(3))); +            } +            gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(0)), +                        offsetof(CPUX86State,xmm_regs[reg].XMM_L(1))); +            gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].XMM_L(2)), +                        offsetof(CPUX86State,xmm_regs[reg].XMM_L(3))); +            break; +        case 0x178: +        case 0x378: +            { +                int bit_index, field_length; + +                if (b1 == 1 && reg != 0) +                    goto illegal_op; +                field_length = cpu_ldub_code(env, s->pc++) & 0x3F; +                bit_index = cpu_ldub_code(env, s->pc++) & 0x3F; +                tcg_gen_addi_ptr(cpu_ptr0, cpu_env, +                    offsetof(CPUX86State,xmm_regs[reg])); +                if (b1 == 1) +                    gen_helper_extrq_i(cpu_env, cpu_ptr0, +                                       tcg_const_i32(bit_index), +                                       tcg_const_i32(field_length)); +                else +                    gen_helper_insertq_i(cpu_env, cpu_ptr0, +                                         tcg_const_i32(bit_index), +                                         tcg_const_i32(field_length)); +            } +            break; +        case 0x7e: /* movd ea, mm */ +#ifdef TARGET_X86_64 +            if (s->dflag == MO_64) { +                tcg_gen_ld_i64(cpu_T[0], cpu_env,  +                               offsetof(CPUX86State,fpregs[reg].mmx)); +                gen_ldst_modrm(env, s, modrm, MO_64, OR_TMP0, 1); +            } else +#endif +            { +                tcg_gen_ld32u_tl(cpu_T[0], cpu_env,  +                                 offsetof(CPUX86State,fpregs[reg].mmx.MMX_L(0))); +                gen_ldst_modrm(env, s, modrm, MO_32, OR_TMP0, 1); +            } +            break; +        case 0x17e: /* movd ea, xmm */ +#ifdef TARGET_X86_64 +            if (s->dflag == MO_64) { +                tcg_gen_ld_i64(cpu_T[0], cpu_env,  +                               offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0))); +                gen_ldst_modrm(env, s, modrm, MO_64, OR_TMP0, 1); +            } else +#endif +            { +                tcg_gen_ld32u_tl(cpu_T[0], cpu_env,  +                                 offsetof(CPUX86State,xmm_regs[reg].XMM_L(0))); +                gen_ldst_modrm(env, s, modrm, MO_32, OR_TMP0, 1); +            } +            break; +        case 0x27e: /* movq xmm, ea */ +            if (mod != 3) { +                gen_lea_modrm(env, s, modrm); +                gen_ldq_env_A0(s, offsetof(CPUX86State, +                                           xmm_regs[reg].XMM_Q(0))); +            } else { +                rm = (modrm & 7) | REX_B(s); +                gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)), +                            offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0))); +            } +            gen_op_movq_env_0(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(1))); +            break; +        case 0x7f: /* movq ea, mm */ +            if (mod != 3) { +                gen_lea_modrm(env, s, modrm); +                gen_stq_env_A0(s, offsetof(CPUX86State, fpregs[reg].mmx)); +            } else { +                rm = (modrm & 7); +                gen_op_movq(offsetof(CPUX86State,fpregs[rm].mmx), +                            offsetof(CPUX86State,fpregs[reg].mmx)); +            } +            break; +        case 0x011: /* movups */ +        case 0x111: /* movupd */ +        case 0x029: /* movaps */ +        case 0x129: /* movapd */ +        case 0x17f: /* movdqa ea, xmm */ +        case 0x27f: /* movdqu ea, xmm */ +            if (mod != 3) { +                gen_lea_modrm(env, s, modrm); +                gen_sto_env_A0(s, offsetof(CPUX86State, xmm_regs[reg])); +            } else { +                rm = (modrm & 7) | REX_B(s); +                gen_op_movo(offsetof(CPUX86State,xmm_regs[rm]), +                            offsetof(CPUX86State,xmm_regs[reg])); +            } +            break; +        case 0x211: /* movss ea, xmm */ +            if (mod != 3) { +                gen_lea_modrm(env, s, modrm); +                tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_regs[reg].XMM_L(0))); +                gen_op_st_v(s, MO_32, cpu_T[0], cpu_A0); +            } else { +                rm = (modrm & 7) | REX_B(s); +                gen_op_movl(offsetof(CPUX86State,xmm_regs[rm].XMM_L(0)), +                            offsetof(CPUX86State,xmm_regs[reg].XMM_L(0))); +            } +            break; +        case 0x311: /* movsd ea, xmm */ +            if (mod != 3) { +                gen_lea_modrm(env, s, modrm); +                gen_stq_env_A0(s, offsetof(CPUX86State, +                                           xmm_regs[reg].XMM_Q(0))); +            } else { +                rm = (modrm & 7) | REX_B(s); +                gen_op_movq(offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0)), +                            offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0))); +            } +            break; +        case 0x013: /* movlps */ +        case 0x113: /* movlpd */ +            if (mod != 3) { +                gen_lea_modrm(env, s, modrm); +                gen_stq_env_A0(s, offsetof(CPUX86State, +                                           xmm_regs[reg].XMM_Q(0))); +            } else { +                goto illegal_op; +            } +            break; +        case 0x017: /* movhps */ +        case 0x117: /* movhpd */ +            if (mod != 3) { +                gen_lea_modrm(env, s, modrm); +                gen_stq_env_A0(s, offsetof(CPUX86State, +                                           xmm_regs[reg].XMM_Q(1))); +            } else { +                goto illegal_op; +            } +            break; +        case 0x71: /* shift mm, im */ +        case 0x72: +        case 0x73: +        case 0x171: /* shift xmm, im */ +        case 0x172: +        case 0x173: +            if (b1 >= 2) { +	        goto illegal_op; +            } +            val = cpu_ldub_code(env, s->pc++); +            if (is_xmm) { +                tcg_gen_movi_tl(cpu_T[0], val); +                tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_t0.XMM_L(0))); +                tcg_gen_movi_tl(cpu_T[0], 0); +                tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_t0.XMM_L(1))); +                op1_offset = offsetof(CPUX86State,xmm_t0); +            } else { +                tcg_gen_movi_tl(cpu_T[0], val); +                tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,mmx_t0.MMX_L(0))); +                tcg_gen_movi_tl(cpu_T[0], 0); +                tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,mmx_t0.MMX_L(1))); +                op1_offset = offsetof(CPUX86State,mmx_t0); +            } +            sse_fn_epp = sse_op_table2[((b - 1) & 3) * 8 + +                                       (((modrm >> 3)) & 7)][b1]; +            if (!sse_fn_epp) { +                goto illegal_op; +            } +            if (is_xmm) { +                rm = (modrm & 7) | REX_B(s); +                op2_offset = offsetof(CPUX86State,xmm_regs[rm]); +            } else { +                rm = (modrm & 7); +                op2_offset = offsetof(CPUX86State,fpregs[rm].mmx); +            } +            tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op2_offset); +            tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op1_offset); +            sse_fn_epp(cpu_env, cpu_ptr0, cpu_ptr1); +            break; +        case 0x050: /* movmskps */ +            rm = (modrm & 7) | REX_B(s); +            tcg_gen_addi_ptr(cpu_ptr0, cpu_env,  +                             offsetof(CPUX86State,xmm_regs[rm])); +            gen_helper_movmskps(cpu_tmp2_i32, cpu_env, cpu_ptr0); +            tcg_gen_extu_i32_tl(cpu_regs[reg], cpu_tmp2_i32); +            break; +        case 0x150: /* movmskpd */ +            rm = (modrm & 7) | REX_B(s); +            tcg_gen_addi_ptr(cpu_ptr0, cpu_env,  +                             offsetof(CPUX86State,xmm_regs[rm])); +            gen_helper_movmskpd(cpu_tmp2_i32, cpu_env, cpu_ptr0); +            tcg_gen_extu_i32_tl(cpu_regs[reg], cpu_tmp2_i32); +            break; +        case 0x02a: /* cvtpi2ps */ +        case 0x12a: /* cvtpi2pd */ +            gen_helper_enter_mmx(cpu_env); +            if (mod != 3) { +                gen_lea_modrm(env, s, modrm); +                op2_offset = offsetof(CPUX86State,mmx_t0); +                gen_ldq_env_A0(s, op2_offset); +            } else { +                rm = (modrm & 7); +                op2_offset = offsetof(CPUX86State,fpregs[rm].mmx); +            } +            op1_offset = offsetof(CPUX86State,xmm_regs[reg]); +            tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset); +            tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op2_offset); +            switch(b >> 8) { +            case 0x0: +                gen_helper_cvtpi2ps(cpu_env, cpu_ptr0, cpu_ptr1); +                break; +            default: +            case 0x1: +                gen_helper_cvtpi2pd(cpu_env, cpu_ptr0, cpu_ptr1); +                break; +            } +            break; +        case 0x22a: /* cvtsi2ss */ +        case 0x32a: /* cvtsi2sd */ +            ot = mo_64_32(s->dflag); +            gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); +            op1_offset = offsetof(CPUX86State,xmm_regs[reg]); +            tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset); +            if (ot == MO_32) { +                SSEFunc_0_epi sse_fn_epi = sse_op_table3ai[(b >> 8) & 1]; +                tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); +                sse_fn_epi(cpu_env, cpu_ptr0, cpu_tmp2_i32); +            } else { +#ifdef TARGET_X86_64 +                SSEFunc_0_epl sse_fn_epl = sse_op_table3aq[(b >> 8) & 1]; +                sse_fn_epl(cpu_env, cpu_ptr0, cpu_T[0]); +#else +                goto illegal_op; +#endif +            } +            break; +        case 0x02c: /* cvttps2pi */ +        case 0x12c: /* cvttpd2pi */ +        case 0x02d: /* cvtps2pi */ +        case 0x12d: /* cvtpd2pi */ +            gen_helper_enter_mmx(cpu_env); +            if (mod != 3) { +                gen_lea_modrm(env, s, modrm); +                op2_offset = offsetof(CPUX86State,xmm_t0); +                gen_ldo_env_A0(s, op2_offset); +            } else { +                rm = (modrm & 7) | REX_B(s); +                op2_offset = offsetof(CPUX86State,xmm_regs[rm]); +            } +            op1_offset = offsetof(CPUX86State,fpregs[reg & 7].mmx); +            tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset); +            tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op2_offset); +            switch(b) { +            case 0x02c: +                gen_helper_cvttps2pi(cpu_env, cpu_ptr0, cpu_ptr1); +                break; +            case 0x12c: +                gen_helper_cvttpd2pi(cpu_env, cpu_ptr0, cpu_ptr1); +                break; +            case 0x02d: +                gen_helper_cvtps2pi(cpu_env, cpu_ptr0, cpu_ptr1); +                break; +            case 0x12d: +                gen_helper_cvtpd2pi(cpu_env, cpu_ptr0, cpu_ptr1); +                break; +            } +            break; +        case 0x22c: /* cvttss2si */ +        case 0x32c: /* cvttsd2si */ +        case 0x22d: /* cvtss2si */ +        case 0x32d: /* cvtsd2si */ +            ot = mo_64_32(s->dflag); +            if (mod != 3) { +                gen_lea_modrm(env, s, modrm); +                if ((b >> 8) & 1) { +                    gen_ldq_env_A0(s, offsetof(CPUX86State, xmm_t0.XMM_Q(0))); +                } else { +                    gen_op_ld_v(s, MO_32, cpu_T[0], cpu_A0); +                    tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_t0.XMM_L(0))); +                } +                op2_offset = offsetof(CPUX86State,xmm_t0); +            } else { +                rm = (modrm & 7) | REX_B(s); +                op2_offset = offsetof(CPUX86State,xmm_regs[rm]); +            } +            tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op2_offset); +            if (ot == MO_32) { +                SSEFunc_i_ep sse_fn_i_ep = +                    sse_op_table3bi[((b >> 7) & 2) | (b & 1)]; +                sse_fn_i_ep(cpu_tmp2_i32, cpu_env, cpu_ptr0); +                tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32); +            } else { +#ifdef TARGET_X86_64 +                SSEFunc_l_ep sse_fn_l_ep = +                    sse_op_table3bq[((b >> 7) & 2) | (b & 1)]; +                sse_fn_l_ep(cpu_T[0], cpu_env, cpu_ptr0); +#else +                goto illegal_op; +#endif +            } +            gen_op_mov_reg_v(ot, reg, cpu_T[0]); +            break; +        case 0xc4: /* pinsrw */ +        case 0x1c4: +            s->rip_offset = 1; +            gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); +            val = cpu_ldub_code(env, s->pc++); +            if (b1) { +                val &= 7; +                tcg_gen_st16_tl(cpu_T[0], cpu_env, +                                offsetof(CPUX86State,xmm_regs[reg].XMM_W(val))); +            } else { +                val &= 3; +                tcg_gen_st16_tl(cpu_T[0], cpu_env, +                                offsetof(CPUX86State,fpregs[reg].mmx.MMX_W(val))); +            } +            break; +        case 0xc5: /* pextrw */ +        case 0x1c5: +            if (mod != 3) +                goto illegal_op; +            ot = mo_64_32(s->dflag); +            val = cpu_ldub_code(env, s->pc++); +            if (b1) { +                val &= 7; +                rm = (modrm & 7) | REX_B(s); +                tcg_gen_ld16u_tl(cpu_T[0], cpu_env, +                                 offsetof(CPUX86State,xmm_regs[rm].XMM_W(val))); +            } else { +                val &= 3; +                rm = (modrm & 7); +                tcg_gen_ld16u_tl(cpu_T[0], cpu_env, +                                offsetof(CPUX86State,fpregs[rm].mmx.MMX_W(val))); +            } +            reg = ((modrm >> 3) & 7) | rex_r; +            gen_op_mov_reg_v(ot, reg, cpu_T[0]); +            break; +        case 0x1d6: /* movq ea, xmm */ +            if (mod != 3) { +                gen_lea_modrm(env, s, modrm); +                gen_stq_env_A0(s, offsetof(CPUX86State, +                                           xmm_regs[reg].XMM_Q(0))); +            } else { +                rm = (modrm & 7) | REX_B(s); +                gen_op_movq(offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0)), +                            offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0))); +                gen_op_movq_env_0(offsetof(CPUX86State,xmm_regs[rm].XMM_Q(1))); +            } +            break; +        case 0x2d6: /* movq2dq */ +            gen_helper_enter_mmx(cpu_env); +            rm = (modrm & 7); +            gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(0)), +                        offsetof(CPUX86State,fpregs[rm].mmx)); +            gen_op_movq_env_0(offsetof(CPUX86State,xmm_regs[reg].XMM_Q(1))); +            break; +        case 0x3d6: /* movdq2q */ +            gen_helper_enter_mmx(cpu_env); +            rm = (modrm & 7) | REX_B(s); +            gen_op_movq(offsetof(CPUX86State,fpregs[reg & 7].mmx), +                        offsetof(CPUX86State,xmm_regs[rm].XMM_Q(0))); +            break; +        case 0xd7: /* pmovmskb */ +        case 0x1d7: +            if (mod != 3) +                goto illegal_op; +            if (b1) { +                rm = (modrm & 7) | REX_B(s); +                tcg_gen_addi_ptr(cpu_ptr0, cpu_env, offsetof(CPUX86State,xmm_regs[rm])); +                gen_helper_pmovmskb_xmm(cpu_tmp2_i32, cpu_env, cpu_ptr0); +            } else { +                rm = (modrm & 7); +                tcg_gen_addi_ptr(cpu_ptr0, cpu_env, offsetof(CPUX86State,fpregs[rm].mmx)); +                gen_helper_pmovmskb_mmx(cpu_tmp2_i32, cpu_env, cpu_ptr0); +            } +            reg = ((modrm >> 3) & 7) | rex_r; +            tcg_gen_extu_i32_tl(cpu_regs[reg], cpu_tmp2_i32); +            break; + +        case 0x138: +        case 0x038: +            b = modrm; +            if ((b & 0xf0) == 0xf0) { +                goto do_0f_38_fx; +            } +            modrm = cpu_ldub_code(env, s->pc++); +            rm = modrm & 7; +            reg = ((modrm >> 3) & 7) | rex_r; +            mod = (modrm >> 6) & 3; +            if (b1 >= 2) { +                goto illegal_op; +            } + +            sse_fn_epp = sse_op_table6[b].op[b1]; +            if (!sse_fn_epp) { +                goto illegal_op; +            } +            if (!(s->cpuid_ext_features & sse_op_table6[b].ext_mask)) +                goto illegal_op; + +            if (b1) { +                op1_offset = offsetof(CPUX86State,xmm_regs[reg]); +                if (mod == 3) { +                    op2_offset = offsetof(CPUX86State,xmm_regs[rm | REX_B(s)]); +                } else { +                    op2_offset = offsetof(CPUX86State,xmm_t0); +                    gen_lea_modrm(env, s, modrm); +                    switch (b) { +                    case 0x20: case 0x30: /* pmovsxbw, pmovzxbw */ +                    case 0x23: case 0x33: /* pmovsxwd, pmovzxwd */ +                    case 0x25: case 0x35: /* pmovsxdq, pmovzxdq */ +                        gen_ldq_env_A0(s, op2_offset + +                                        offsetof(XMMReg, XMM_Q(0))); +                        break; +                    case 0x21: case 0x31: /* pmovsxbd, pmovzxbd */ +                    case 0x24: case 0x34: /* pmovsxwq, pmovzxwq */ +                        tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0, +                                            s->mem_index, MO_LEUL); +                        tcg_gen_st_i32(cpu_tmp2_i32, cpu_env, op2_offset + +                                        offsetof(XMMReg, XMM_L(0))); +                        break; +                    case 0x22: case 0x32: /* pmovsxbq, pmovzxbq */ +                        tcg_gen_qemu_ld_tl(cpu_tmp0, cpu_A0, +                                           s->mem_index, MO_LEUW); +                        tcg_gen_st16_tl(cpu_tmp0, cpu_env, op2_offset + +                                        offsetof(XMMReg, XMM_W(0))); +                        break; +                    case 0x2a:            /* movntqda */ +                        gen_ldo_env_A0(s, op1_offset); +                        return; +                    default: +                        gen_ldo_env_A0(s, op2_offset); +                    } +                } +            } else { +                op1_offset = offsetof(CPUX86State,fpregs[reg].mmx); +                if (mod == 3) { +                    op2_offset = offsetof(CPUX86State,fpregs[rm].mmx); +                } else { +                    op2_offset = offsetof(CPUX86State,mmx_t0); +                    gen_lea_modrm(env, s, modrm); +                    gen_ldq_env_A0(s, op2_offset); +                } +            } +            if (sse_fn_epp == SSE_SPECIAL) { +                goto illegal_op; +            } + +            tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset); +            tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op2_offset); +            sse_fn_epp(cpu_env, cpu_ptr0, cpu_ptr1); + +            if (b == 0x17) { +                set_cc_op(s, CC_OP_EFLAGS); +            } +            break; + +        case 0x238: +        case 0x338: +        do_0f_38_fx: +            /* Various integer extensions at 0f 38 f[0-f].  */ +            b = modrm | (b1 << 8); +            modrm = cpu_ldub_code(env, s->pc++); +            reg = ((modrm >> 3) & 7) | rex_r; + +            switch (b) { +            case 0x3f0: /* crc32 Gd,Eb */ +            case 0x3f1: /* crc32 Gd,Ey */ +            do_crc32: +                if (!(s->cpuid_ext_features & CPUID_EXT_SSE42)) { +                    goto illegal_op; +                } +                if ((b & 0xff) == 0xf0) { +                    ot = MO_8; +                } else if (s->dflag != MO_64) { +                    ot = (s->prefix & PREFIX_DATA ? MO_16 : MO_32); +                } else { +                    ot = MO_64; +                } + +                tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_regs[reg]); +                gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); +                gen_helper_crc32(cpu_T[0], cpu_tmp2_i32, +                                 cpu_T[0], tcg_const_i32(8 << ot)); + +                ot = mo_64_32(s->dflag); +                gen_op_mov_reg_v(ot, reg, cpu_T[0]); +                break; + +            case 0x1f0: /* crc32 or movbe */ +            case 0x1f1: +                /* For these insns, the f3 prefix is supposed to have priority +                   over the 66 prefix, but that's not what we implement above +                   setting b1.  */ +                if (s->prefix & PREFIX_REPNZ) { +                    goto do_crc32; +                } +                /* FALLTHRU */ +            case 0x0f0: /* movbe Gy,My */ +            case 0x0f1: /* movbe My,Gy */ +                if (!(s->cpuid_ext_features & CPUID_EXT_MOVBE)) { +                    goto illegal_op; +                } +                if (s->dflag != MO_64) { +                    ot = (s->prefix & PREFIX_DATA ? MO_16 : MO_32); +                } else { +                    ot = MO_64; +                } + +                gen_lea_modrm(env, s, modrm); +                if ((b & 1) == 0) { +                    tcg_gen_qemu_ld_tl(cpu_T[0], cpu_A0, +                                       s->mem_index, ot | MO_BE); +                    gen_op_mov_reg_v(ot, reg, cpu_T[0]); +                } else { +                    tcg_gen_qemu_st_tl(cpu_regs[reg], cpu_A0, +                                       s->mem_index, ot | MO_BE); +                } +                break; + +            case 0x0f2: /* andn Gy, By, Ey */ +                if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI1) +                    || !(s->prefix & PREFIX_VEX) +                    || s->vex_l != 0) { +                    goto illegal_op; +                } +                ot = mo_64_32(s->dflag); +                gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); +                tcg_gen_andc_tl(cpu_T[0], cpu_regs[s->vex_v], cpu_T[0]); +                gen_op_mov_reg_v(ot, reg, cpu_T[0]); +                gen_op_update1_cc(); +                set_cc_op(s, CC_OP_LOGICB + ot); +                break; + +            case 0x0f7: /* bextr Gy, Ey, By */ +                if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI1) +                    || !(s->prefix & PREFIX_VEX) +                    || s->vex_l != 0) { +                    goto illegal_op; +                } +                ot = mo_64_32(s->dflag); +                { +                    TCGv bound, zero; + +                    gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); +                    /* Extract START, and shift the operand. +                       Shifts larger than operand size get zeros.  */ +                    tcg_gen_ext8u_tl(cpu_A0, cpu_regs[s->vex_v]); +                    tcg_gen_shr_tl(cpu_T[0], cpu_T[0], cpu_A0); + +                    bound = tcg_const_tl(ot == MO_64 ? 63 : 31); +                    zero = tcg_const_tl(0); +                    tcg_gen_movcond_tl(TCG_COND_LEU, cpu_T[0], cpu_A0, bound, +                                       cpu_T[0], zero); +                    tcg_temp_free(zero); + +                    /* Extract the LEN into a mask.  Lengths larger than +                       operand size get all ones.  */ +                    tcg_gen_shri_tl(cpu_A0, cpu_regs[s->vex_v], 8); +                    tcg_gen_ext8u_tl(cpu_A0, cpu_A0); +                    tcg_gen_movcond_tl(TCG_COND_LEU, cpu_A0, cpu_A0, bound, +                                       cpu_A0, bound); +                    tcg_temp_free(bound); +                    tcg_gen_movi_tl(cpu_T[1], 1); +                    tcg_gen_shl_tl(cpu_T[1], cpu_T[1], cpu_A0); +                    tcg_gen_subi_tl(cpu_T[1], cpu_T[1], 1); +                    tcg_gen_and_tl(cpu_T[0], cpu_T[0], cpu_T[1]); + +                    gen_op_mov_reg_v(ot, reg, cpu_T[0]); +                    gen_op_update1_cc(); +                    set_cc_op(s, CC_OP_LOGICB + ot); +                } +                break; + +            case 0x0f5: /* bzhi Gy, Ey, By */ +                if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2) +                    || !(s->prefix & PREFIX_VEX) +                    || s->vex_l != 0) { +                    goto illegal_op; +                } +                ot = mo_64_32(s->dflag); +                gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); +                tcg_gen_ext8u_tl(cpu_T[1], cpu_regs[s->vex_v]); +                { +                    TCGv bound = tcg_const_tl(ot == MO_64 ? 63 : 31); +                    /* Note that since we're using BMILG (in order to get O +                       cleared) we need to store the inverse into C.  */ +                    tcg_gen_setcond_tl(TCG_COND_LT, cpu_cc_src, +                                       cpu_T[1], bound); +                    tcg_gen_movcond_tl(TCG_COND_GT, cpu_T[1], cpu_T[1], +                                       bound, bound, cpu_T[1]); +                    tcg_temp_free(bound); +                } +                tcg_gen_movi_tl(cpu_A0, -1); +                tcg_gen_shl_tl(cpu_A0, cpu_A0, cpu_T[1]); +                tcg_gen_andc_tl(cpu_T[0], cpu_T[0], cpu_A0); +                gen_op_mov_reg_v(ot, reg, cpu_T[0]); +                gen_op_update1_cc(); +                set_cc_op(s, CC_OP_BMILGB + ot); +                break; + +            case 0x3f6: /* mulx By, Gy, rdx, Ey */ +                if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2) +                    || !(s->prefix & PREFIX_VEX) +                    || s->vex_l != 0) { +                    goto illegal_op; +                } +                ot = mo_64_32(s->dflag); +                gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); +                switch (ot) { +                default: +                    tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); +                    tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_regs[R_EDX]); +                    tcg_gen_mulu2_i32(cpu_tmp2_i32, cpu_tmp3_i32, +                                      cpu_tmp2_i32, cpu_tmp3_i32); +                    tcg_gen_extu_i32_tl(cpu_regs[s->vex_v], cpu_tmp2_i32); +                    tcg_gen_extu_i32_tl(cpu_regs[reg], cpu_tmp3_i32); +                    break; +#ifdef TARGET_X86_64 +                case MO_64: +                    tcg_gen_mulu2_i64(cpu_regs[s->vex_v], cpu_regs[reg], +                                      cpu_T[0], cpu_regs[R_EDX]); +                    break; +#endif +                } +                break; + +            case 0x3f5: /* pdep Gy, By, Ey */ +                if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2) +                    || !(s->prefix & PREFIX_VEX) +                    || s->vex_l != 0) { +                    goto illegal_op; +                } +                ot = mo_64_32(s->dflag); +                gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); +                /* Note that by zero-extending the mask operand, we +                   automatically handle zero-extending the result.  */ +                if (ot == MO_64) { +                    tcg_gen_mov_tl(cpu_T[1], cpu_regs[s->vex_v]); +                } else { +                    tcg_gen_ext32u_tl(cpu_T[1], cpu_regs[s->vex_v]); +                } +                gen_helper_pdep(cpu_regs[reg], cpu_T[0], cpu_T[1]); +                break; + +            case 0x2f5: /* pext Gy, By, Ey */ +                if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2) +                    || !(s->prefix & PREFIX_VEX) +                    || s->vex_l != 0) { +                    goto illegal_op; +                } +                ot = mo_64_32(s->dflag); +                gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); +                /* Note that by zero-extending the mask operand, we +                   automatically handle zero-extending the result.  */ +                if (ot == MO_64) { +                    tcg_gen_mov_tl(cpu_T[1], cpu_regs[s->vex_v]); +                } else { +                    tcg_gen_ext32u_tl(cpu_T[1], cpu_regs[s->vex_v]); +                } +                gen_helper_pext(cpu_regs[reg], cpu_T[0], cpu_T[1]); +                break; + +            case 0x1f6: /* adcx Gy, Ey */ +            case 0x2f6: /* adox Gy, Ey */ +                if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_ADX)) { +                    goto illegal_op; +                } else { +                    TCGv carry_in, carry_out, zero; +                    int end_op; + +                    ot = mo_64_32(s->dflag); +                    gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + +                    /* Re-use the carry-out from a previous round.  */ +                    TCGV_UNUSED(carry_in); +                    carry_out = (b == 0x1f6 ? cpu_cc_dst : cpu_cc_src2); +                    switch (s->cc_op) { +                    case CC_OP_ADCX: +                        if (b == 0x1f6) { +                            carry_in = cpu_cc_dst; +                            end_op = CC_OP_ADCX; +                        } else { +                            end_op = CC_OP_ADCOX; +                        } +                        break; +                    case CC_OP_ADOX: +                        if (b == 0x1f6) { +                            end_op = CC_OP_ADCOX; +                        } else { +                            carry_in = cpu_cc_src2; +                            end_op = CC_OP_ADOX; +                        } +                        break; +                    case CC_OP_ADCOX: +                        end_op = CC_OP_ADCOX; +                        carry_in = carry_out; +                        break; +                    default: +                        end_op = (b == 0x1f6 ? CC_OP_ADCX : CC_OP_ADOX); +                        break; +                    } +                    /* If we can't reuse carry-out, get it out of EFLAGS.  */ +                    if (TCGV_IS_UNUSED(carry_in)) { +                        if (s->cc_op != CC_OP_ADCX && s->cc_op != CC_OP_ADOX) { +                            gen_compute_eflags(s); +                        } +                        carry_in = cpu_tmp0; +                        tcg_gen_shri_tl(carry_in, cpu_cc_src, +                                        ctz32(b == 0x1f6 ? CC_C : CC_O)); +                        tcg_gen_andi_tl(carry_in, carry_in, 1); +                    } + +                    switch (ot) { +#ifdef TARGET_X86_64 +                    case MO_32: +                        /* If we know TL is 64-bit, and we want a 32-bit +                           result, just do everything in 64-bit arithmetic.  */ +                        tcg_gen_ext32u_i64(cpu_regs[reg], cpu_regs[reg]); +                        tcg_gen_ext32u_i64(cpu_T[0], cpu_T[0]); +                        tcg_gen_add_i64(cpu_T[0], cpu_T[0], cpu_regs[reg]); +                        tcg_gen_add_i64(cpu_T[0], cpu_T[0], carry_in); +                        tcg_gen_ext32u_i64(cpu_regs[reg], cpu_T[0]); +                        tcg_gen_shri_i64(carry_out, cpu_T[0], 32); +                        break; +#endif +                    default: +                        /* Otherwise compute the carry-out in two steps.  */ +                        zero = tcg_const_tl(0); +                        tcg_gen_add2_tl(cpu_T[0], carry_out, +                                        cpu_T[0], zero, +                                        carry_in, zero); +                        tcg_gen_add2_tl(cpu_regs[reg], carry_out, +                                        cpu_regs[reg], carry_out, +                                        cpu_T[0], zero); +                        tcg_temp_free(zero); +                        break; +                    } +                    set_cc_op(s, end_op); +                } +                break; + +            case 0x1f7: /* shlx Gy, Ey, By */ +            case 0x2f7: /* sarx Gy, Ey, By */ +            case 0x3f7: /* shrx Gy, Ey, By */ +                if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2) +                    || !(s->prefix & PREFIX_VEX) +                    || s->vex_l != 0) { +                    goto illegal_op; +                } +                ot = mo_64_32(s->dflag); +                gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); +                if (ot == MO_64) { +                    tcg_gen_andi_tl(cpu_T[1], cpu_regs[s->vex_v], 63); +                } else { +                    tcg_gen_andi_tl(cpu_T[1], cpu_regs[s->vex_v], 31); +                } +                if (b == 0x1f7) { +                    tcg_gen_shl_tl(cpu_T[0], cpu_T[0], cpu_T[1]); +                } else if (b == 0x2f7) { +                    if (ot != MO_64) { +                        tcg_gen_ext32s_tl(cpu_T[0], cpu_T[0]); +                    } +                    tcg_gen_sar_tl(cpu_T[0], cpu_T[0], cpu_T[1]); +                } else { +                    if (ot != MO_64) { +                        tcg_gen_ext32u_tl(cpu_T[0], cpu_T[0]); +                    } +                    tcg_gen_shr_tl(cpu_T[0], cpu_T[0], cpu_T[1]); +                } +                gen_op_mov_reg_v(ot, reg, cpu_T[0]); +                break; + +            case 0x0f3: +            case 0x1f3: +            case 0x2f3: +            case 0x3f3: /* Group 17 */ +                if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI1) +                    || !(s->prefix & PREFIX_VEX) +                    || s->vex_l != 0) { +                    goto illegal_op; +                } +                ot = mo_64_32(s->dflag); +                gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + +                switch (reg & 7) { +                case 1: /* blsr By,Ey */ +                    tcg_gen_neg_tl(cpu_T[1], cpu_T[0]); +                    tcg_gen_and_tl(cpu_T[0], cpu_T[0], cpu_T[1]); +                    gen_op_mov_reg_v(ot, s->vex_v, cpu_T[0]); +                    gen_op_update2_cc(); +                    set_cc_op(s, CC_OP_BMILGB + ot); +                    break; + +                case 2: /* blsmsk By,Ey */ +                    tcg_gen_mov_tl(cpu_cc_src, cpu_T[0]); +                    tcg_gen_subi_tl(cpu_T[0], cpu_T[0], 1); +                    tcg_gen_xor_tl(cpu_T[0], cpu_T[0], cpu_cc_src); +                    tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); +                    set_cc_op(s, CC_OP_BMILGB + ot); +                    break; + +                case 3: /* blsi By, Ey */ +                    tcg_gen_mov_tl(cpu_cc_src, cpu_T[0]); +                    tcg_gen_subi_tl(cpu_T[0], cpu_T[0], 1); +                    tcg_gen_and_tl(cpu_T[0], cpu_T[0], cpu_cc_src); +                    tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); +                    set_cc_op(s, CC_OP_BMILGB + ot); +                    break; + +                default: +                    goto illegal_op; +                } +                break; + +            default: +                goto illegal_op; +            } +            break; + +        case 0x03a: +        case 0x13a: +            b = modrm; +            modrm = cpu_ldub_code(env, s->pc++); +            rm = modrm & 7; +            reg = ((modrm >> 3) & 7) | rex_r; +            mod = (modrm >> 6) & 3; +            if (b1 >= 2) { +                goto illegal_op; +            } + +            sse_fn_eppi = sse_op_table7[b].op[b1]; +            if (!sse_fn_eppi) { +                goto illegal_op; +            } +            if (!(s->cpuid_ext_features & sse_op_table7[b].ext_mask)) +                goto illegal_op; + +            if (sse_fn_eppi == SSE_SPECIAL) { +                ot = mo_64_32(s->dflag); +                rm = (modrm & 7) | REX_B(s); +                if (mod != 3) +                    gen_lea_modrm(env, s, modrm); +                reg = ((modrm >> 3) & 7) | rex_r; +                val = cpu_ldub_code(env, s->pc++); +                switch (b) { +                case 0x14: /* pextrb */ +                    tcg_gen_ld8u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, +                                            xmm_regs[reg].XMM_B(val & 15))); +                    if (mod == 3) { +                        gen_op_mov_reg_v(ot, rm, cpu_T[0]); +                    } else { +                        tcg_gen_qemu_st_tl(cpu_T[0], cpu_A0, +                                           s->mem_index, MO_UB); +                    } +                    break; +                case 0x15: /* pextrw */ +                    tcg_gen_ld16u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, +                                            xmm_regs[reg].XMM_W(val & 7))); +                    if (mod == 3) { +                        gen_op_mov_reg_v(ot, rm, cpu_T[0]); +                    } else { +                        tcg_gen_qemu_st_tl(cpu_T[0], cpu_A0, +                                           s->mem_index, MO_LEUW); +                    } +                    break; +                case 0x16: +                    if (ot == MO_32) { /* pextrd */ +                        tcg_gen_ld_i32(cpu_tmp2_i32, cpu_env, +                                        offsetof(CPUX86State, +                                                xmm_regs[reg].XMM_L(val & 3))); +                        if (mod == 3) { +                            tcg_gen_extu_i32_tl(cpu_regs[rm], cpu_tmp2_i32); +                        } else { +                            tcg_gen_qemu_st_i32(cpu_tmp2_i32, cpu_A0, +                                                s->mem_index, MO_LEUL); +                        } +                    } else { /* pextrq */ +#ifdef TARGET_X86_64 +                        tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, +                                        offsetof(CPUX86State, +                                                xmm_regs[reg].XMM_Q(val & 1))); +                        if (mod == 3) { +                            tcg_gen_mov_i64(cpu_regs[rm], cpu_tmp1_i64); +                        } else { +                            tcg_gen_qemu_st_i64(cpu_tmp1_i64, cpu_A0, +                                                s->mem_index, MO_LEQ); +                        } +#else +                        goto illegal_op; +#endif +                    } +                    break; +                case 0x17: /* extractps */ +                    tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, +                                            xmm_regs[reg].XMM_L(val & 3))); +                    if (mod == 3) { +                        gen_op_mov_reg_v(ot, rm, cpu_T[0]); +                    } else { +                        tcg_gen_qemu_st_tl(cpu_T[0], cpu_A0, +                                           s->mem_index, MO_LEUL); +                    } +                    break; +                case 0x20: /* pinsrb */ +                    if (mod == 3) { +                        gen_op_mov_v_reg(MO_32, cpu_T[0], rm); +                    } else { +                        tcg_gen_qemu_ld_tl(cpu_T[0], cpu_A0, +                                           s->mem_index, MO_UB); +                    } +                    tcg_gen_st8_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, +                                            xmm_regs[reg].XMM_B(val & 15))); +                    break; +                case 0x21: /* insertps */ +                    if (mod == 3) { +                        tcg_gen_ld_i32(cpu_tmp2_i32, cpu_env, +                                        offsetof(CPUX86State,xmm_regs[rm] +                                                .XMM_L((val >> 6) & 3))); +                    } else { +                        tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0, +                                            s->mem_index, MO_LEUL); +                    } +                    tcg_gen_st_i32(cpu_tmp2_i32, cpu_env, +                                    offsetof(CPUX86State,xmm_regs[reg] +                                            .XMM_L((val >> 4) & 3))); +                    if ((val >> 0) & 1) +                        tcg_gen_st_i32(tcg_const_i32(0 /*float32_zero*/), +                                        cpu_env, offsetof(CPUX86State, +                                                xmm_regs[reg].XMM_L(0))); +                    if ((val >> 1) & 1) +                        tcg_gen_st_i32(tcg_const_i32(0 /*float32_zero*/), +                                        cpu_env, offsetof(CPUX86State, +                                                xmm_regs[reg].XMM_L(1))); +                    if ((val >> 2) & 1) +                        tcg_gen_st_i32(tcg_const_i32(0 /*float32_zero*/), +                                        cpu_env, offsetof(CPUX86State, +                                                xmm_regs[reg].XMM_L(2))); +                    if ((val >> 3) & 1) +                        tcg_gen_st_i32(tcg_const_i32(0 /*float32_zero*/), +                                        cpu_env, offsetof(CPUX86State, +                                                xmm_regs[reg].XMM_L(3))); +                    break; +                case 0x22: +                    if (ot == MO_32) { /* pinsrd */ +                        if (mod == 3) { +                            tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_regs[rm]); +                        } else { +                            tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0, +                                                s->mem_index, MO_LEUL); +                        } +                        tcg_gen_st_i32(cpu_tmp2_i32, cpu_env, +                                        offsetof(CPUX86State, +                                                xmm_regs[reg].XMM_L(val & 3))); +                    } else { /* pinsrq */ +#ifdef TARGET_X86_64 +                        if (mod == 3) { +                            gen_op_mov_v_reg(ot, cpu_tmp1_i64, rm); +                        } else { +                            tcg_gen_qemu_ld_i64(cpu_tmp1_i64, cpu_A0, +                                                s->mem_index, MO_LEQ); +                        } +                        tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, +                                        offsetof(CPUX86State, +                                                xmm_regs[reg].XMM_Q(val & 1))); +#else +                        goto illegal_op; +#endif +                    } +                    break; +                } +                return; +            } + +            if (b1) { +                op1_offset = offsetof(CPUX86State,xmm_regs[reg]); +                if (mod == 3) { +                    op2_offset = offsetof(CPUX86State,xmm_regs[rm | REX_B(s)]); +                } else { +                    op2_offset = offsetof(CPUX86State,xmm_t0); +                    gen_lea_modrm(env, s, modrm); +                    gen_ldo_env_A0(s, op2_offset); +                } +            } else { +                op1_offset = offsetof(CPUX86State,fpregs[reg].mmx); +                if (mod == 3) { +                    op2_offset = offsetof(CPUX86State,fpregs[rm].mmx); +                } else { +                    op2_offset = offsetof(CPUX86State,mmx_t0); +                    gen_lea_modrm(env, s, modrm); +                    gen_ldq_env_A0(s, op2_offset); +                } +            } +            val = cpu_ldub_code(env, s->pc++); + +            if ((b & 0xfc) == 0x60) { /* pcmpXstrX */ +                set_cc_op(s, CC_OP_EFLAGS); + +                if (s->dflag == MO_64) { +                    /* The helper must use entire 64-bit gp registers */ +                    val |= 1 << 8; +                } +            } + +            tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset); +            tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op2_offset); +            sse_fn_eppi(cpu_env, cpu_ptr0, cpu_ptr1, tcg_const_i32(val)); +            break; + +        case 0x33a: +            /* Various integer extensions at 0f 3a f[0-f].  */ +            b = modrm | (b1 << 8); +            modrm = cpu_ldub_code(env, s->pc++); +            reg = ((modrm >> 3) & 7) | rex_r; + +            switch (b) { +            case 0x3f0: /* rorx Gy,Ey, Ib */ +                if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2) +                    || !(s->prefix & PREFIX_VEX) +                    || s->vex_l != 0) { +                    goto illegal_op; +                } +                ot = mo_64_32(s->dflag); +                gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); +                b = cpu_ldub_code(env, s->pc++); +                if (ot == MO_64) { +                    tcg_gen_rotri_tl(cpu_T[0], cpu_T[0], b & 63); +                } else { +                    tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); +                    tcg_gen_rotri_i32(cpu_tmp2_i32, cpu_tmp2_i32, b & 31); +                    tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32); +                } +                gen_op_mov_reg_v(ot, reg, cpu_T[0]); +                break; + +            default: +                goto illegal_op; +            } +            break; + +        default: +            goto illegal_op; +        } +    } else { +        /* generic MMX or SSE operation */ +        switch(b) { +        case 0x70: /* pshufx insn */ +        case 0xc6: /* pshufx insn */ +        case 0xc2: /* compare insns */ +            s->rip_offset = 1; +            break; +        default: +            break; +        } +        if (is_xmm) { +            op1_offset = offsetof(CPUX86State,xmm_regs[reg]); +            if (mod != 3) { +                int sz = 4; + +                gen_lea_modrm(env, s, modrm); +                op2_offset = offsetof(CPUX86State,xmm_t0); + +                switch (b) { +                case 0x50 ... 0x5a: +                case 0x5c ... 0x5f: +                case 0xc2: +                    /* Most sse scalar operations.  */ +                    if (b1 == 2) { +                        sz = 2; +                    } else if (b1 == 3) { +                        sz = 3; +                    } +                    break; + +                case 0x2e:  /* ucomis[sd] */ +                case 0x2f:  /* comis[sd] */ +                    if (b1 == 0) { +                        sz = 2; +                    } else { +                        sz = 3; +                    } +                    break; +                } + +                switch (sz) { +                case 2: +                    /* 32 bit access */ +                    gen_op_ld_v(s, MO_32, cpu_T[0], cpu_A0); +                    tcg_gen_st32_tl(cpu_T[0], cpu_env, +                                    offsetof(CPUX86State,xmm_t0.XMM_L(0))); +                    break; +                case 3: +                    /* 64 bit access */ +                    gen_ldq_env_A0(s, offsetof(CPUX86State, xmm_t0.XMM_D(0))); +                    break; +                default: +                    /* 128 bit access */ +                    gen_ldo_env_A0(s, op2_offset); +                    break; +                } +            } else { +                rm = (modrm & 7) | REX_B(s); +                op2_offset = offsetof(CPUX86State,xmm_regs[rm]); +            } +        } else { +            op1_offset = offsetof(CPUX86State,fpregs[reg].mmx); +            if (mod != 3) { +                gen_lea_modrm(env, s, modrm); +                op2_offset = offsetof(CPUX86State,mmx_t0); +                gen_ldq_env_A0(s, op2_offset); +            } else { +                rm = (modrm & 7); +                op2_offset = offsetof(CPUX86State,fpregs[rm].mmx); +            } +        } +        switch(b) { +        case 0x0f: /* 3DNow! data insns */ +            if (!(s->cpuid_ext2_features & CPUID_EXT2_3DNOW)) +                goto illegal_op; +            val = cpu_ldub_code(env, s->pc++); +            sse_fn_epp = sse_op_table5[val]; +            if (!sse_fn_epp) { +                goto illegal_op; +            } +            tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset); +            tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op2_offset); +            sse_fn_epp(cpu_env, cpu_ptr0, cpu_ptr1); +            break; +        case 0x70: /* pshufx insn */ +        case 0xc6: /* pshufx insn */ +            val = cpu_ldub_code(env, s->pc++); +            tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset); +            tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op2_offset); +            /* XXX: introduce a new table? */ +            sse_fn_ppi = (SSEFunc_0_ppi)sse_fn_epp; +            sse_fn_ppi(cpu_ptr0, cpu_ptr1, tcg_const_i32(val)); +            break; +        case 0xc2: +            /* compare insns */ +            val = cpu_ldub_code(env, s->pc++); +            if (val >= 8) +                goto illegal_op; +            sse_fn_epp = sse_op_table4[val][b1]; + +            tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset); +            tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op2_offset); +            sse_fn_epp(cpu_env, cpu_ptr0, cpu_ptr1); +            break; +        case 0xf7: +            /* maskmov : we must prepare A0 */ +            if (mod != 3) +                goto illegal_op; +            tcg_gen_mov_tl(cpu_A0, cpu_regs[R_EDI]); +            gen_extu(s->aflag, cpu_A0); +            gen_add_A0_ds_seg(s); + +            tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset); +            tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op2_offset); +            /* XXX: introduce a new table? */ +            sse_fn_eppt = (SSEFunc_0_eppt)sse_fn_epp; +            sse_fn_eppt(cpu_env, cpu_ptr0, cpu_ptr1, cpu_A0); +            break; +        default: +            tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset); +            tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op2_offset); +            sse_fn_epp(cpu_env, cpu_ptr0, cpu_ptr1); +            break; +        } +        if (b == 0x2e || b == 0x2f) { +            set_cc_op(s, CC_OP_EFLAGS); +        } +    } +} + +/* convert one instruction. s->is_jmp is set if the translation must +   be stopped. Return the next pc value */ +static target_ulong disas_insn(CPUX86State *env, DisasContext *s, +                               target_ulong pc_start) +{ +    int b, prefixes; +    int shift; +    TCGMemOp ot, aflag, dflag; +    int modrm, reg, rm, mod, op, opreg, val; +    target_ulong next_eip, tval; +    int rex_w, rex_r; + +    if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { +        tcg_gen_debug_insn_start(pc_start); +    } +    s->pc = pc_start; +    prefixes = 0; +    s->override = -1; +    rex_w = -1; +    rex_r = 0; +#ifdef TARGET_X86_64 +    s->rex_x = 0; +    s->rex_b = 0; +    x86_64_hregs = 0; +#endif +    s->rip_offset = 0; /* for relative ip address */ +    s->vex_l = 0; +    s->vex_v = 0; + next_byte: +    b = cpu_ldub_code(env, s->pc); +    s->pc++; +    /* Collect prefixes.  */ +    switch (b) { +    case 0xf3: +        prefixes |= PREFIX_REPZ; +        goto next_byte; +    case 0xf2: +        prefixes |= PREFIX_REPNZ; +        goto next_byte; +    case 0xf0: +        prefixes |= PREFIX_LOCK; +        goto next_byte; +    case 0x2e: +        s->override = R_CS; +        goto next_byte; +    case 0x36: +        s->override = R_SS; +        goto next_byte; +    case 0x3e: +        s->override = R_DS; +        goto next_byte; +    case 0x26: +        s->override = R_ES; +        goto next_byte; +    case 0x64: +        s->override = R_FS; +        goto next_byte; +    case 0x65: +        s->override = R_GS; +        goto next_byte; +    case 0x66: +        prefixes |= PREFIX_DATA; +        goto next_byte; +    case 0x67: +        prefixes |= PREFIX_ADR; +        goto next_byte; +#ifdef TARGET_X86_64 +    case 0x40 ... 0x4f: +        if (CODE64(s)) { +            /* REX prefix */ +            rex_w = (b >> 3) & 1; +            rex_r = (b & 0x4) << 1; +            s->rex_x = (b & 0x2) << 2; +            REX_B(s) = (b & 0x1) << 3; +            x86_64_hregs = 1; /* select uniform byte register addressing */ +            goto next_byte; +        } +        break; +#endif +    case 0xc5: /* 2-byte VEX */ +    case 0xc4: /* 3-byte VEX */ +        /* VEX prefixes cannot be used except in 32-bit mode. +           Otherwise the instruction is LES or LDS.  */ +        if (s->code32 && !s->vm86) { +            static const int pp_prefix[4] = { +                0, PREFIX_DATA, PREFIX_REPZ, PREFIX_REPNZ +            }; +            int vex3, vex2 = cpu_ldub_code(env, s->pc); + +            if (!CODE64(s) && (vex2 & 0xc0) != 0xc0) { +                /* 4.1.4.6: In 32-bit mode, bits [7:6] must be 11b, +                   otherwise the instruction is LES or LDS.  */ +                break; +            } +            s->pc++; + +            /* 4.1.1-4.1.3: No preceding lock, 66, f2, f3, or rex prefixes. */ +            if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ +                            | PREFIX_LOCK | PREFIX_DATA)) { +                goto illegal_op; +            } +#ifdef TARGET_X86_64 +            if (x86_64_hregs) { +                goto illegal_op; +            } +#endif +            rex_r = (~vex2 >> 4) & 8; +            if (b == 0xc5) { +                vex3 = vex2; +                b = cpu_ldub_code(env, s->pc++); +            } else { +#ifdef TARGET_X86_64 +                s->rex_x = (~vex2 >> 3) & 8; +                s->rex_b = (~vex2 >> 2) & 8; +#endif +                vex3 = cpu_ldub_code(env, s->pc++); +                rex_w = (vex3 >> 7) & 1; +                switch (vex2 & 0x1f) { +                case 0x01: /* Implied 0f leading opcode bytes.  */ +                    b = cpu_ldub_code(env, s->pc++) | 0x100; +                    break; +                case 0x02: /* Implied 0f 38 leading opcode bytes.  */ +                    b = 0x138; +                    break; +                case 0x03: /* Implied 0f 3a leading opcode bytes.  */ +                    b = 0x13a; +                    break; +                default:   /* Reserved for future use.  */ +                    goto illegal_op; +                } +            } +            s->vex_v = (~vex3 >> 3) & 0xf; +            s->vex_l = (vex3 >> 2) & 1; +            prefixes |= pp_prefix[vex3 & 3] | PREFIX_VEX; +        } +        break; +    } + +    /* Post-process prefixes.  */ +    if (CODE64(s)) { +        /* In 64-bit mode, the default data size is 32-bit.  Select 64-bit +           data with rex_w, and 16-bit data with 0x66; rex_w takes precedence +           over 0x66 if both are present.  */ +        dflag = (rex_w > 0 ? MO_64 : prefixes & PREFIX_DATA ? MO_16 : MO_32); +        /* In 64-bit mode, 0x67 selects 32-bit addressing.  */ +        aflag = (prefixes & PREFIX_ADR ? MO_32 : MO_64); +    } else { +        /* In 16/32-bit mode, 0x66 selects the opposite data size.  */ +        if (s->code32 ^ ((prefixes & PREFIX_DATA) != 0)) { +            dflag = MO_32; +        } else { +            dflag = MO_16; +        } +        /* In 16/32-bit mode, 0x67 selects the opposite addressing.  */ +        if (s->code32 ^ ((prefixes & PREFIX_ADR) != 0)) { +            aflag = MO_32; +        }  else { +            aflag = MO_16; +        } +    } + +    s->prefix = prefixes; +    s->aflag = aflag; +    s->dflag = dflag; + +    /* lock generation */ +    if (prefixes & PREFIX_LOCK) +        gen_helper_lock(); + +    /* now check op code */ + reswitch: +    switch(b) { +    case 0x0f: +        /**************************/ +        /* extended op code */ +        b = cpu_ldub_code(env, s->pc++) | 0x100; +        goto reswitch; + +        /**************************/ +        /* arith & logic */ +    case 0x00 ... 0x05: +    case 0x08 ... 0x0d: +    case 0x10 ... 0x15: +    case 0x18 ... 0x1d: +    case 0x20 ... 0x25: +    case 0x28 ... 0x2d: +    case 0x30 ... 0x35: +    case 0x38 ... 0x3d: +        { +            int op, f, val; +            op = (b >> 3) & 7; +            f = (b >> 1) & 3; + +            ot = mo_b_d(b, dflag); + +            switch(f) { +            case 0: /* OP Ev, Gv */ +                modrm = cpu_ldub_code(env, s->pc++); +                reg = ((modrm >> 3) & 7) | rex_r; +                mod = (modrm >> 6) & 3; +                rm = (modrm & 7) | REX_B(s); +                if (mod != 3) { +                    gen_lea_modrm(env, s, modrm); +                    opreg = OR_TMP0; +                } else if (op == OP_XORL && rm == reg) { +                xor_zero: +                    /* xor reg, reg optimisation */ +                    set_cc_op(s, CC_OP_CLR); +                    tcg_gen_movi_tl(cpu_T[0], 0); +                    gen_op_mov_reg_v(ot, reg, cpu_T[0]); +                    break; +                } else { +                    opreg = rm; +                } +                gen_op_mov_v_reg(ot, cpu_T[1], reg); +                gen_op(s, op, ot, opreg); +                break; +            case 1: /* OP Gv, Ev */ +                modrm = cpu_ldub_code(env, s->pc++); +                mod = (modrm >> 6) & 3; +                reg = ((modrm >> 3) & 7) | rex_r; +                rm = (modrm & 7) | REX_B(s); +                if (mod != 3) { +                    gen_lea_modrm(env, s, modrm); +                    gen_op_ld_v(s, ot, cpu_T[1], cpu_A0); +                } else if (op == OP_XORL && rm == reg) { +                    goto xor_zero; +                } else { +                    gen_op_mov_v_reg(ot, cpu_T[1], rm); +                } +                gen_op(s, op, ot, reg); +                break; +            case 2: /* OP A, Iv */ +                val = insn_get(env, s, ot); +                tcg_gen_movi_tl(cpu_T[1], val); +                gen_op(s, op, ot, OR_EAX); +                break; +            } +        } +        break; + +    case 0x82: +        if (CODE64(s)) +            goto illegal_op; +    case 0x80: /* GRP1 */ +    case 0x81: +    case 0x83: +        { +            int val; + +            ot = mo_b_d(b, dflag); + +            modrm = cpu_ldub_code(env, s->pc++); +            mod = (modrm >> 6) & 3; +            rm = (modrm & 7) | REX_B(s); +            op = (modrm >> 3) & 7; + +            if (mod != 3) { +                if (b == 0x83) +                    s->rip_offset = 1; +                else +                    s->rip_offset = insn_const_size(ot); +                gen_lea_modrm(env, s, modrm); +                opreg = OR_TMP0; +            } else { +                opreg = rm; +            } + +            switch(b) { +            default: +            case 0x80: +            case 0x81: +            case 0x82: +                val = insn_get(env, s, ot); +                break; +            case 0x83: +                val = (int8_t)insn_get(env, s, MO_8); +                break; +            } +            tcg_gen_movi_tl(cpu_T[1], val); +            gen_op(s, op, ot, opreg); +        } +        break; + +        /**************************/ +        /* inc, dec, and other misc arith */ +    case 0x40 ... 0x47: /* inc Gv */ +        ot = dflag; +        gen_inc(s, ot, OR_EAX + (b & 7), 1); +        break; +    case 0x48 ... 0x4f: /* dec Gv */ +        ot = dflag; +        gen_inc(s, ot, OR_EAX + (b & 7), -1); +        break; +    case 0xf6: /* GRP3 */ +    case 0xf7: +        ot = mo_b_d(b, dflag); + +        modrm = cpu_ldub_code(env, s->pc++); +        mod = (modrm >> 6) & 3; +        rm = (modrm & 7) | REX_B(s); +        op = (modrm >> 3) & 7; +        if (mod != 3) { +            if (op == 0) +                s->rip_offset = insn_const_size(ot); +            gen_lea_modrm(env, s, modrm); +            gen_op_ld_v(s, ot, cpu_T[0], cpu_A0); +        } else { +            gen_op_mov_v_reg(ot, cpu_T[0], rm); +        } + +        switch(op) { +        case 0: /* test */ +            val = insn_get(env, s, ot); +            tcg_gen_movi_tl(cpu_T[1], val); +            gen_op_testl_T0_T1_cc(); +            set_cc_op(s, CC_OP_LOGICB + ot); +            break; +        case 2: /* not */ +            tcg_gen_not_tl(cpu_T[0], cpu_T[0]); +            if (mod != 3) { +                gen_op_st_v(s, ot, cpu_T[0], cpu_A0); +            } else { +                gen_op_mov_reg_v(ot, rm, cpu_T[0]); +            } +            break; +        case 3: /* neg */ +            tcg_gen_neg_tl(cpu_T[0], cpu_T[0]); +            if (mod != 3) { +                gen_op_st_v(s, ot, cpu_T[0], cpu_A0); +            } else { +                gen_op_mov_reg_v(ot, rm, cpu_T[0]); +            } +            gen_op_update_neg_cc(); +            set_cc_op(s, CC_OP_SUBB + ot); +            break; +        case 4: /* mul */ +            switch(ot) { +            case MO_8: +                gen_op_mov_v_reg(MO_8, cpu_T[1], R_EAX); +                tcg_gen_ext8u_tl(cpu_T[0], cpu_T[0]); +                tcg_gen_ext8u_tl(cpu_T[1], cpu_T[1]); +                /* XXX: use 32 bit mul which could be faster */ +                tcg_gen_mul_tl(cpu_T[0], cpu_T[0], cpu_T[1]); +                gen_op_mov_reg_v(MO_16, R_EAX, cpu_T[0]); +                tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); +                tcg_gen_andi_tl(cpu_cc_src, cpu_T[0], 0xff00); +                set_cc_op(s, CC_OP_MULB); +                break; +            case MO_16: +                gen_op_mov_v_reg(MO_16, cpu_T[1], R_EAX); +                tcg_gen_ext16u_tl(cpu_T[0], cpu_T[0]); +                tcg_gen_ext16u_tl(cpu_T[1], cpu_T[1]); +                /* XXX: use 32 bit mul which could be faster */ +                tcg_gen_mul_tl(cpu_T[0], cpu_T[0], cpu_T[1]); +                gen_op_mov_reg_v(MO_16, R_EAX, cpu_T[0]); +                tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); +                tcg_gen_shri_tl(cpu_T[0], cpu_T[0], 16); +                gen_op_mov_reg_v(MO_16, R_EDX, cpu_T[0]); +                tcg_gen_mov_tl(cpu_cc_src, cpu_T[0]); +                set_cc_op(s, CC_OP_MULW); +                break; +            default: +            case MO_32: +                tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); +                tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_regs[R_EAX]); +                tcg_gen_mulu2_i32(cpu_tmp2_i32, cpu_tmp3_i32, +                                  cpu_tmp2_i32, cpu_tmp3_i32); +                tcg_gen_extu_i32_tl(cpu_regs[R_EAX], cpu_tmp2_i32); +                tcg_gen_extu_i32_tl(cpu_regs[R_EDX], cpu_tmp3_i32); +                tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]); +                tcg_gen_mov_tl(cpu_cc_src, cpu_regs[R_EDX]); +                set_cc_op(s, CC_OP_MULL); +                break; +#ifdef TARGET_X86_64 +            case MO_64: +                tcg_gen_mulu2_i64(cpu_regs[R_EAX], cpu_regs[R_EDX], +                                  cpu_T[0], cpu_regs[R_EAX]); +                tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]); +                tcg_gen_mov_tl(cpu_cc_src, cpu_regs[R_EDX]); +                set_cc_op(s, CC_OP_MULQ); +                break; +#endif +            } +            break; +        case 5: /* imul */ +            switch(ot) { +            case MO_8: +                gen_op_mov_v_reg(MO_8, cpu_T[1], R_EAX); +                tcg_gen_ext8s_tl(cpu_T[0], cpu_T[0]); +                tcg_gen_ext8s_tl(cpu_T[1], cpu_T[1]); +                /* XXX: use 32 bit mul which could be faster */ +                tcg_gen_mul_tl(cpu_T[0], cpu_T[0], cpu_T[1]); +                gen_op_mov_reg_v(MO_16, R_EAX, cpu_T[0]); +                tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); +                tcg_gen_ext8s_tl(cpu_tmp0, cpu_T[0]); +                tcg_gen_sub_tl(cpu_cc_src, cpu_T[0], cpu_tmp0); +                set_cc_op(s, CC_OP_MULB); +                break; +            case MO_16: +                gen_op_mov_v_reg(MO_16, cpu_T[1], R_EAX); +                tcg_gen_ext16s_tl(cpu_T[0], cpu_T[0]); +                tcg_gen_ext16s_tl(cpu_T[1], cpu_T[1]); +                /* XXX: use 32 bit mul which could be faster */ +                tcg_gen_mul_tl(cpu_T[0], cpu_T[0], cpu_T[1]); +                gen_op_mov_reg_v(MO_16, R_EAX, cpu_T[0]); +                tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); +                tcg_gen_ext16s_tl(cpu_tmp0, cpu_T[0]); +                tcg_gen_sub_tl(cpu_cc_src, cpu_T[0], cpu_tmp0); +                tcg_gen_shri_tl(cpu_T[0], cpu_T[0], 16); +                gen_op_mov_reg_v(MO_16, R_EDX, cpu_T[0]); +                set_cc_op(s, CC_OP_MULW); +                break; +            default: +            case MO_32: +                tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); +                tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_regs[R_EAX]); +                tcg_gen_muls2_i32(cpu_tmp2_i32, cpu_tmp3_i32, +                                  cpu_tmp2_i32, cpu_tmp3_i32); +                tcg_gen_extu_i32_tl(cpu_regs[R_EAX], cpu_tmp2_i32); +                tcg_gen_extu_i32_tl(cpu_regs[R_EDX], cpu_tmp3_i32); +                tcg_gen_sari_i32(cpu_tmp2_i32, cpu_tmp2_i32, 31); +                tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]); +                tcg_gen_sub_i32(cpu_tmp2_i32, cpu_tmp2_i32, cpu_tmp3_i32); +                tcg_gen_extu_i32_tl(cpu_cc_src, cpu_tmp2_i32); +                set_cc_op(s, CC_OP_MULL); +                break; +#ifdef TARGET_X86_64 +            case MO_64: +                tcg_gen_muls2_i64(cpu_regs[R_EAX], cpu_regs[R_EDX], +                                  cpu_T[0], cpu_regs[R_EAX]); +                tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]); +                tcg_gen_sari_tl(cpu_cc_src, cpu_regs[R_EAX], 63); +                tcg_gen_sub_tl(cpu_cc_src, cpu_cc_src, cpu_regs[R_EDX]); +                set_cc_op(s, CC_OP_MULQ); +                break; +#endif +            } +            break; +        case 6: /* div */ +            switch(ot) { +            case MO_8: +                gen_jmp_im(pc_start - s->cs_base); +                gen_helper_divb_AL(cpu_env, cpu_T[0]); +                break; +            case MO_16: +                gen_jmp_im(pc_start - s->cs_base); +                gen_helper_divw_AX(cpu_env, cpu_T[0]); +                break; +            default: +            case MO_32: +                gen_jmp_im(pc_start - s->cs_base); +                gen_helper_divl_EAX(cpu_env, cpu_T[0]); +                break; +#ifdef TARGET_X86_64 +            case MO_64: +                gen_jmp_im(pc_start - s->cs_base); +                gen_helper_divq_EAX(cpu_env, cpu_T[0]); +                break; +#endif +            } +            break; +        case 7: /* idiv */ +            switch(ot) { +            case MO_8: +                gen_jmp_im(pc_start - s->cs_base); +                gen_helper_idivb_AL(cpu_env, cpu_T[0]); +                break; +            case MO_16: +                gen_jmp_im(pc_start - s->cs_base); +                gen_helper_idivw_AX(cpu_env, cpu_T[0]); +                break; +            default: +            case MO_32: +                gen_jmp_im(pc_start - s->cs_base); +                gen_helper_idivl_EAX(cpu_env, cpu_T[0]); +                break; +#ifdef TARGET_X86_64 +            case MO_64: +                gen_jmp_im(pc_start - s->cs_base); +                gen_helper_idivq_EAX(cpu_env, cpu_T[0]); +                break; +#endif +            } +            break; +        default: +            goto illegal_op; +        } +        break; + +    case 0xfe: /* GRP4 */ +    case 0xff: /* GRP5 */ +        ot = mo_b_d(b, dflag); + +        modrm = cpu_ldub_code(env, s->pc++); +        mod = (modrm >> 6) & 3; +        rm = (modrm & 7) | REX_B(s); +        op = (modrm >> 3) & 7; +        if (op >= 2 && b == 0xfe) { +            goto illegal_op; +        } +        if (CODE64(s)) { +            if (op == 2 || op == 4) { +                /* operand size for jumps is 64 bit */ +                ot = MO_64; +            } else if (op == 3 || op == 5) { +                ot = dflag != MO_16 ? MO_32 + (rex_w == 1) : MO_16; +            } else if (op == 6) { +                /* default push size is 64 bit */ +                ot = mo_pushpop(s, dflag); +            } +        } +        if (mod != 3) { +            gen_lea_modrm(env, s, modrm); +            if (op >= 2 && op != 3 && op != 5) +                gen_op_ld_v(s, ot, cpu_T[0], cpu_A0); +        } else { +            gen_op_mov_v_reg(ot, cpu_T[0], rm); +        } + +        switch(op) { +        case 0: /* inc Ev */ +            if (mod != 3) +                opreg = OR_TMP0; +            else +                opreg = rm; +            gen_inc(s, ot, opreg, 1); +            break; +        case 1: /* dec Ev */ +            if (mod != 3) +                opreg = OR_TMP0; +            else +                opreg = rm; +            gen_inc(s, ot, opreg, -1); +            break; +        case 2: /* call Ev */ +            /* XXX: optimize if memory (no 'and' is necessary) */ +            if (dflag == MO_16) { +                tcg_gen_ext16u_tl(cpu_T[0], cpu_T[0]); +            } +            next_eip = s->pc - s->cs_base; +            tcg_gen_movi_tl(cpu_T[1], next_eip); +            gen_push_v(s, cpu_T[1]); +            gen_op_jmp_v(cpu_T[0]); +            gen_eob(s); +            break; +        case 3: /* lcall Ev */ +            gen_op_ld_v(s, ot, cpu_T[1], cpu_A0); +            gen_add_A0_im(s, 1 << ot); +            gen_op_ld_v(s, MO_16, cpu_T[0], cpu_A0); +        do_lcall: +            if (s->pe && !s->vm86) { +                gen_update_cc_op(s); +                gen_jmp_im(pc_start - s->cs_base); +                tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); +                gen_helper_lcall_protected(cpu_env, cpu_tmp2_i32, cpu_T[1], +                                           tcg_const_i32(dflag - 1), +                                           tcg_const_i32(s->pc - pc_start)); +            } else { +                tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); +                gen_helper_lcall_real(cpu_env, cpu_tmp2_i32, cpu_T[1], +                                      tcg_const_i32(dflag - 1), +                                      tcg_const_i32(s->pc - s->cs_base)); +            } +            gen_eob(s); +            break; +        case 4: /* jmp Ev */ +            if (dflag == MO_16) { +                tcg_gen_ext16u_tl(cpu_T[0], cpu_T[0]); +            } +            gen_op_jmp_v(cpu_T[0]); +            gen_eob(s); +            break; +        case 5: /* ljmp Ev */ +            gen_op_ld_v(s, ot, cpu_T[1], cpu_A0); +            gen_add_A0_im(s, 1 << ot); +            gen_op_ld_v(s, MO_16, cpu_T[0], cpu_A0); +        do_ljmp: +            if (s->pe && !s->vm86) { +                gen_update_cc_op(s); +                gen_jmp_im(pc_start - s->cs_base); +                tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); +                gen_helper_ljmp_protected(cpu_env, cpu_tmp2_i32, cpu_T[1], +                                          tcg_const_i32(s->pc - pc_start)); +            } else { +                gen_op_movl_seg_T0_vm(R_CS); +                gen_op_jmp_v(cpu_T[1]); +            } +            gen_eob(s); +            break; +        case 6: /* push Ev */ +            gen_push_v(s, cpu_T[0]); +            break; +        default: +            goto illegal_op; +        } +        break; + +    case 0x84: /* test Ev, Gv */ +    case 0x85: +        ot = mo_b_d(b, dflag); + +        modrm = cpu_ldub_code(env, s->pc++); +        reg = ((modrm >> 3) & 7) | rex_r; + +        gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); +        gen_op_mov_v_reg(ot, cpu_T[1], reg); +        gen_op_testl_T0_T1_cc(); +        set_cc_op(s, CC_OP_LOGICB + ot); +        break; + +    case 0xa8: /* test eAX, Iv */ +    case 0xa9: +        ot = mo_b_d(b, dflag); +        val = insn_get(env, s, ot); + +        gen_op_mov_v_reg(ot, cpu_T[0], OR_EAX); +        tcg_gen_movi_tl(cpu_T[1], val); +        gen_op_testl_T0_T1_cc(); +        set_cc_op(s, CC_OP_LOGICB + ot); +        break; + +    case 0x98: /* CWDE/CBW */ +        switch (dflag) { +#ifdef TARGET_X86_64 +        case MO_64: +            gen_op_mov_v_reg(MO_32, cpu_T[0], R_EAX); +            tcg_gen_ext32s_tl(cpu_T[0], cpu_T[0]); +            gen_op_mov_reg_v(MO_64, R_EAX, cpu_T[0]); +            break; +#endif +        case MO_32: +            gen_op_mov_v_reg(MO_16, cpu_T[0], R_EAX); +            tcg_gen_ext16s_tl(cpu_T[0], cpu_T[0]); +            gen_op_mov_reg_v(MO_32, R_EAX, cpu_T[0]); +            break; +        case MO_16: +            gen_op_mov_v_reg(MO_8, cpu_T[0], R_EAX); +            tcg_gen_ext8s_tl(cpu_T[0], cpu_T[0]); +            gen_op_mov_reg_v(MO_16, R_EAX, cpu_T[0]); +            break; +        default: +            tcg_abort(); +        } +        break; +    case 0x99: /* CDQ/CWD */ +        switch (dflag) { +#ifdef TARGET_X86_64 +        case MO_64: +            gen_op_mov_v_reg(MO_64, cpu_T[0], R_EAX); +            tcg_gen_sari_tl(cpu_T[0], cpu_T[0], 63); +            gen_op_mov_reg_v(MO_64, R_EDX, cpu_T[0]); +            break; +#endif +        case MO_32: +            gen_op_mov_v_reg(MO_32, cpu_T[0], R_EAX); +            tcg_gen_ext32s_tl(cpu_T[0], cpu_T[0]); +            tcg_gen_sari_tl(cpu_T[0], cpu_T[0], 31); +            gen_op_mov_reg_v(MO_32, R_EDX, cpu_T[0]); +            break; +        case MO_16: +            gen_op_mov_v_reg(MO_16, cpu_T[0], R_EAX); +            tcg_gen_ext16s_tl(cpu_T[0], cpu_T[0]); +            tcg_gen_sari_tl(cpu_T[0], cpu_T[0], 15); +            gen_op_mov_reg_v(MO_16, R_EDX, cpu_T[0]); +            break; +        default: +            tcg_abort(); +        } +        break; +    case 0x1af: /* imul Gv, Ev */ +    case 0x69: /* imul Gv, Ev, I */ +    case 0x6b: +        ot = dflag; +        modrm = cpu_ldub_code(env, s->pc++); +        reg = ((modrm >> 3) & 7) | rex_r; +        if (b == 0x69) +            s->rip_offset = insn_const_size(ot); +        else if (b == 0x6b) +            s->rip_offset = 1; +        gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); +        if (b == 0x69) { +            val = insn_get(env, s, ot); +            tcg_gen_movi_tl(cpu_T[1], val); +        } else if (b == 0x6b) { +            val = (int8_t)insn_get(env, s, MO_8); +            tcg_gen_movi_tl(cpu_T[1], val); +        } else { +            gen_op_mov_v_reg(ot, cpu_T[1], reg); +        } +        switch (ot) { +#ifdef TARGET_X86_64 +        case MO_64: +            tcg_gen_muls2_i64(cpu_regs[reg], cpu_T[1], cpu_T[0], cpu_T[1]); +            tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[reg]); +            tcg_gen_sari_tl(cpu_cc_src, cpu_cc_dst, 63); +            tcg_gen_sub_tl(cpu_cc_src, cpu_cc_src, cpu_T[1]); +            break; +#endif +        case MO_32: +            tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); +            tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_T[1]); +            tcg_gen_muls2_i32(cpu_tmp2_i32, cpu_tmp3_i32, +                              cpu_tmp2_i32, cpu_tmp3_i32); +            tcg_gen_extu_i32_tl(cpu_regs[reg], cpu_tmp2_i32); +            tcg_gen_sari_i32(cpu_tmp2_i32, cpu_tmp2_i32, 31); +            tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[reg]); +            tcg_gen_sub_i32(cpu_tmp2_i32, cpu_tmp2_i32, cpu_tmp3_i32); +            tcg_gen_extu_i32_tl(cpu_cc_src, cpu_tmp2_i32); +            break; +        default: +            tcg_gen_ext16s_tl(cpu_T[0], cpu_T[0]); +            tcg_gen_ext16s_tl(cpu_T[1], cpu_T[1]); +            /* XXX: use 32 bit mul which could be faster */ +            tcg_gen_mul_tl(cpu_T[0], cpu_T[0], cpu_T[1]); +            tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); +            tcg_gen_ext16s_tl(cpu_tmp0, cpu_T[0]); +            tcg_gen_sub_tl(cpu_cc_src, cpu_T[0], cpu_tmp0); +            gen_op_mov_reg_v(ot, reg, cpu_T[0]); +            break; +        } +        set_cc_op(s, CC_OP_MULB + ot); +        break; +    case 0x1c0: +    case 0x1c1: /* xadd Ev, Gv */ +        ot = mo_b_d(b, dflag); +        modrm = cpu_ldub_code(env, s->pc++); +        reg = ((modrm >> 3) & 7) | rex_r; +        mod = (modrm >> 6) & 3; +        if (mod == 3) { +            rm = (modrm & 7) | REX_B(s); +            gen_op_mov_v_reg(ot, cpu_T[0], reg); +            gen_op_mov_v_reg(ot, cpu_T[1], rm); +            tcg_gen_add_tl(cpu_T[0], cpu_T[0], cpu_T[1]); +            gen_op_mov_reg_v(ot, reg, cpu_T[1]); +            gen_op_mov_reg_v(ot, rm, cpu_T[0]); +        } else { +            gen_lea_modrm(env, s, modrm); +            gen_op_mov_v_reg(ot, cpu_T[0], reg); +            gen_op_ld_v(s, ot, cpu_T[1], cpu_A0); +            tcg_gen_add_tl(cpu_T[0], cpu_T[0], cpu_T[1]); +            gen_op_st_v(s, ot, cpu_T[0], cpu_A0); +            gen_op_mov_reg_v(ot, reg, cpu_T[1]); +        } +        gen_op_update2_cc(); +        set_cc_op(s, CC_OP_ADDB + ot); +        break; +    case 0x1b0: +    case 0x1b1: /* cmpxchg Ev, Gv */ +        { +            TCGLabel *label1, *label2; +            TCGv t0, t1, t2, a0; + +            ot = mo_b_d(b, dflag); +            modrm = cpu_ldub_code(env, s->pc++); +            reg = ((modrm >> 3) & 7) | rex_r; +            mod = (modrm >> 6) & 3; +            t0 = tcg_temp_local_new(); +            t1 = tcg_temp_local_new(); +            t2 = tcg_temp_local_new(); +            a0 = tcg_temp_local_new(); +            gen_op_mov_v_reg(ot, t1, reg); +            if (mod == 3) { +                rm = (modrm & 7) | REX_B(s); +                gen_op_mov_v_reg(ot, t0, rm); +            } else { +                gen_lea_modrm(env, s, modrm); +                tcg_gen_mov_tl(a0, cpu_A0); +                gen_op_ld_v(s, ot, t0, a0); +                rm = 0; /* avoid warning */ +            } +            label1 = gen_new_label(); +            tcg_gen_mov_tl(t2, cpu_regs[R_EAX]); +            gen_extu(ot, t0); +            gen_extu(ot, t2); +            tcg_gen_brcond_tl(TCG_COND_EQ, t2, t0, label1); +            label2 = gen_new_label(); +            if (mod == 3) { +                gen_op_mov_reg_v(ot, R_EAX, t0); +                tcg_gen_br(label2); +                gen_set_label(label1); +                gen_op_mov_reg_v(ot, rm, t1); +            } else { +                /* perform no-op store cycle like physical cpu; must be +                   before changing accumulator to ensure idempotency if +                   the store faults and the instruction is restarted */ +                gen_op_st_v(s, ot, t0, a0); +                gen_op_mov_reg_v(ot, R_EAX, t0); +                tcg_gen_br(label2); +                gen_set_label(label1); +                gen_op_st_v(s, ot, t1, a0); +            } +            gen_set_label(label2); +            tcg_gen_mov_tl(cpu_cc_src, t0); +            tcg_gen_mov_tl(cpu_cc_srcT, t2); +            tcg_gen_sub_tl(cpu_cc_dst, t2, t0); +            set_cc_op(s, CC_OP_SUBB + ot); +            tcg_temp_free(t0); +            tcg_temp_free(t1); +            tcg_temp_free(t2); +            tcg_temp_free(a0); +        } +        break; +    case 0x1c7: /* cmpxchg8b */ +        modrm = cpu_ldub_code(env, s->pc++); +        mod = (modrm >> 6) & 3; +        if ((mod == 3) || ((modrm & 0x38) != 0x8)) +            goto illegal_op; +#ifdef TARGET_X86_64 +        if (dflag == MO_64) { +            if (!(s->cpuid_ext_features & CPUID_EXT_CX16)) +                goto illegal_op; +            gen_jmp_im(pc_start - s->cs_base); +            gen_update_cc_op(s); +            gen_lea_modrm(env, s, modrm); +            gen_helper_cmpxchg16b(cpu_env, cpu_A0); +        } else +#endif         +        { +            if (!(s->cpuid_features & CPUID_CX8)) +                goto illegal_op; +            gen_jmp_im(pc_start - s->cs_base); +            gen_update_cc_op(s); +            gen_lea_modrm(env, s, modrm); +            gen_helper_cmpxchg8b(cpu_env, cpu_A0); +        } +        set_cc_op(s, CC_OP_EFLAGS); +        break; + +        /**************************/ +        /* push/pop */ +    case 0x50 ... 0x57: /* push */ +        gen_op_mov_v_reg(MO_32, cpu_T[0], (b & 7) | REX_B(s)); +        gen_push_v(s, cpu_T[0]); +        break; +    case 0x58 ... 0x5f: /* pop */ +        ot = gen_pop_T0(s); +        /* NOTE: order is important for pop %sp */ +        gen_pop_update(s, ot); +        gen_op_mov_reg_v(ot, (b & 7) | REX_B(s), cpu_T[0]); +        break; +    case 0x60: /* pusha */ +        if (CODE64(s)) +            goto illegal_op; +        gen_pusha(s); +        break; +    case 0x61: /* popa */ +        if (CODE64(s)) +            goto illegal_op; +        gen_popa(s); +        break; +    case 0x68: /* push Iv */ +    case 0x6a: +        ot = mo_pushpop(s, dflag); +        if (b == 0x68) +            val = insn_get(env, s, ot); +        else +            val = (int8_t)insn_get(env, s, MO_8); +        tcg_gen_movi_tl(cpu_T[0], val); +        gen_push_v(s, cpu_T[0]); +        break; +    case 0x8f: /* pop Ev */ +        modrm = cpu_ldub_code(env, s->pc++); +        mod = (modrm >> 6) & 3; +        ot = gen_pop_T0(s); +        if (mod == 3) { +            /* NOTE: order is important for pop %sp */ +            gen_pop_update(s, ot); +            rm = (modrm & 7) | REX_B(s); +            gen_op_mov_reg_v(ot, rm, cpu_T[0]); +        } else { +            /* NOTE: order is important too for MMU exceptions */ +            s->popl_esp_hack = 1 << ot; +            gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); +            s->popl_esp_hack = 0; +            gen_pop_update(s, ot); +        } +        break; +    case 0xc8: /* enter */ +        { +            int level; +            val = cpu_lduw_code(env, s->pc); +            s->pc += 2; +            level = cpu_ldub_code(env, s->pc++); +            gen_enter(s, val, level); +        } +        break; +    case 0xc9: /* leave */ +        /* XXX: exception not precise (ESP is updated before potential exception) */ +        if (CODE64(s)) { +            gen_op_mov_v_reg(MO_64, cpu_T[0], R_EBP); +            gen_op_mov_reg_v(MO_64, R_ESP, cpu_T[0]); +        } else if (s->ss32) { +            gen_op_mov_v_reg(MO_32, cpu_T[0], R_EBP); +            gen_op_mov_reg_v(MO_32, R_ESP, cpu_T[0]); +        } else { +            gen_op_mov_v_reg(MO_16, cpu_T[0], R_EBP); +            gen_op_mov_reg_v(MO_16, R_ESP, cpu_T[0]); +        } +        ot = gen_pop_T0(s); +        gen_op_mov_reg_v(ot, R_EBP, cpu_T[0]); +        gen_pop_update(s, ot); +        break; +    case 0x06: /* push es */ +    case 0x0e: /* push cs */ +    case 0x16: /* push ss */ +    case 0x1e: /* push ds */ +        if (CODE64(s)) +            goto illegal_op; +        gen_op_movl_T0_seg(b >> 3); +        gen_push_v(s, cpu_T[0]); +        break; +    case 0x1a0: /* push fs */ +    case 0x1a8: /* push gs */ +        gen_op_movl_T0_seg((b >> 3) & 7); +        gen_push_v(s, cpu_T[0]); +        break; +    case 0x07: /* pop es */ +    case 0x17: /* pop ss */ +    case 0x1f: /* pop ds */ +        if (CODE64(s)) +            goto illegal_op; +        reg = b >> 3; +        ot = gen_pop_T0(s); +        gen_movl_seg_T0(s, reg, pc_start - s->cs_base); +        gen_pop_update(s, ot); +        if (reg == R_SS) { +            /* if reg == SS, inhibit interrupts/trace. */ +            /* If several instructions disable interrupts, only the +               _first_ does it */ +            if (!(s->tb->flags & HF_INHIBIT_IRQ_MASK)) +                gen_helper_set_inhibit_irq(cpu_env); +            s->tf = 0; +        } +        if (s->is_jmp) { +            gen_jmp_im(s->pc - s->cs_base); +            gen_eob(s); +        } +        break; +    case 0x1a1: /* pop fs */ +    case 0x1a9: /* pop gs */ +        ot = gen_pop_T0(s); +        gen_movl_seg_T0(s, (b >> 3) & 7, pc_start - s->cs_base); +        gen_pop_update(s, ot); +        if (s->is_jmp) { +            gen_jmp_im(s->pc - s->cs_base); +            gen_eob(s); +        } +        break; + +        /**************************/ +        /* mov */ +    case 0x88: +    case 0x89: /* mov Gv, Ev */ +        ot = mo_b_d(b, dflag); +        modrm = cpu_ldub_code(env, s->pc++); +        reg = ((modrm >> 3) & 7) | rex_r; + +        /* generate a generic store */ +        gen_ldst_modrm(env, s, modrm, ot, reg, 1); +        break; +    case 0xc6: +    case 0xc7: /* mov Ev, Iv */ +        ot = mo_b_d(b, dflag); +        modrm = cpu_ldub_code(env, s->pc++); +        mod = (modrm >> 6) & 3; +        if (mod != 3) { +            s->rip_offset = insn_const_size(ot); +            gen_lea_modrm(env, s, modrm); +        } +        val = insn_get(env, s, ot); +        tcg_gen_movi_tl(cpu_T[0], val); +        if (mod != 3) { +            gen_op_st_v(s, ot, cpu_T[0], cpu_A0); +        } else { +            gen_op_mov_reg_v(ot, (modrm & 7) | REX_B(s), cpu_T[0]); +        } +        break; +    case 0x8a: +    case 0x8b: /* mov Ev, Gv */ +        ot = mo_b_d(b, dflag); +        modrm = cpu_ldub_code(env, s->pc++); +        reg = ((modrm >> 3) & 7) | rex_r; + +        gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); +        gen_op_mov_reg_v(ot, reg, cpu_T[0]); +        break; +    case 0x8e: /* mov seg, Gv */ +        modrm = cpu_ldub_code(env, s->pc++); +        reg = (modrm >> 3) & 7; +        if (reg >= 6 || reg == R_CS) +            goto illegal_op; +        gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); +        gen_movl_seg_T0(s, reg, pc_start - s->cs_base); +        if (reg == R_SS) { +            /* if reg == SS, inhibit interrupts/trace */ +            /* If several instructions disable interrupts, only the +               _first_ does it */ +            if (!(s->tb->flags & HF_INHIBIT_IRQ_MASK)) +                gen_helper_set_inhibit_irq(cpu_env); +            s->tf = 0; +        } +        if (s->is_jmp) { +            gen_jmp_im(s->pc - s->cs_base); +            gen_eob(s); +        } +        break; +    case 0x8c: /* mov Gv, seg */ +        modrm = cpu_ldub_code(env, s->pc++); +        reg = (modrm >> 3) & 7; +        mod = (modrm >> 6) & 3; +        if (reg >= 6) +            goto illegal_op; +        gen_op_movl_T0_seg(reg); +        ot = mod == 3 ? dflag : MO_16; +        gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); +        break; + +    case 0x1b6: /* movzbS Gv, Eb */ +    case 0x1b7: /* movzwS Gv, Eb */ +    case 0x1be: /* movsbS Gv, Eb */ +    case 0x1bf: /* movswS Gv, Eb */ +        { +            TCGMemOp d_ot; +            TCGMemOp s_ot; + +            /* d_ot is the size of destination */ +            d_ot = dflag; +            /* ot is the size of source */ +            ot = (b & 1) + MO_8; +            /* s_ot is the sign+size of source */ +            s_ot = b & 8 ? MO_SIGN | ot : ot; + +            modrm = cpu_ldub_code(env, s->pc++); +            reg = ((modrm >> 3) & 7) | rex_r; +            mod = (modrm >> 6) & 3; +            rm = (modrm & 7) | REX_B(s); + +            if (mod == 3) { +                gen_op_mov_v_reg(ot, cpu_T[0], rm); +                switch (s_ot) { +                case MO_UB: +                    tcg_gen_ext8u_tl(cpu_T[0], cpu_T[0]); +                    break; +                case MO_SB: +                    tcg_gen_ext8s_tl(cpu_T[0], cpu_T[0]); +                    break; +                case MO_UW: +                    tcg_gen_ext16u_tl(cpu_T[0], cpu_T[0]); +                    break; +                default: +                case MO_SW: +                    tcg_gen_ext16s_tl(cpu_T[0], cpu_T[0]); +                    break; +                } +                gen_op_mov_reg_v(d_ot, reg, cpu_T[0]); +            } else { +                gen_lea_modrm(env, s, modrm); +                gen_op_ld_v(s, s_ot, cpu_T[0], cpu_A0); +                gen_op_mov_reg_v(d_ot, reg, cpu_T[0]); +            } +        } +        break; + +    case 0x8d: /* lea */ +        ot = dflag; +        modrm = cpu_ldub_code(env, s->pc++); +        mod = (modrm >> 6) & 3; +        if (mod == 3) +            goto illegal_op; +        reg = ((modrm >> 3) & 7) | rex_r; +        /* we must ensure that no segment is added */ +        s->override = -1; +        val = s->addseg; +        s->addseg = 0; +        gen_lea_modrm(env, s, modrm); +        s->addseg = val; +        gen_op_mov_reg_v(ot, reg, cpu_A0); +        break; + +    case 0xa0: /* mov EAX, Ov */ +    case 0xa1: +    case 0xa2: /* mov Ov, EAX */ +    case 0xa3: +        { +            target_ulong offset_addr; + +            ot = mo_b_d(b, dflag); +            switch (s->aflag) { +#ifdef TARGET_X86_64 +            case MO_64: +                offset_addr = cpu_ldq_code(env, s->pc); +                s->pc += 8; +                break; +#endif +            default: +                offset_addr = insn_get(env, s, s->aflag); +                break; +            } +            tcg_gen_movi_tl(cpu_A0, offset_addr); +            gen_add_A0_ds_seg(s); +            if ((b & 2) == 0) { +                gen_op_ld_v(s, ot, cpu_T[0], cpu_A0); +                gen_op_mov_reg_v(ot, R_EAX, cpu_T[0]); +            } else { +                gen_op_mov_v_reg(ot, cpu_T[0], R_EAX); +                gen_op_st_v(s, ot, cpu_T[0], cpu_A0); +            } +        } +        break; +    case 0xd7: /* xlat */ +        tcg_gen_mov_tl(cpu_A0, cpu_regs[R_EBX]); +        tcg_gen_ext8u_tl(cpu_T[0], cpu_regs[R_EAX]); +        tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_T[0]); +        gen_extu(s->aflag, cpu_A0); +        gen_add_A0_ds_seg(s); +        gen_op_ld_v(s, MO_8, cpu_T[0], cpu_A0); +        gen_op_mov_reg_v(MO_8, R_EAX, cpu_T[0]); +        break; +    case 0xb0 ... 0xb7: /* mov R, Ib */ +        val = insn_get(env, s, MO_8); +        tcg_gen_movi_tl(cpu_T[0], val); +        gen_op_mov_reg_v(MO_8, (b & 7) | REX_B(s), cpu_T[0]); +        break; +    case 0xb8 ... 0xbf: /* mov R, Iv */ +#ifdef TARGET_X86_64 +        if (dflag == MO_64) { +            uint64_t tmp; +            /* 64 bit case */ +            tmp = cpu_ldq_code(env, s->pc); +            s->pc += 8; +            reg = (b & 7) | REX_B(s); +            tcg_gen_movi_tl(cpu_T[0], tmp); +            gen_op_mov_reg_v(MO_64, reg, cpu_T[0]); +        } else +#endif +        { +            ot = dflag; +            val = insn_get(env, s, ot); +            reg = (b & 7) | REX_B(s); +            tcg_gen_movi_tl(cpu_T[0], val); +            gen_op_mov_reg_v(ot, reg, cpu_T[0]); +        } +        break; + +    case 0x91 ... 0x97: /* xchg R, EAX */ +    do_xchg_reg_eax: +        ot = dflag; +        reg = (b & 7) | REX_B(s); +        rm = R_EAX; +        goto do_xchg_reg; +    case 0x86: +    case 0x87: /* xchg Ev, Gv */ +        ot = mo_b_d(b, dflag); +        modrm = cpu_ldub_code(env, s->pc++); +        reg = ((modrm >> 3) & 7) | rex_r; +        mod = (modrm >> 6) & 3; +        if (mod == 3) { +            rm = (modrm & 7) | REX_B(s); +        do_xchg_reg: +            gen_op_mov_v_reg(ot, cpu_T[0], reg); +            gen_op_mov_v_reg(ot, cpu_T[1], rm); +            gen_op_mov_reg_v(ot, rm, cpu_T[0]); +            gen_op_mov_reg_v(ot, reg, cpu_T[1]); +        } else { +            gen_lea_modrm(env, s, modrm); +            gen_op_mov_v_reg(ot, cpu_T[0], reg); +            /* for xchg, lock is implicit */ +            if (!(prefixes & PREFIX_LOCK)) +                gen_helper_lock(); +            gen_op_ld_v(s, ot, cpu_T[1], cpu_A0); +            gen_op_st_v(s, ot, cpu_T[0], cpu_A0); +            if (!(prefixes & PREFIX_LOCK)) +                gen_helper_unlock(); +            gen_op_mov_reg_v(ot, reg, cpu_T[1]); +        } +        break; +    case 0xc4: /* les Gv */ +        /* In CODE64 this is VEX3; see above.  */ +        op = R_ES; +        goto do_lxx; +    case 0xc5: /* lds Gv */ +        /* In CODE64 this is VEX2; see above.  */ +        op = R_DS; +        goto do_lxx; +    case 0x1b2: /* lss Gv */ +        op = R_SS; +        goto do_lxx; +    case 0x1b4: /* lfs Gv */ +        op = R_FS; +        goto do_lxx; +    case 0x1b5: /* lgs Gv */ +        op = R_GS; +    do_lxx: +        ot = dflag != MO_16 ? MO_32 : MO_16; +        modrm = cpu_ldub_code(env, s->pc++); +        reg = ((modrm >> 3) & 7) | rex_r; +        mod = (modrm >> 6) & 3; +        if (mod == 3) +            goto illegal_op; +        gen_lea_modrm(env, s, modrm); +        gen_op_ld_v(s, ot, cpu_T[1], cpu_A0); +        gen_add_A0_im(s, 1 << ot); +        /* load the segment first to handle exceptions properly */ +        gen_op_ld_v(s, MO_16, cpu_T[0], cpu_A0); +        gen_movl_seg_T0(s, op, pc_start - s->cs_base); +        /* then put the data */ +        gen_op_mov_reg_v(ot, reg, cpu_T[1]); +        if (s->is_jmp) { +            gen_jmp_im(s->pc - s->cs_base); +            gen_eob(s); +        } +        break; + +        /************************/ +        /* shifts */ +    case 0xc0: +    case 0xc1: +        /* shift Ev,Ib */ +        shift = 2; +    grp2: +        { +            ot = mo_b_d(b, dflag); +            modrm = cpu_ldub_code(env, s->pc++); +            mod = (modrm >> 6) & 3; +            op = (modrm >> 3) & 7; + +            if (mod != 3) { +                if (shift == 2) { +                    s->rip_offset = 1; +                } +                gen_lea_modrm(env, s, modrm); +                opreg = OR_TMP0; +            } else { +                opreg = (modrm & 7) | REX_B(s); +            } + +            /* simpler op */ +            if (shift == 0) { +                gen_shift(s, op, ot, opreg, OR_ECX); +            } else { +                if (shift == 2) { +                    shift = cpu_ldub_code(env, s->pc++); +                } +                gen_shifti(s, op, ot, opreg, shift); +            } +        } +        break; +    case 0xd0: +    case 0xd1: +        /* shift Ev,1 */ +        shift = 1; +        goto grp2; +    case 0xd2: +    case 0xd3: +        /* shift Ev,cl */ +        shift = 0; +        goto grp2; + +    case 0x1a4: /* shld imm */ +        op = 0; +        shift = 1; +        goto do_shiftd; +    case 0x1a5: /* shld cl */ +        op = 0; +        shift = 0; +        goto do_shiftd; +    case 0x1ac: /* shrd imm */ +        op = 1; +        shift = 1; +        goto do_shiftd; +    case 0x1ad: /* shrd cl */ +        op = 1; +        shift = 0; +    do_shiftd: +        ot = dflag; +        modrm = cpu_ldub_code(env, s->pc++); +        mod = (modrm >> 6) & 3; +        rm = (modrm & 7) | REX_B(s); +        reg = ((modrm >> 3) & 7) | rex_r; +        if (mod != 3) { +            gen_lea_modrm(env, s, modrm); +            opreg = OR_TMP0; +        } else { +            opreg = rm; +        } +        gen_op_mov_v_reg(ot, cpu_T[1], reg); + +        if (shift) { +            TCGv imm = tcg_const_tl(cpu_ldub_code(env, s->pc++)); +            gen_shiftd_rm_T1(s, ot, opreg, op, imm); +            tcg_temp_free(imm); +        } else { +            gen_shiftd_rm_T1(s, ot, opreg, op, cpu_regs[R_ECX]); +        } +        break; + +        /************************/ +        /* floats */ +    case 0xd8 ... 0xdf: +        if (s->flags & (HF_EM_MASK | HF_TS_MASK)) { +            /* if CR0.EM or CR0.TS are set, generate an FPU exception */ +            /* XXX: what to do if illegal op ? */ +            gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); +            break; +        } +        modrm = cpu_ldub_code(env, s->pc++); +        mod = (modrm >> 6) & 3; +        rm = modrm & 7; +        op = ((b & 7) << 3) | ((modrm >> 3) & 7); +        if (mod != 3) { +            /* memory op */ +            gen_lea_modrm(env, s, modrm); +            switch(op) { +            case 0x00 ... 0x07: /* fxxxs */ +            case 0x10 ... 0x17: /* fixxxl */ +            case 0x20 ... 0x27: /* fxxxl */ +            case 0x30 ... 0x37: /* fixxx */ +                { +                    int op1; +                    op1 = op & 7; + +                    switch(op >> 4) { +                    case 0: +                        tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0, +                                            s->mem_index, MO_LEUL); +                        gen_helper_flds_FT0(cpu_env, cpu_tmp2_i32); +                        break; +                    case 1: +                        tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0, +                                            s->mem_index, MO_LEUL); +                        gen_helper_fildl_FT0(cpu_env, cpu_tmp2_i32); +                        break; +                    case 2: +                        tcg_gen_qemu_ld_i64(cpu_tmp1_i64, cpu_A0, +                                            s->mem_index, MO_LEQ); +                        gen_helper_fldl_FT0(cpu_env, cpu_tmp1_i64); +                        break; +                    case 3: +                    default: +                        tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0, +                                            s->mem_index, MO_LESW); +                        gen_helper_fildl_FT0(cpu_env, cpu_tmp2_i32); +                        break; +                    } + +                    gen_helper_fp_arith_ST0_FT0(op1); +                    if (op1 == 3) { +                        /* fcomp needs pop */ +                        gen_helper_fpop(cpu_env); +                    } +                } +                break; +            case 0x08: /* flds */ +            case 0x0a: /* fsts */ +            case 0x0b: /* fstps */ +            case 0x18 ... 0x1b: /* fildl, fisttpl, fistl, fistpl */ +            case 0x28 ... 0x2b: /* fldl, fisttpll, fstl, fstpl */ +            case 0x38 ... 0x3b: /* filds, fisttps, fists, fistps */ +                switch(op & 7) { +                case 0: +                    switch(op >> 4) { +                    case 0: +                        tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0, +                                            s->mem_index, MO_LEUL); +                        gen_helper_flds_ST0(cpu_env, cpu_tmp2_i32); +                        break; +                    case 1: +                        tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0, +                                            s->mem_index, MO_LEUL); +                        gen_helper_fildl_ST0(cpu_env, cpu_tmp2_i32); +                        break; +                    case 2: +                        tcg_gen_qemu_ld_i64(cpu_tmp1_i64, cpu_A0, +                                            s->mem_index, MO_LEQ); +                        gen_helper_fldl_ST0(cpu_env, cpu_tmp1_i64); +                        break; +                    case 3: +                    default: +                        tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0, +                                            s->mem_index, MO_LESW); +                        gen_helper_fildl_ST0(cpu_env, cpu_tmp2_i32); +                        break; +                    } +                    break; +                case 1: +                    /* XXX: the corresponding CPUID bit must be tested ! */ +                    switch(op >> 4) { +                    case 1: +                        gen_helper_fisttl_ST0(cpu_tmp2_i32, cpu_env); +                        tcg_gen_qemu_st_i32(cpu_tmp2_i32, cpu_A0, +                                            s->mem_index, MO_LEUL); +                        break; +                    case 2: +                        gen_helper_fisttll_ST0(cpu_tmp1_i64, cpu_env); +                        tcg_gen_qemu_st_i64(cpu_tmp1_i64, cpu_A0, +                                            s->mem_index, MO_LEQ); +                        break; +                    case 3: +                    default: +                        gen_helper_fistt_ST0(cpu_tmp2_i32, cpu_env); +                        tcg_gen_qemu_st_i32(cpu_tmp2_i32, cpu_A0, +                                            s->mem_index, MO_LEUW); +                        break; +                    } +                    gen_helper_fpop(cpu_env); +                    break; +                default: +                    switch(op >> 4) { +                    case 0: +                        gen_helper_fsts_ST0(cpu_tmp2_i32, cpu_env); +                        tcg_gen_qemu_st_i32(cpu_tmp2_i32, cpu_A0, +                                            s->mem_index, MO_LEUL); +                        break; +                    case 1: +                        gen_helper_fistl_ST0(cpu_tmp2_i32, cpu_env); +                        tcg_gen_qemu_st_i32(cpu_tmp2_i32, cpu_A0, +                                            s->mem_index, MO_LEUL); +                        break; +                    case 2: +                        gen_helper_fstl_ST0(cpu_tmp1_i64, cpu_env); +                        tcg_gen_qemu_st_i64(cpu_tmp1_i64, cpu_A0, +                                            s->mem_index, MO_LEQ); +                        break; +                    case 3: +                    default: +                        gen_helper_fist_ST0(cpu_tmp2_i32, cpu_env); +                        tcg_gen_qemu_st_i32(cpu_tmp2_i32, cpu_A0, +                                            s->mem_index, MO_LEUW); +                        break; +                    } +                    if ((op & 7) == 3) +                        gen_helper_fpop(cpu_env); +                    break; +                } +                break; +            case 0x0c: /* fldenv mem */ +                gen_update_cc_op(s); +                gen_jmp_im(pc_start - s->cs_base); +                gen_helper_fldenv(cpu_env, cpu_A0, tcg_const_i32(dflag - 1)); +                break; +            case 0x0d: /* fldcw mem */ +                tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0, +                                    s->mem_index, MO_LEUW); +                gen_helper_fldcw(cpu_env, cpu_tmp2_i32); +                break; +            case 0x0e: /* fnstenv mem */ +                gen_update_cc_op(s); +                gen_jmp_im(pc_start - s->cs_base); +                gen_helper_fstenv(cpu_env, cpu_A0, tcg_const_i32(dflag - 1)); +                break; +            case 0x0f: /* fnstcw mem */ +                gen_helper_fnstcw(cpu_tmp2_i32, cpu_env); +                tcg_gen_qemu_st_i32(cpu_tmp2_i32, cpu_A0, +                                    s->mem_index, MO_LEUW); +                break; +            case 0x1d: /* fldt mem */ +                gen_update_cc_op(s); +                gen_jmp_im(pc_start - s->cs_base); +                gen_helper_fldt_ST0(cpu_env, cpu_A0); +                break; +            case 0x1f: /* fstpt mem */ +                gen_update_cc_op(s); +                gen_jmp_im(pc_start - s->cs_base); +                gen_helper_fstt_ST0(cpu_env, cpu_A0); +                gen_helper_fpop(cpu_env); +                break; +            case 0x2c: /* frstor mem */ +                gen_update_cc_op(s); +                gen_jmp_im(pc_start - s->cs_base); +                gen_helper_frstor(cpu_env, cpu_A0, tcg_const_i32(dflag - 1)); +                break; +            case 0x2e: /* fnsave mem */ +                gen_update_cc_op(s); +                gen_jmp_im(pc_start - s->cs_base); +                gen_helper_fsave(cpu_env, cpu_A0, tcg_const_i32(dflag - 1)); +                break; +            case 0x2f: /* fnstsw mem */ +                gen_helper_fnstsw(cpu_tmp2_i32, cpu_env); +                tcg_gen_qemu_st_i32(cpu_tmp2_i32, cpu_A0, +                                    s->mem_index, MO_LEUW); +                break; +            case 0x3c: /* fbld */ +                gen_update_cc_op(s); +                gen_jmp_im(pc_start - s->cs_base); +                gen_helper_fbld_ST0(cpu_env, cpu_A0); +                break; +            case 0x3e: /* fbstp */ +                gen_update_cc_op(s); +                gen_jmp_im(pc_start - s->cs_base); +                gen_helper_fbst_ST0(cpu_env, cpu_A0); +                gen_helper_fpop(cpu_env); +                break; +            case 0x3d: /* fildll */ +                tcg_gen_qemu_ld_i64(cpu_tmp1_i64, cpu_A0, s->mem_index, MO_LEQ); +                gen_helper_fildll_ST0(cpu_env, cpu_tmp1_i64); +                break; +            case 0x3f: /* fistpll */ +                gen_helper_fistll_ST0(cpu_tmp1_i64, cpu_env); +                tcg_gen_qemu_st_i64(cpu_tmp1_i64, cpu_A0, s->mem_index, MO_LEQ); +                gen_helper_fpop(cpu_env); +                break; +            default: +                goto illegal_op; +            } +        } else { +            /* register float ops */ +            opreg = rm; + +            switch(op) { +            case 0x08: /* fld sti */ +                gen_helper_fpush(cpu_env); +                gen_helper_fmov_ST0_STN(cpu_env, +                                        tcg_const_i32((opreg + 1) & 7)); +                break; +            case 0x09: /* fxchg sti */ +            case 0x29: /* fxchg4 sti, undocumented op */ +            case 0x39: /* fxchg7 sti, undocumented op */ +                gen_helper_fxchg_ST0_STN(cpu_env, tcg_const_i32(opreg)); +                break; +            case 0x0a: /* grp d9/2 */ +                switch(rm) { +                case 0: /* fnop */ +                    /* check exceptions (FreeBSD FPU probe) */ +                    gen_update_cc_op(s); +                    gen_jmp_im(pc_start - s->cs_base); +                    gen_helper_fwait(cpu_env); +                    break; +                default: +                    goto illegal_op; +                } +                break; +            case 0x0c: /* grp d9/4 */ +                switch(rm) { +                case 0: /* fchs */ +                    gen_helper_fchs_ST0(cpu_env); +                    break; +                case 1: /* fabs */ +                    gen_helper_fabs_ST0(cpu_env); +                    break; +                case 4: /* ftst */ +                    gen_helper_fldz_FT0(cpu_env); +                    gen_helper_fcom_ST0_FT0(cpu_env); +                    break; +                case 5: /* fxam */ +                    gen_helper_fxam_ST0(cpu_env); +                    break; +                default: +                    goto illegal_op; +                } +                break; +            case 0x0d: /* grp d9/5 */ +                { +                    switch(rm) { +                    case 0: +                        gen_helper_fpush(cpu_env); +                        gen_helper_fld1_ST0(cpu_env); +                        break; +                    case 1: +                        gen_helper_fpush(cpu_env); +                        gen_helper_fldl2t_ST0(cpu_env); +                        break; +                    case 2: +                        gen_helper_fpush(cpu_env); +                        gen_helper_fldl2e_ST0(cpu_env); +                        break; +                    case 3: +                        gen_helper_fpush(cpu_env); +                        gen_helper_fldpi_ST0(cpu_env); +                        break; +                    case 4: +                        gen_helper_fpush(cpu_env); +                        gen_helper_fldlg2_ST0(cpu_env); +                        break; +                    case 5: +                        gen_helper_fpush(cpu_env); +                        gen_helper_fldln2_ST0(cpu_env); +                        break; +                    case 6: +                        gen_helper_fpush(cpu_env); +                        gen_helper_fldz_ST0(cpu_env); +                        break; +                    default: +                        goto illegal_op; +                    } +                } +                break; +            case 0x0e: /* grp d9/6 */ +                switch(rm) { +                case 0: /* f2xm1 */ +                    gen_helper_f2xm1(cpu_env); +                    break; +                case 1: /* fyl2x */ +                    gen_helper_fyl2x(cpu_env); +                    break; +                case 2: /* fptan */ +                    gen_helper_fptan(cpu_env); +                    break; +                case 3: /* fpatan */ +                    gen_helper_fpatan(cpu_env); +                    break; +                case 4: /* fxtract */ +                    gen_helper_fxtract(cpu_env); +                    break; +                case 5: /* fprem1 */ +                    gen_helper_fprem1(cpu_env); +                    break; +                case 6: /* fdecstp */ +                    gen_helper_fdecstp(cpu_env); +                    break; +                default: +                case 7: /* fincstp */ +                    gen_helper_fincstp(cpu_env); +                    break; +                } +                break; +            case 0x0f: /* grp d9/7 */ +                switch(rm) { +                case 0: /* fprem */ +                    gen_helper_fprem(cpu_env); +                    break; +                case 1: /* fyl2xp1 */ +                    gen_helper_fyl2xp1(cpu_env); +                    break; +                case 2: /* fsqrt */ +                    gen_helper_fsqrt(cpu_env); +                    break; +                case 3: /* fsincos */ +                    gen_helper_fsincos(cpu_env); +                    break; +                case 5: /* fscale */ +                    gen_helper_fscale(cpu_env); +                    break; +                case 4: /* frndint */ +                    gen_helper_frndint(cpu_env); +                    break; +                case 6: /* fsin */ +                    gen_helper_fsin(cpu_env); +                    break; +                default: +                case 7: /* fcos */ +                    gen_helper_fcos(cpu_env); +                    break; +                } +                break; +            case 0x00: case 0x01: case 0x04 ... 0x07: /* fxxx st, sti */ +            case 0x20: case 0x21: case 0x24 ... 0x27: /* fxxx sti, st */ +            case 0x30: case 0x31: case 0x34 ... 0x37: /* fxxxp sti, st */ +                { +                    int op1; + +                    op1 = op & 7; +                    if (op >= 0x20) { +                        gen_helper_fp_arith_STN_ST0(op1, opreg); +                        if (op >= 0x30) +                            gen_helper_fpop(cpu_env); +                    } else { +                        gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); +                        gen_helper_fp_arith_ST0_FT0(op1); +                    } +                } +                break; +            case 0x02: /* fcom */ +            case 0x22: /* fcom2, undocumented op */ +                gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); +                gen_helper_fcom_ST0_FT0(cpu_env); +                break; +            case 0x03: /* fcomp */ +            case 0x23: /* fcomp3, undocumented op */ +            case 0x32: /* fcomp5, undocumented op */ +                gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); +                gen_helper_fcom_ST0_FT0(cpu_env); +                gen_helper_fpop(cpu_env); +                break; +            case 0x15: /* da/5 */ +                switch(rm) { +                case 1: /* fucompp */ +                    gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(1)); +                    gen_helper_fucom_ST0_FT0(cpu_env); +                    gen_helper_fpop(cpu_env); +                    gen_helper_fpop(cpu_env); +                    break; +                default: +                    goto illegal_op; +                } +                break; +            case 0x1c: +                switch(rm) { +                case 0: /* feni (287 only, just do nop here) */ +                    break; +                case 1: /* fdisi (287 only, just do nop here) */ +                    break; +                case 2: /* fclex */ +                    gen_helper_fclex(cpu_env); +                    break; +                case 3: /* fninit */ +                    gen_helper_fninit(cpu_env); +                    break; +                case 4: /* fsetpm (287 only, just do nop here) */ +                    break; +                default: +                    goto illegal_op; +                } +                break; +            case 0x1d: /* fucomi */ +                if (!(s->cpuid_features & CPUID_CMOV)) { +                    goto illegal_op; +                } +                gen_update_cc_op(s); +                gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); +                gen_helper_fucomi_ST0_FT0(cpu_env); +                set_cc_op(s, CC_OP_EFLAGS); +                break; +            case 0x1e: /* fcomi */ +                if (!(s->cpuid_features & CPUID_CMOV)) { +                    goto illegal_op; +                } +                gen_update_cc_op(s); +                gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); +                gen_helper_fcomi_ST0_FT0(cpu_env); +                set_cc_op(s, CC_OP_EFLAGS); +                break; +            case 0x28: /* ffree sti */ +                gen_helper_ffree_STN(cpu_env, tcg_const_i32(opreg)); +                break; +            case 0x2a: /* fst sti */ +                gen_helper_fmov_STN_ST0(cpu_env, tcg_const_i32(opreg)); +                break; +            case 0x2b: /* fstp sti */ +            case 0x0b: /* fstp1 sti, undocumented op */ +            case 0x3a: /* fstp8 sti, undocumented op */ +            case 0x3b: /* fstp9 sti, undocumented op */ +                gen_helper_fmov_STN_ST0(cpu_env, tcg_const_i32(opreg)); +                gen_helper_fpop(cpu_env); +                break; +            case 0x2c: /* fucom st(i) */ +                gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); +                gen_helper_fucom_ST0_FT0(cpu_env); +                break; +            case 0x2d: /* fucomp st(i) */ +                gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); +                gen_helper_fucom_ST0_FT0(cpu_env); +                gen_helper_fpop(cpu_env); +                break; +            case 0x33: /* de/3 */ +                switch(rm) { +                case 1: /* fcompp */ +                    gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(1)); +                    gen_helper_fcom_ST0_FT0(cpu_env); +                    gen_helper_fpop(cpu_env); +                    gen_helper_fpop(cpu_env); +                    break; +                default: +                    goto illegal_op; +                } +                break; +            case 0x38: /* ffreep sti, undocumented op */ +                gen_helper_ffree_STN(cpu_env, tcg_const_i32(opreg)); +                gen_helper_fpop(cpu_env); +                break; +            case 0x3c: /* df/4 */ +                switch(rm) { +                case 0: +                    gen_helper_fnstsw(cpu_tmp2_i32, cpu_env); +                    tcg_gen_extu_i32_tl(cpu_T[0], cpu_tmp2_i32); +                    gen_op_mov_reg_v(MO_16, R_EAX, cpu_T[0]); +                    break; +                default: +                    goto illegal_op; +                } +                break; +            case 0x3d: /* fucomip */ +                if (!(s->cpuid_features & CPUID_CMOV)) { +                    goto illegal_op; +                } +                gen_update_cc_op(s); +                gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); +                gen_helper_fucomi_ST0_FT0(cpu_env); +                gen_helper_fpop(cpu_env); +                set_cc_op(s, CC_OP_EFLAGS); +                break; +            case 0x3e: /* fcomip */ +                if (!(s->cpuid_features & CPUID_CMOV)) { +                    goto illegal_op; +                } +                gen_update_cc_op(s); +                gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); +                gen_helper_fcomi_ST0_FT0(cpu_env); +                gen_helper_fpop(cpu_env); +                set_cc_op(s, CC_OP_EFLAGS); +                break; +            case 0x10 ... 0x13: /* fcmovxx */ +            case 0x18 ... 0x1b: +                { +                    int op1; +                    TCGLabel *l1; +                    static const uint8_t fcmov_cc[8] = { +                        (JCC_B << 1), +                        (JCC_Z << 1), +                        (JCC_BE << 1), +                        (JCC_P << 1), +                    }; + +                    if (!(s->cpuid_features & CPUID_CMOV)) { +                        goto illegal_op; +                    } +                    op1 = fcmov_cc[op & 3] | (((op >> 3) & 1) ^ 1); +                    l1 = gen_new_label(); +                    gen_jcc1_noeob(s, op1, l1); +                    gen_helper_fmov_ST0_STN(cpu_env, tcg_const_i32(opreg)); +                    gen_set_label(l1); +                } +                break; +            default: +                goto illegal_op; +            } +        } +        break; +        /************************/ +        /* string ops */ + +    case 0xa4: /* movsS */ +    case 0xa5: +        ot = mo_b_d(b, dflag); +        if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { +            gen_repz_movs(s, ot, pc_start - s->cs_base, s->pc - s->cs_base); +        } else { +            gen_movs(s, ot); +        } +        break; + +    case 0xaa: /* stosS */ +    case 0xab: +        ot = mo_b_d(b, dflag); +        if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { +            gen_repz_stos(s, ot, pc_start - s->cs_base, s->pc - s->cs_base); +        } else { +            gen_stos(s, ot); +        } +        break; +    case 0xac: /* lodsS */ +    case 0xad: +        ot = mo_b_d(b, dflag); +        if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { +            gen_repz_lods(s, ot, pc_start - s->cs_base, s->pc - s->cs_base); +        } else { +            gen_lods(s, ot); +        } +        break; +    case 0xae: /* scasS */ +    case 0xaf: +        ot = mo_b_d(b, dflag); +        if (prefixes & PREFIX_REPNZ) { +            gen_repz_scas(s, ot, pc_start - s->cs_base, s->pc - s->cs_base, 1); +        } else if (prefixes & PREFIX_REPZ) { +            gen_repz_scas(s, ot, pc_start - s->cs_base, s->pc - s->cs_base, 0); +        } else { +            gen_scas(s, ot); +        } +        break; + +    case 0xa6: /* cmpsS */ +    case 0xa7: +        ot = mo_b_d(b, dflag); +        if (prefixes & PREFIX_REPNZ) { +            gen_repz_cmps(s, ot, pc_start - s->cs_base, s->pc - s->cs_base, 1); +        } else if (prefixes & PREFIX_REPZ) { +            gen_repz_cmps(s, ot, pc_start - s->cs_base, s->pc - s->cs_base, 0); +        } else { +            gen_cmps(s, ot); +        } +        break; +    case 0x6c: /* insS */ +    case 0x6d: +        ot = mo_b_d32(b, dflag); +        tcg_gen_ext16u_tl(cpu_T[0], cpu_regs[R_EDX]); +        gen_check_io(s, ot, pc_start - s->cs_base,  +                     SVM_IOIO_TYPE_MASK | svm_is_rep(prefixes) | 4); +        if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { +            gen_repz_ins(s, ot, pc_start - s->cs_base, s->pc - s->cs_base); +        } else { +            gen_ins(s, ot); +            if (s->tb->cflags & CF_USE_ICOUNT) { +                gen_jmp(s, s->pc - s->cs_base); +            } +        } +        break; +    case 0x6e: /* outsS */ +    case 0x6f: +        ot = mo_b_d32(b, dflag); +        tcg_gen_ext16u_tl(cpu_T[0], cpu_regs[R_EDX]); +        gen_check_io(s, ot, pc_start - s->cs_base, +                     svm_is_rep(prefixes) | 4); +        if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { +            gen_repz_outs(s, ot, pc_start - s->cs_base, s->pc - s->cs_base); +        } else { +            gen_outs(s, ot); +            if (s->tb->cflags & CF_USE_ICOUNT) { +                gen_jmp(s, s->pc - s->cs_base); +            } +        } +        break; + +        /************************/ +        /* port I/O */ + +    case 0xe4: +    case 0xe5: +        ot = mo_b_d32(b, dflag); +        val = cpu_ldub_code(env, s->pc++); +        tcg_gen_movi_tl(cpu_T[0], val); +        gen_check_io(s, ot, pc_start - s->cs_base, +                     SVM_IOIO_TYPE_MASK | svm_is_rep(prefixes)); +        if (s->tb->cflags & CF_USE_ICOUNT) { +            gen_io_start(); +	} +        tcg_gen_movi_i32(cpu_tmp2_i32, val); +        gen_helper_in_func(ot, cpu_T[1], cpu_tmp2_i32); +        gen_op_mov_reg_v(ot, R_EAX, cpu_T[1]); +        if (s->tb->cflags & CF_USE_ICOUNT) { +            gen_io_end(); +            gen_jmp(s, s->pc - s->cs_base); +        } +        break; +    case 0xe6: +    case 0xe7: +        ot = mo_b_d32(b, dflag); +        val = cpu_ldub_code(env, s->pc++); +        tcg_gen_movi_tl(cpu_T[0], val); +        gen_check_io(s, ot, pc_start - s->cs_base, +                     svm_is_rep(prefixes)); +        gen_op_mov_v_reg(ot, cpu_T[1], R_EAX); + +        if (s->tb->cflags & CF_USE_ICOUNT) { +            gen_io_start(); +	} +        tcg_gen_movi_i32(cpu_tmp2_i32, val); +        tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_T[1]); +        gen_helper_out_func(ot, cpu_tmp2_i32, cpu_tmp3_i32); +        if (s->tb->cflags & CF_USE_ICOUNT) { +            gen_io_end(); +            gen_jmp(s, s->pc - s->cs_base); +        } +        break; +    case 0xec: +    case 0xed: +        ot = mo_b_d32(b, dflag); +        tcg_gen_ext16u_tl(cpu_T[0], cpu_regs[R_EDX]); +        gen_check_io(s, ot, pc_start - s->cs_base, +                     SVM_IOIO_TYPE_MASK | svm_is_rep(prefixes)); +        if (s->tb->cflags & CF_USE_ICOUNT) { +            gen_io_start(); +	} +        tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); +        gen_helper_in_func(ot, cpu_T[1], cpu_tmp2_i32); +        gen_op_mov_reg_v(ot, R_EAX, cpu_T[1]); +        if (s->tb->cflags & CF_USE_ICOUNT) { +            gen_io_end(); +            gen_jmp(s, s->pc - s->cs_base); +        } +        break; +    case 0xee: +    case 0xef: +        ot = mo_b_d32(b, dflag); +        tcg_gen_ext16u_tl(cpu_T[0], cpu_regs[R_EDX]); +        gen_check_io(s, ot, pc_start - s->cs_base, +                     svm_is_rep(prefixes)); +        gen_op_mov_v_reg(ot, cpu_T[1], R_EAX); + +        if (s->tb->cflags & CF_USE_ICOUNT) { +            gen_io_start(); +	} +        tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); +        tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_T[1]); +        gen_helper_out_func(ot, cpu_tmp2_i32, cpu_tmp3_i32); +        if (s->tb->cflags & CF_USE_ICOUNT) { +            gen_io_end(); +            gen_jmp(s, s->pc - s->cs_base); +        } +        break; + +        /************************/ +        /* control */ +    case 0xc2: /* ret im */ +        val = cpu_ldsw_code(env, s->pc); +        s->pc += 2; +        ot = gen_pop_T0(s); +        gen_stack_update(s, val + (1 << ot)); +        /* Note that gen_pop_T0 uses a zero-extending load.  */ +        gen_op_jmp_v(cpu_T[0]); +        gen_eob(s); +        break; +    case 0xc3: /* ret */ +        ot = gen_pop_T0(s); +        gen_pop_update(s, ot); +        /* Note that gen_pop_T0 uses a zero-extending load.  */ +        gen_op_jmp_v(cpu_T[0]); +        gen_eob(s); +        break; +    case 0xca: /* lret im */ +        val = cpu_ldsw_code(env, s->pc); +        s->pc += 2; +    do_lret: +        if (s->pe && !s->vm86) { +            gen_update_cc_op(s); +            gen_jmp_im(pc_start - s->cs_base); +            gen_helper_lret_protected(cpu_env, tcg_const_i32(dflag - 1), +                                      tcg_const_i32(val)); +        } else { +            gen_stack_A0(s); +            /* pop offset */ +            gen_op_ld_v(s, dflag, cpu_T[0], cpu_A0); +            /* NOTE: keeping EIP updated is not a problem in case of +               exception */ +            gen_op_jmp_v(cpu_T[0]); +            /* pop selector */ +            gen_op_addl_A0_im(1 << dflag); +            gen_op_ld_v(s, dflag, cpu_T[0], cpu_A0); +            gen_op_movl_seg_T0_vm(R_CS); +            /* add stack offset */ +            gen_stack_update(s, val + (2 << dflag)); +        } +        gen_eob(s); +        break; +    case 0xcb: /* lret */ +        val = 0; +        goto do_lret; +    case 0xcf: /* iret */ +        gen_svm_check_intercept(s, pc_start, SVM_EXIT_IRET); +        if (!s->pe) { +            /* real mode */ +            gen_helper_iret_real(cpu_env, tcg_const_i32(dflag - 1)); +            set_cc_op(s, CC_OP_EFLAGS); +        } else if (s->vm86) { +            if (s->iopl != 3) { +                gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); +            } else { +                gen_helper_iret_real(cpu_env, tcg_const_i32(dflag - 1)); +                set_cc_op(s, CC_OP_EFLAGS); +            } +        } else { +            gen_update_cc_op(s); +            gen_jmp_im(pc_start - s->cs_base); +            gen_helper_iret_protected(cpu_env, tcg_const_i32(dflag - 1), +                                      tcg_const_i32(s->pc - s->cs_base)); +            set_cc_op(s, CC_OP_EFLAGS); +        } +        gen_eob(s); +        break; +    case 0xe8: /* call im */ +        { +            if (dflag != MO_16) { +                tval = (int32_t)insn_get(env, s, MO_32); +            } else { +                tval = (int16_t)insn_get(env, s, MO_16); +            } +            next_eip = s->pc - s->cs_base; +            tval += next_eip; +            if (dflag == MO_16) { +                tval &= 0xffff; +            } else if (!CODE64(s)) { +                tval &= 0xffffffff; +            } +            tcg_gen_movi_tl(cpu_T[0], next_eip); +            gen_push_v(s, cpu_T[0]); +            gen_jmp(s, tval); +        } +        break; +    case 0x9a: /* lcall im */ +        { +            unsigned int selector, offset; + +            if (CODE64(s)) +                goto illegal_op; +            ot = dflag; +            offset = insn_get(env, s, ot); +            selector = insn_get(env, s, MO_16); + +            tcg_gen_movi_tl(cpu_T[0], selector); +            tcg_gen_movi_tl(cpu_T[1], offset); +        } +        goto do_lcall; +    case 0xe9: /* jmp im */ +        if (dflag != MO_16) { +            tval = (int32_t)insn_get(env, s, MO_32); +        } else { +            tval = (int16_t)insn_get(env, s, MO_16); +        } +        tval += s->pc - s->cs_base; +        if (dflag == MO_16) { +            tval &= 0xffff; +        } else if (!CODE64(s)) { +            tval &= 0xffffffff; +        } +        gen_jmp(s, tval); +        break; +    case 0xea: /* ljmp im */ +        { +            unsigned int selector, offset; + +            if (CODE64(s)) +                goto illegal_op; +            ot = dflag; +            offset = insn_get(env, s, ot); +            selector = insn_get(env, s, MO_16); + +            tcg_gen_movi_tl(cpu_T[0], selector); +            tcg_gen_movi_tl(cpu_T[1], offset); +        } +        goto do_ljmp; +    case 0xeb: /* jmp Jb */ +        tval = (int8_t)insn_get(env, s, MO_8); +        tval += s->pc - s->cs_base; +        if (dflag == MO_16) { +            tval &= 0xffff; +        } +        gen_jmp(s, tval); +        break; +    case 0x70 ... 0x7f: /* jcc Jb */ +        tval = (int8_t)insn_get(env, s, MO_8); +        goto do_jcc; +    case 0x180 ... 0x18f: /* jcc Jv */ +        if (dflag != MO_16) { +            tval = (int32_t)insn_get(env, s, MO_32); +        } else { +            tval = (int16_t)insn_get(env, s, MO_16); +        } +    do_jcc: +        next_eip = s->pc - s->cs_base; +        tval += next_eip; +        if (dflag == MO_16) { +            tval &= 0xffff; +        } +        gen_jcc(s, b, tval, next_eip); +        break; + +    case 0x190 ... 0x19f: /* setcc Gv */ +        modrm = cpu_ldub_code(env, s->pc++); +        gen_setcc1(s, b, cpu_T[0]); +        gen_ldst_modrm(env, s, modrm, MO_8, OR_TMP0, 1); +        break; +    case 0x140 ... 0x14f: /* cmov Gv, Ev */ +        if (!(s->cpuid_features & CPUID_CMOV)) { +            goto illegal_op; +        } +        ot = dflag; +        modrm = cpu_ldub_code(env, s->pc++); +        reg = ((modrm >> 3) & 7) | rex_r; +        gen_cmovcc1(env, s, ot, b, modrm, reg); +        break; + +        /************************/ +        /* flags */ +    case 0x9c: /* pushf */ +        gen_svm_check_intercept(s, pc_start, SVM_EXIT_PUSHF); +        if (s->vm86 && s->iopl != 3) { +            gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); +        } else { +            gen_update_cc_op(s); +            gen_helper_read_eflags(cpu_T[0], cpu_env); +            gen_push_v(s, cpu_T[0]); +        } +        break; +    case 0x9d: /* popf */ +        gen_svm_check_intercept(s, pc_start, SVM_EXIT_POPF); +        if (s->vm86 && s->iopl != 3) { +            gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); +        } else { +            ot = gen_pop_T0(s); +            if (s->cpl == 0) { +                if (dflag != MO_16) { +                    gen_helper_write_eflags(cpu_env, cpu_T[0], +                                            tcg_const_i32((TF_MASK | AC_MASK | +                                                           ID_MASK | NT_MASK | +                                                           IF_MASK | +                                                           IOPL_MASK))); +                } else { +                    gen_helper_write_eflags(cpu_env, cpu_T[0], +                                            tcg_const_i32((TF_MASK | AC_MASK | +                                                           ID_MASK | NT_MASK | +                                                           IF_MASK | IOPL_MASK) +                                                          & 0xffff)); +                } +            } else { +                if (s->cpl <= s->iopl) { +                    if (dflag != MO_16) { +                        gen_helper_write_eflags(cpu_env, cpu_T[0], +                                                tcg_const_i32((TF_MASK | +                                                               AC_MASK | +                                                               ID_MASK | +                                                               NT_MASK | +                                                               IF_MASK))); +                    } else { +                        gen_helper_write_eflags(cpu_env, cpu_T[0], +                                                tcg_const_i32((TF_MASK | +                                                               AC_MASK | +                                                               ID_MASK | +                                                               NT_MASK | +                                                               IF_MASK) +                                                              & 0xffff)); +                    } +                } else { +                    if (dflag != MO_16) { +                        gen_helper_write_eflags(cpu_env, cpu_T[0], +                                           tcg_const_i32((TF_MASK | AC_MASK | +                                                          ID_MASK | NT_MASK))); +                    } else { +                        gen_helper_write_eflags(cpu_env, cpu_T[0], +                                           tcg_const_i32((TF_MASK | AC_MASK | +                                                          ID_MASK | NT_MASK) +                                                         & 0xffff)); +                    } +                } +            } +            gen_pop_update(s, ot); +            set_cc_op(s, CC_OP_EFLAGS); +            /* abort translation because TF/AC flag may change */ +            gen_jmp_im(s->pc - s->cs_base); +            gen_eob(s); +        } +        break; +    case 0x9e: /* sahf */ +        if (CODE64(s) && !(s->cpuid_ext3_features & CPUID_EXT3_LAHF_LM)) +            goto illegal_op; +        gen_op_mov_v_reg(MO_8, cpu_T[0], R_AH); +        gen_compute_eflags(s); +        tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, CC_O); +        tcg_gen_andi_tl(cpu_T[0], cpu_T[0], CC_S | CC_Z | CC_A | CC_P | CC_C); +        tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, cpu_T[0]); +        break; +    case 0x9f: /* lahf */ +        if (CODE64(s) && !(s->cpuid_ext3_features & CPUID_EXT3_LAHF_LM)) +            goto illegal_op; +        gen_compute_eflags(s); +        /* Note: gen_compute_eflags() only gives the condition codes */ +        tcg_gen_ori_tl(cpu_T[0], cpu_cc_src, 0x02); +        gen_op_mov_reg_v(MO_8, R_AH, cpu_T[0]); +        break; +    case 0xf5: /* cmc */ +        gen_compute_eflags(s); +        tcg_gen_xori_tl(cpu_cc_src, cpu_cc_src, CC_C); +        break; +    case 0xf8: /* clc */ +        gen_compute_eflags(s); +        tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, ~CC_C); +        break; +    case 0xf9: /* stc */ +        gen_compute_eflags(s); +        tcg_gen_ori_tl(cpu_cc_src, cpu_cc_src, CC_C); +        break; +    case 0xfc: /* cld */ +        tcg_gen_movi_i32(cpu_tmp2_i32, 1); +        tcg_gen_st_i32(cpu_tmp2_i32, cpu_env, offsetof(CPUX86State, df)); +        break; +    case 0xfd: /* std */ +        tcg_gen_movi_i32(cpu_tmp2_i32, -1); +        tcg_gen_st_i32(cpu_tmp2_i32, cpu_env, offsetof(CPUX86State, df)); +        break; + +        /************************/ +        /* bit operations */ +    case 0x1ba: /* bt/bts/btr/btc Gv, im */ +        ot = dflag; +        modrm = cpu_ldub_code(env, s->pc++); +        op = (modrm >> 3) & 7; +        mod = (modrm >> 6) & 3; +        rm = (modrm & 7) | REX_B(s); +        if (mod != 3) { +            s->rip_offset = 1; +            gen_lea_modrm(env, s, modrm); +            gen_op_ld_v(s, ot, cpu_T[0], cpu_A0); +        } else { +            gen_op_mov_v_reg(ot, cpu_T[0], rm); +        } +        /* load shift */ +        val = cpu_ldub_code(env, s->pc++); +        tcg_gen_movi_tl(cpu_T[1], val); +        if (op < 4) +            goto illegal_op; +        op -= 4; +        goto bt_op; +    case 0x1a3: /* bt Gv, Ev */ +        op = 0; +        goto do_btx; +    case 0x1ab: /* bts */ +        op = 1; +        goto do_btx; +    case 0x1b3: /* btr */ +        op = 2; +        goto do_btx; +    case 0x1bb: /* btc */ +        op = 3; +    do_btx: +        ot = dflag; +        modrm = cpu_ldub_code(env, s->pc++); +        reg = ((modrm >> 3) & 7) | rex_r; +        mod = (modrm >> 6) & 3; +        rm = (modrm & 7) | REX_B(s); +        gen_op_mov_v_reg(MO_32, cpu_T[1], reg); +        if (mod != 3) { +            gen_lea_modrm(env, s, modrm); +            /* specific case: we need to add a displacement */ +            gen_exts(ot, cpu_T[1]); +            tcg_gen_sari_tl(cpu_tmp0, cpu_T[1], 3 + ot); +            tcg_gen_shli_tl(cpu_tmp0, cpu_tmp0, ot); +            tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0); +            gen_op_ld_v(s, ot, cpu_T[0], cpu_A0); +        } else { +            gen_op_mov_v_reg(ot, cpu_T[0], rm); +        } +    bt_op: +        tcg_gen_andi_tl(cpu_T[1], cpu_T[1], (1 << (3 + ot)) - 1); +        tcg_gen_shr_tl(cpu_tmp4, cpu_T[0], cpu_T[1]); +        switch(op) { +        case 0: +            break; +        case 1: +            tcg_gen_movi_tl(cpu_tmp0, 1); +            tcg_gen_shl_tl(cpu_tmp0, cpu_tmp0, cpu_T[1]); +            tcg_gen_or_tl(cpu_T[0], cpu_T[0], cpu_tmp0); +            break; +        case 2: +            tcg_gen_movi_tl(cpu_tmp0, 1); +            tcg_gen_shl_tl(cpu_tmp0, cpu_tmp0, cpu_T[1]); +            tcg_gen_andc_tl(cpu_T[0], cpu_T[0], cpu_tmp0); +            break; +        default: +        case 3: +            tcg_gen_movi_tl(cpu_tmp0, 1); +            tcg_gen_shl_tl(cpu_tmp0, cpu_tmp0, cpu_T[1]); +            tcg_gen_xor_tl(cpu_T[0], cpu_T[0], cpu_tmp0); +            break; +        } +        if (op != 0) { +            if (mod != 3) { +                gen_op_st_v(s, ot, cpu_T[0], cpu_A0); +            } else { +                gen_op_mov_reg_v(ot, rm, cpu_T[0]); +            } +        } + +        /* Delay all CC updates until after the store above.  Note that +           C is the result of the test, Z is unchanged, and the others +           are all undefined.  */ +        switch (s->cc_op) { +        case CC_OP_MULB ... CC_OP_MULQ: +        case CC_OP_ADDB ... CC_OP_ADDQ: +        case CC_OP_ADCB ... CC_OP_ADCQ: +        case CC_OP_SUBB ... CC_OP_SUBQ: +        case CC_OP_SBBB ... CC_OP_SBBQ: +        case CC_OP_LOGICB ... CC_OP_LOGICQ: +        case CC_OP_INCB ... CC_OP_INCQ: +        case CC_OP_DECB ... CC_OP_DECQ: +        case CC_OP_SHLB ... CC_OP_SHLQ: +        case CC_OP_SARB ... CC_OP_SARQ: +        case CC_OP_BMILGB ... CC_OP_BMILGQ: +            /* Z was going to be computed from the non-zero status of CC_DST. +               We can get that same Z value (and the new C value) by leaving +               CC_DST alone, setting CC_SRC, and using a CC_OP_SAR of the +               same width.  */ +            tcg_gen_mov_tl(cpu_cc_src, cpu_tmp4); +            set_cc_op(s, ((s->cc_op - CC_OP_MULB) & 3) + CC_OP_SARB); +            break; +        default: +            /* Otherwise, generate EFLAGS and replace the C bit.  */ +            gen_compute_eflags(s); +            tcg_gen_deposit_tl(cpu_cc_src, cpu_cc_src, cpu_tmp4, +                               ctz32(CC_C), 1); +            break; +        } +        break; +    case 0x1bc: /* bsf / tzcnt */ +    case 0x1bd: /* bsr / lzcnt */ +        ot = dflag; +        modrm = cpu_ldub_code(env, s->pc++); +        reg = ((modrm >> 3) & 7) | rex_r; +        gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); +        gen_extu(ot, cpu_T[0]); + +        /* Note that lzcnt and tzcnt are in different extensions.  */ +        if ((prefixes & PREFIX_REPZ) +            && (b & 1 +                ? s->cpuid_ext3_features & CPUID_EXT3_ABM +                : s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI1)) { +            int size = 8 << ot; +            tcg_gen_mov_tl(cpu_cc_src, cpu_T[0]); +            if (b & 1) { +                /* For lzcnt, reduce the target_ulong result by the +                   number of zeros that we expect to find at the top.  */ +                gen_helper_clz(cpu_T[0], cpu_T[0]); +                tcg_gen_subi_tl(cpu_T[0], cpu_T[0], TARGET_LONG_BITS - size); +            } else { +                /* For tzcnt, a zero input must return the operand size: +                   force all bits outside the operand size to 1.  */ +                target_ulong mask = (target_ulong)-2 << (size - 1); +                tcg_gen_ori_tl(cpu_T[0], cpu_T[0], mask); +                gen_helper_ctz(cpu_T[0], cpu_T[0]); +            } +            /* For lzcnt/tzcnt, C and Z bits are defined and are +               related to the result.  */ +            gen_op_update1_cc(); +            set_cc_op(s, CC_OP_BMILGB + ot); +        } else { +            /* For bsr/bsf, only the Z bit is defined and it is related +               to the input and not the result.  */ +            tcg_gen_mov_tl(cpu_cc_dst, cpu_T[0]); +            set_cc_op(s, CC_OP_LOGICB + ot); +            if (b & 1) { +                /* For bsr, return the bit index of the first 1 bit, +                   not the count of leading zeros.  */ +                gen_helper_clz(cpu_T[0], cpu_T[0]); +                tcg_gen_xori_tl(cpu_T[0], cpu_T[0], TARGET_LONG_BITS - 1); +            } else { +                gen_helper_ctz(cpu_T[0], cpu_T[0]); +            } +            /* ??? The manual says that the output is undefined when the +               input is zero, but real hardware leaves it unchanged, and +               real programs appear to depend on that.  */ +            tcg_gen_movi_tl(cpu_tmp0, 0); +            tcg_gen_movcond_tl(TCG_COND_EQ, cpu_T[0], cpu_cc_dst, cpu_tmp0, +                               cpu_regs[reg], cpu_T[0]); +        } +        gen_op_mov_reg_v(ot, reg, cpu_T[0]); +        break; +        /************************/ +        /* bcd */ +    case 0x27: /* daa */ +        if (CODE64(s)) +            goto illegal_op; +        gen_update_cc_op(s); +        gen_helper_daa(cpu_env); +        set_cc_op(s, CC_OP_EFLAGS); +        break; +    case 0x2f: /* das */ +        if (CODE64(s)) +            goto illegal_op; +        gen_update_cc_op(s); +        gen_helper_das(cpu_env); +        set_cc_op(s, CC_OP_EFLAGS); +        break; +    case 0x37: /* aaa */ +        if (CODE64(s)) +            goto illegal_op; +        gen_update_cc_op(s); +        gen_helper_aaa(cpu_env); +        set_cc_op(s, CC_OP_EFLAGS); +        break; +    case 0x3f: /* aas */ +        if (CODE64(s)) +            goto illegal_op; +        gen_update_cc_op(s); +        gen_helper_aas(cpu_env); +        set_cc_op(s, CC_OP_EFLAGS); +        break; +    case 0xd4: /* aam */ +        if (CODE64(s)) +            goto illegal_op; +        val = cpu_ldub_code(env, s->pc++); +        if (val == 0) { +            gen_exception(s, EXCP00_DIVZ, pc_start - s->cs_base); +        } else { +            gen_helper_aam(cpu_env, tcg_const_i32(val)); +            set_cc_op(s, CC_OP_LOGICB); +        } +        break; +    case 0xd5: /* aad */ +        if (CODE64(s)) +            goto illegal_op; +        val = cpu_ldub_code(env, s->pc++); +        gen_helper_aad(cpu_env, tcg_const_i32(val)); +        set_cc_op(s, CC_OP_LOGICB); +        break; +        /************************/ +        /* misc */ +    case 0x90: /* nop */ +        /* XXX: correct lock test for all insn */ +        if (prefixes & PREFIX_LOCK) { +            goto illegal_op; +        } +        /* If REX_B is set, then this is xchg eax, r8d, not a nop.  */ +        if (REX_B(s)) { +            goto do_xchg_reg_eax; +        } +        if (prefixes & PREFIX_REPZ) { +            gen_update_cc_op(s); +            gen_jmp_im(pc_start - s->cs_base); +            gen_helper_pause(cpu_env, tcg_const_i32(s->pc - pc_start)); +            s->is_jmp = DISAS_TB_JUMP; +        } +        break; +    case 0x9b: /* fwait */ +        if ((s->flags & (HF_MP_MASK | HF_TS_MASK)) == +            (HF_MP_MASK | HF_TS_MASK)) { +            gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); +        } else { +            gen_update_cc_op(s); +            gen_jmp_im(pc_start - s->cs_base); +            gen_helper_fwait(cpu_env); +        } +        break; +    case 0xcc: /* int3 */ +        gen_interrupt(s, EXCP03_INT3, pc_start - s->cs_base, s->pc - s->cs_base); +        break; +    case 0xcd: /* int N */ +        val = cpu_ldub_code(env, s->pc++); +        if (s->vm86 && s->iopl != 3) { +            gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); +        } else { +            gen_interrupt(s, val, pc_start - s->cs_base, s->pc - s->cs_base); +        } +        break; +    case 0xce: /* into */ +        if (CODE64(s)) +            goto illegal_op; +        gen_update_cc_op(s); +        gen_jmp_im(pc_start - s->cs_base); +        gen_helper_into(cpu_env, tcg_const_i32(s->pc - pc_start)); +        break; +#ifdef WANT_ICEBP +    case 0xf1: /* icebp (undocumented, exits to external debugger) */ +        gen_svm_check_intercept(s, pc_start, SVM_EXIT_ICEBP); +#if 1 +        gen_debug(s, pc_start - s->cs_base); +#else +        /* start debug */ +        tb_flush(CPU(x86_env_get_cpu(env))); +        qemu_set_log(CPU_LOG_INT | CPU_LOG_TB_IN_ASM); +#endif +        break; +#endif +    case 0xfa: /* cli */ +        if (!s->vm86) { +            if (s->cpl <= s->iopl) { +                gen_helper_cli(cpu_env); +            } else { +                gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); +            } +        } else { +            if (s->iopl == 3) { +                gen_helper_cli(cpu_env); +            } else { +                gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); +            } +        } +        break; +    case 0xfb: /* sti */ +        if (!s->vm86) { +            if (s->cpl <= s->iopl) { +            gen_sti: +                gen_helper_sti(cpu_env); +                /* interruptions are enabled only the first insn after sti */ +                /* If several instructions disable interrupts, only the +                   _first_ does it */ +                if (!(s->tb->flags & HF_INHIBIT_IRQ_MASK)) +                    gen_helper_set_inhibit_irq(cpu_env); +                /* give a chance to handle pending irqs */ +                gen_jmp_im(s->pc - s->cs_base); +                gen_eob(s); +            } else { +                gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); +            } +        } else { +            if (s->iopl == 3) { +                goto gen_sti; +            } else { +                gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); +            } +        } +        break; +    case 0x62: /* bound */ +        if (CODE64(s)) +            goto illegal_op; +        ot = dflag; +        modrm = cpu_ldub_code(env, s->pc++); +        reg = (modrm >> 3) & 7; +        mod = (modrm >> 6) & 3; +        if (mod == 3) +            goto illegal_op; +        gen_op_mov_v_reg(ot, cpu_T[0], reg); +        gen_lea_modrm(env, s, modrm); +        gen_jmp_im(pc_start - s->cs_base); +        tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); +        if (ot == MO_16) { +            gen_helper_boundw(cpu_env, cpu_A0, cpu_tmp2_i32); +        } else { +            gen_helper_boundl(cpu_env, cpu_A0, cpu_tmp2_i32); +        } +        break; +    case 0x1c8 ... 0x1cf: /* bswap reg */ +        reg = (b & 7) | REX_B(s); +#ifdef TARGET_X86_64 +        if (dflag == MO_64) { +            gen_op_mov_v_reg(MO_64, cpu_T[0], reg); +            tcg_gen_bswap64_i64(cpu_T[0], cpu_T[0]); +            gen_op_mov_reg_v(MO_64, reg, cpu_T[0]); +        } else +#endif +        { +            gen_op_mov_v_reg(MO_32, cpu_T[0], reg); +            tcg_gen_ext32u_tl(cpu_T[0], cpu_T[0]); +            tcg_gen_bswap32_tl(cpu_T[0], cpu_T[0]); +            gen_op_mov_reg_v(MO_32, reg, cpu_T[0]); +        } +        break; +    case 0xd6: /* salc */ +        if (CODE64(s)) +            goto illegal_op; +        gen_compute_eflags_c(s, cpu_T[0]); +        tcg_gen_neg_tl(cpu_T[0], cpu_T[0]); +        gen_op_mov_reg_v(MO_8, R_EAX, cpu_T[0]); +        break; +    case 0xe0: /* loopnz */ +    case 0xe1: /* loopz */ +    case 0xe2: /* loop */ +    case 0xe3: /* jecxz */ +        { +            TCGLabel *l1, *l2, *l3; + +            tval = (int8_t)insn_get(env, s, MO_8); +            next_eip = s->pc - s->cs_base; +            tval += next_eip; +            if (dflag == MO_16) { +                tval &= 0xffff; +            } + +            l1 = gen_new_label(); +            l2 = gen_new_label(); +            l3 = gen_new_label(); +            b &= 3; +            switch(b) { +            case 0: /* loopnz */ +            case 1: /* loopz */ +                gen_op_add_reg_im(s->aflag, R_ECX, -1); +                gen_op_jz_ecx(s->aflag, l3); +                gen_jcc1(s, (JCC_Z << 1) | (b ^ 1), l1); +                break; +            case 2: /* loop */ +                gen_op_add_reg_im(s->aflag, R_ECX, -1); +                gen_op_jnz_ecx(s->aflag, l1); +                break; +            default: +            case 3: /* jcxz */ +                gen_op_jz_ecx(s->aflag, l1); +                break; +            } + +            gen_set_label(l3); +            gen_jmp_im(next_eip); +            tcg_gen_br(l2); + +            gen_set_label(l1); +            gen_jmp_im(tval); +            gen_set_label(l2); +            gen_eob(s); +        } +        break; +    case 0x130: /* wrmsr */ +    case 0x132: /* rdmsr */ +        if (s->cpl != 0) { +            gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); +        } else { +            gen_update_cc_op(s); +            gen_jmp_im(pc_start - s->cs_base); +            if (b & 2) { +                gen_helper_rdmsr(cpu_env); +            } else { +                gen_helper_wrmsr(cpu_env); +            } +        } +        break; +    case 0x131: /* rdtsc */ +        gen_update_cc_op(s); +        gen_jmp_im(pc_start - s->cs_base); +        if (s->tb->cflags & CF_USE_ICOUNT) { +            gen_io_start(); +	} +        gen_helper_rdtsc(cpu_env); +        if (s->tb->cflags & CF_USE_ICOUNT) { +            gen_io_end(); +            gen_jmp(s, s->pc - s->cs_base); +        } +        break; +    case 0x133: /* rdpmc */ +        gen_update_cc_op(s); +        gen_jmp_im(pc_start - s->cs_base); +        gen_helper_rdpmc(cpu_env); +        break; +    case 0x134: /* sysenter */ +        /* For Intel SYSENTER is valid on 64-bit */ +        if (CODE64(s) && env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1) +            goto illegal_op; +        if (!s->pe) { +            gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); +        } else { +            gen_update_cc_op(s); +            gen_jmp_im(pc_start - s->cs_base); +            gen_helper_sysenter(cpu_env); +            gen_eob(s); +        } +        break; +    case 0x135: /* sysexit */ +        /* For Intel SYSEXIT is valid on 64-bit */ +        if (CODE64(s) && env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1) +            goto illegal_op; +        if (!s->pe) { +            gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); +        } else { +            gen_update_cc_op(s); +            gen_jmp_im(pc_start - s->cs_base); +            gen_helper_sysexit(cpu_env, tcg_const_i32(dflag - 1)); +            gen_eob(s); +        } +        break; +#ifdef TARGET_X86_64 +    case 0x105: /* syscall */ +        /* XXX: is it usable in real mode ? */ +        gen_update_cc_op(s); +        gen_jmp_im(pc_start - s->cs_base); +        gen_helper_syscall(cpu_env, tcg_const_i32(s->pc - pc_start)); +        gen_eob(s); +        break; +    case 0x107: /* sysret */ +        if (!s->pe) { +            gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); +        } else { +            gen_update_cc_op(s); +            gen_jmp_im(pc_start - s->cs_base); +            gen_helper_sysret(cpu_env, tcg_const_i32(dflag - 1)); +            /* condition codes are modified only in long mode */ +            if (s->lma) { +                set_cc_op(s, CC_OP_EFLAGS); +            } +            gen_eob(s); +        } +        break; +#endif +    case 0x1a2: /* cpuid */ +        gen_update_cc_op(s); +        gen_jmp_im(pc_start - s->cs_base); +        gen_helper_cpuid(cpu_env); +        break; +    case 0xf4: /* hlt */ +        if (s->cpl != 0) { +            gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); +        } else { +            gen_update_cc_op(s); +            gen_jmp_im(pc_start - s->cs_base); +            gen_helper_hlt(cpu_env, tcg_const_i32(s->pc - pc_start)); +            s->is_jmp = DISAS_TB_JUMP; +        } +        break; +    case 0x100: +        modrm = cpu_ldub_code(env, s->pc++); +        mod = (modrm >> 6) & 3; +        op = (modrm >> 3) & 7; +        switch(op) { +        case 0: /* sldt */ +            if (!s->pe || s->vm86) +                goto illegal_op; +            gen_svm_check_intercept(s, pc_start, SVM_EXIT_LDTR_READ); +            tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,ldt.selector)); +            ot = mod == 3 ? dflag : MO_16; +            gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); +            break; +        case 2: /* lldt */ +            if (!s->pe || s->vm86) +                goto illegal_op; +            if (s->cpl != 0) { +                gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); +            } else { +                gen_svm_check_intercept(s, pc_start, SVM_EXIT_LDTR_WRITE); +                gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); +                gen_jmp_im(pc_start - s->cs_base); +                tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); +                gen_helper_lldt(cpu_env, cpu_tmp2_i32); +            } +            break; +        case 1: /* str */ +            if (!s->pe || s->vm86) +                goto illegal_op; +            gen_svm_check_intercept(s, pc_start, SVM_EXIT_TR_READ); +            tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,tr.selector)); +            ot = mod == 3 ? dflag : MO_16; +            gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); +            break; +        case 3: /* ltr */ +            if (!s->pe || s->vm86) +                goto illegal_op; +            if (s->cpl != 0) { +                gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); +            } else { +                gen_svm_check_intercept(s, pc_start, SVM_EXIT_TR_WRITE); +                gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); +                gen_jmp_im(pc_start - s->cs_base); +                tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T[0]); +                gen_helper_ltr(cpu_env, cpu_tmp2_i32); +            } +            break; +        case 4: /* verr */ +        case 5: /* verw */ +            if (!s->pe || s->vm86) +                goto illegal_op; +            gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); +            gen_update_cc_op(s); +            if (op == 4) { +                gen_helper_verr(cpu_env, cpu_T[0]); +            } else { +                gen_helper_verw(cpu_env, cpu_T[0]); +            } +            set_cc_op(s, CC_OP_EFLAGS); +            break; +        default: +            goto illegal_op; +        } +        break; +    case 0x101: +        modrm = cpu_ldub_code(env, s->pc++); +        mod = (modrm >> 6) & 3; +        op = (modrm >> 3) & 7; +        rm = modrm & 7; +        switch(op) { +        case 0: /* sgdt */ +            if (mod == 3) +                goto illegal_op; +            gen_svm_check_intercept(s, pc_start, SVM_EXIT_GDTR_READ); +            gen_lea_modrm(env, s, modrm); +            tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, gdt.limit)); +            gen_op_st_v(s, MO_16, cpu_T[0], cpu_A0); +            gen_add_A0_im(s, 2); +            tcg_gen_ld_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, gdt.base)); +            if (dflag == MO_16) { +                tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 0xffffff); +            } +            gen_op_st_v(s, CODE64(s) + MO_32, cpu_T[0], cpu_A0); +            break; +        case 1: +            if (mod == 3) { +                switch (rm) { +                case 0: /* monitor */ +                    if (!(s->cpuid_ext_features & CPUID_EXT_MONITOR) || +                        s->cpl != 0) +                        goto illegal_op; +                    gen_update_cc_op(s); +                    gen_jmp_im(pc_start - s->cs_base); +                    tcg_gen_mov_tl(cpu_A0, cpu_regs[R_EAX]); +                    gen_extu(s->aflag, cpu_A0); +                    gen_add_A0_ds_seg(s); +                    gen_helper_monitor(cpu_env, cpu_A0); +                    break; +                case 1: /* mwait */ +                    if (!(s->cpuid_ext_features & CPUID_EXT_MONITOR) || +                        s->cpl != 0) +                        goto illegal_op; +                    gen_update_cc_op(s); +                    gen_jmp_im(pc_start - s->cs_base); +                    gen_helper_mwait(cpu_env, tcg_const_i32(s->pc - pc_start)); +                    gen_eob(s); +                    break; +                case 2: /* clac */ +                    if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_SMAP) || +                        s->cpl != 0) { +                        goto illegal_op; +                    } +                    gen_helper_clac(cpu_env); +                    gen_jmp_im(s->pc - s->cs_base); +                    gen_eob(s); +                    break; +                case 3: /* stac */ +                    if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_SMAP) || +                        s->cpl != 0) { +                        goto illegal_op; +                    } +                    gen_helper_stac(cpu_env); +                    gen_jmp_im(s->pc - s->cs_base); +                    gen_eob(s); +                    break; +                default: +                    goto illegal_op; +                } +            } else { /* sidt */ +                gen_svm_check_intercept(s, pc_start, SVM_EXIT_IDTR_READ); +                gen_lea_modrm(env, s, modrm); +                tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, idt.limit)); +                gen_op_st_v(s, MO_16, cpu_T[0], cpu_A0); +                gen_add_A0_im(s, 2); +                tcg_gen_ld_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, idt.base)); +                if (dflag == MO_16) { +                    tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 0xffffff); +                } +                gen_op_st_v(s, CODE64(s) + MO_32, cpu_T[0], cpu_A0); +            } +            break; +        case 2: /* lgdt */ +        case 3: /* lidt */ +            if (mod == 3) { +                gen_update_cc_op(s); +                gen_jmp_im(pc_start - s->cs_base); +                switch(rm) { +                case 0: /* VMRUN */ +                    if (!(s->flags & HF_SVME_MASK) || !s->pe) +                        goto illegal_op; +                    if (s->cpl != 0) { +                        gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); +                        break; +                    } else { +                        gen_helper_vmrun(cpu_env, tcg_const_i32(s->aflag - 1), +                                         tcg_const_i32(s->pc - pc_start)); +                        tcg_gen_exit_tb(0); +                        s->is_jmp = DISAS_TB_JUMP; +                    } +                    break; +                case 1: /* VMMCALL */ +                    if (!(s->flags & HF_SVME_MASK)) +                        goto illegal_op; +                    gen_helper_vmmcall(cpu_env); +                    break; +                case 2: /* VMLOAD */ +                    if (!(s->flags & HF_SVME_MASK) || !s->pe) +                        goto illegal_op; +                    if (s->cpl != 0) { +                        gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); +                        break; +                    } else { +                        gen_helper_vmload(cpu_env, tcg_const_i32(s->aflag - 1)); +                    } +                    break; +                case 3: /* VMSAVE */ +                    if (!(s->flags & HF_SVME_MASK) || !s->pe) +                        goto illegal_op; +                    if (s->cpl != 0) { +                        gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); +                        break; +                    } else { +                        gen_helper_vmsave(cpu_env, tcg_const_i32(s->aflag - 1)); +                    } +                    break; +                case 4: /* STGI */ +                    if ((!(s->flags & HF_SVME_MASK) && +                         !(s->cpuid_ext3_features & CPUID_EXT3_SKINIT)) ||  +                        !s->pe) +                        goto illegal_op; +                    if (s->cpl != 0) { +                        gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); +                        break; +                    } else { +                        gen_helper_stgi(cpu_env); +                    } +                    break; +                case 5: /* CLGI */ +                    if (!(s->flags & HF_SVME_MASK) || !s->pe) +                        goto illegal_op; +                    if (s->cpl != 0) { +                        gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); +                        break; +                    } else { +                        gen_helper_clgi(cpu_env); +                    } +                    break; +                case 6: /* SKINIT */ +                    if ((!(s->flags & HF_SVME_MASK) &&  +                         !(s->cpuid_ext3_features & CPUID_EXT3_SKINIT)) ||  +                        !s->pe) +                        goto illegal_op; +                    gen_helper_skinit(cpu_env); +                    break; +                case 7: /* INVLPGA */ +                    if (!(s->flags & HF_SVME_MASK) || !s->pe) +                        goto illegal_op; +                    if (s->cpl != 0) { +                        gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); +                        break; +                    } else { +                        gen_helper_invlpga(cpu_env, +                                           tcg_const_i32(s->aflag - 1)); +                    } +                    break; +                default: +                    goto illegal_op; +                } +            } else if (s->cpl != 0) { +                gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); +            } else { +                gen_svm_check_intercept(s, pc_start, +                                        op==2 ? SVM_EXIT_GDTR_WRITE : SVM_EXIT_IDTR_WRITE); +                gen_lea_modrm(env, s, modrm); +                gen_op_ld_v(s, MO_16, cpu_T[1], cpu_A0); +                gen_add_A0_im(s, 2); +                gen_op_ld_v(s, CODE64(s) + MO_32, cpu_T[0], cpu_A0); +                if (dflag == MO_16) { +                    tcg_gen_andi_tl(cpu_T[0], cpu_T[0], 0xffffff); +                } +                if (op == 2) { +                    tcg_gen_st_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,gdt.base)); +                    tcg_gen_st32_tl(cpu_T[1], cpu_env, offsetof(CPUX86State,gdt.limit)); +                } else { +                    tcg_gen_st_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,idt.base)); +                    tcg_gen_st32_tl(cpu_T[1], cpu_env, offsetof(CPUX86State,idt.limit)); +                } +            } +            break; +        case 4: /* smsw */ +            gen_svm_check_intercept(s, pc_start, SVM_EXIT_READ_CR0); +#if defined TARGET_X86_64 && defined HOST_WORDS_BIGENDIAN +            tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,cr[0]) + 4); +#else +            tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,cr[0])); +#endif +            gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 1); +            break; +        case 6: /* lmsw */ +            if (s->cpl != 0) { +                gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); +            } else { +                gen_svm_check_intercept(s, pc_start, SVM_EXIT_WRITE_CR0); +                gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); +                gen_helper_lmsw(cpu_env, cpu_T[0]); +                gen_jmp_im(s->pc - s->cs_base); +                gen_eob(s); +            } +            break; +        case 7: +            if (mod != 3) { /* invlpg */ +                if (s->cpl != 0) { +                    gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); +                } else { +                    gen_update_cc_op(s); +                    gen_jmp_im(pc_start - s->cs_base); +                    gen_lea_modrm(env, s, modrm); +                    gen_helper_invlpg(cpu_env, cpu_A0); +                    gen_jmp_im(s->pc - s->cs_base); +                    gen_eob(s); +                } +            } else { +                switch (rm) { +                case 0: /* swapgs */ +#ifdef TARGET_X86_64 +                    if (CODE64(s)) { +                        if (s->cpl != 0) { +                            gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); +                        } else { +                            tcg_gen_ld_tl(cpu_T[0], cpu_env, +                                offsetof(CPUX86State,segs[R_GS].base)); +                            tcg_gen_ld_tl(cpu_T[1], cpu_env, +                                offsetof(CPUX86State,kernelgsbase)); +                            tcg_gen_st_tl(cpu_T[1], cpu_env, +                                offsetof(CPUX86State,segs[R_GS].base)); +                            tcg_gen_st_tl(cpu_T[0], cpu_env, +                                offsetof(CPUX86State,kernelgsbase)); +                        } +                    } else +#endif +                    { +                        goto illegal_op; +                    } +                    break; +                case 1: /* rdtscp */ +                    if (!(s->cpuid_ext2_features & CPUID_EXT2_RDTSCP)) +                        goto illegal_op; +                    gen_update_cc_op(s); +                    gen_jmp_im(pc_start - s->cs_base); +                    if (s->tb->cflags & CF_USE_ICOUNT) { +                        gen_io_start(); +		    } +                    gen_helper_rdtscp(cpu_env); +                    if (s->tb->cflags & CF_USE_ICOUNT) { +                        gen_io_end(); +                        gen_jmp(s, s->pc - s->cs_base); +                    } +                    break; +                default: +                    goto illegal_op; +                } +            } +            break; +        default: +            goto illegal_op; +        } +        break; +    case 0x108: /* invd */ +    case 0x109: /* wbinvd */ +        if (s->cpl != 0) { +            gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); +        } else { +            gen_svm_check_intercept(s, pc_start, (b & 2) ? SVM_EXIT_INVD : SVM_EXIT_WBINVD); +            /* nothing to do */ +        } +        break; +    case 0x63: /* arpl or movslS (x86_64) */ +#ifdef TARGET_X86_64 +        if (CODE64(s)) { +            int d_ot; +            /* d_ot is the size of destination */ +            d_ot = dflag; + +            modrm = cpu_ldub_code(env, s->pc++); +            reg = ((modrm >> 3) & 7) | rex_r; +            mod = (modrm >> 6) & 3; +            rm = (modrm & 7) | REX_B(s); + +            if (mod == 3) { +                gen_op_mov_v_reg(MO_32, cpu_T[0], rm); +                /* sign extend */ +                if (d_ot == MO_64) { +                    tcg_gen_ext32s_tl(cpu_T[0], cpu_T[0]); +                } +                gen_op_mov_reg_v(d_ot, reg, cpu_T[0]); +            } else { +                gen_lea_modrm(env, s, modrm); +                gen_op_ld_v(s, MO_32 | MO_SIGN, cpu_T[0], cpu_A0); +                gen_op_mov_reg_v(d_ot, reg, cpu_T[0]); +            } +        } else +#endif +        { +            TCGLabel *label1; +            TCGv t0, t1, t2, a0; + +            if (!s->pe || s->vm86) +                goto illegal_op; +            t0 = tcg_temp_local_new(); +            t1 = tcg_temp_local_new(); +            t2 = tcg_temp_local_new(); +            ot = MO_16; +            modrm = cpu_ldub_code(env, s->pc++); +            reg = (modrm >> 3) & 7; +            mod = (modrm >> 6) & 3; +            rm = modrm & 7; +            if (mod != 3) { +                gen_lea_modrm(env, s, modrm); +                gen_op_ld_v(s, ot, t0, cpu_A0); +                a0 = tcg_temp_local_new(); +                tcg_gen_mov_tl(a0, cpu_A0); +            } else { +                gen_op_mov_v_reg(ot, t0, rm); +                TCGV_UNUSED(a0); +            } +            gen_op_mov_v_reg(ot, t1, reg); +            tcg_gen_andi_tl(cpu_tmp0, t0, 3); +            tcg_gen_andi_tl(t1, t1, 3); +            tcg_gen_movi_tl(t2, 0); +            label1 = gen_new_label(); +            tcg_gen_brcond_tl(TCG_COND_GE, cpu_tmp0, t1, label1); +            tcg_gen_andi_tl(t0, t0, ~3); +            tcg_gen_or_tl(t0, t0, t1); +            tcg_gen_movi_tl(t2, CC_Z); +            gen_set_label(label1); +            if (mod != 3) { +                gen_op_st_v(s, ot, t0, a0); +                tcg_temp_free(a0); +           } else { +                gen_op_mov_reg_v(ot, rm, t0); +            } +            gen_compute_eflags(s); +            tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, ~CC_Z); +            tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, t2); +            tcg_temp_free(t0); +            tcg_temp_free(t1); +            tcg_temp_free(t2); +        } +        break; +    case 0x102: /* lar */ +    case 0x103: /* lsl */ +        { +            TCGLabel *label1; +            TCGv t0; +            if (!s->pe || s->vm86) +                goto illegal_op; +            ot = dflag != MO_16 ? MO_32 : MO_16; +            modrm = cpu_ldub_code(env, s->pc++); +            reg = ((modrm >> 3) & 7) | rex_r; +            gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); +            t0 = tcg_temp_local_new(); +            gen_update_cc_op(s); +            if (b == 0x102) { +                gen_helper_lar(t0, cpu_env, cpu_T[0]); +            } else { +                gen_helper_lsl(t0, cpu_env, cpu_T[0]); +            } +            tcg_gen_andi_tl(cpu_tmp0, cpu_cc_src, CC_Z); +            label1 = gen_new_label(); +            tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_tmp0, 0, label1); +            gen_op_mov_reg_v(ot, reg, t0); +            gen_set_label(label1); +            set_cc_op(s, CC_OP_EFLAGS); +            tcg_temp_free(t0); +        } +        break; +    case 0x118: +        modrm = cpu_ldub_code(env, s->pc++); +        mod = (modrm >> 6) & 3; +        op = (modrm >> 3) & 7; +        switch(op) { +        case 0: /* prefetchnta */ +        case 1: /* prefetchnt0 */ +        case 2: /* prefetchnt0 */ +        case 3: /* prefetchnt0 */ +            if (mod == 3) +                goto illegal_op; +            gen_lea_modrm(env, s, modrm); +            /* nothing more to do */ +            break; +        default: /* nop (multi byte) */ +            gen_nop_modrm(env, s, modrm); +            break; +        } +        break; +    case 0x119 ... 0x11f: /* nop (multi byte) */ +        modrm = cpu_ldub_code(env, s->pc++); +        gen_nop_modrm(env, s, modrm); +        break; +    case 0x120: /* mov reg, crN */ +    case 0x122: /* mov crN, reg */ +        if (s->cpl != 0) { +            gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); +        } else { +            modrm = cpu_ldub_code(env, s->pc++); +            /* Ignore the mod bits (assume (modrm&0xc0)==0xc0). +             * AMD documentation (24594.pdf) and testing of +             * intel 386 and 486 processors all show that the mod bits +             * are assumed to be 1's, regardless of actual values. +             */ +            rm = (modrm & 7) | REX_B(s); +            reg = ((modrm >> 3) & 7) | rex_r; +            if (CODE64(s)) +                ot = MO_64; +            else +                ot = MO_32; +            if ((prefixes & PREFIX_LOCK) && (reg == 0) && +                (s->cpuid_ext3_features & CPUID_EXT3_CR8LEG)) { +                reg = 8; +            } +            switch(reg) { +            case 0: +            case 2: +            case 3: +            case 4: +            case 8: +                gen_update_cc_op(s); +                gen_jmp_im(pc_start - s->cs_base); +                if (b & 2) { +                    gen_op_mov_v_reg(ot, cpu_T[0], rm); +                    gen_helper_write_crN(cpu_env, tcg_const_i32(reg), +                                         cpu_T[0]); +                    gen_jmp_im(s->pc - s->cs_base); +                    gen_eob(s); +                } else { +                    gen_helper_read_crN(cpu_T[0], cpu_env, tcg_const_i32(reg)); +                    gen_op_mov_reg_v(ot, rm, cpu_T[0]); +                } +                break; +            default: +                goto illegal_op; +            } +        } +        break; +    case 0x121: /* mov reg, drN */ +    case 0x123: /* mov drN, reg */ +        if (s->cpl != 0) { +            gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); +        } else { +            modrm = cpu_ldub_code(env, s->pc++); +            /* Ignore the mod bits (assume (modrm&0xc0)==0xc0). +             * AMD documentation (24594.pdf) and testing of +             * intel 386 and 486 processors all show that the mod bits +             * are assumed to be 1's, regardless of actual values. +             */ +            rm = (modrm & 7) | REX_B(s); +            reg = ((modrm >> 3) & 7) | rex_r; +            if (CODE64(s)) +                ot = MO_64; +            else +                ot = MO_32; +            /* XXX: do it dynamically with CR4.DE bit */ +            if (reg == 4 || reg == 5 || reg >= 8) +                goto illegal_op; +            if (b & 2) { +                gen_svm_check_intercept(s, pc_start, SVM_EXIT_WRITE_DR0 + reg); +                gen_op_mov_v_reg(ot, cpu_T[0], rm); +                gen_helper_movl_drN_T0(cpu_env, tcg_const_i32(reg), cpu_T[0]); +                gen_jmp_im(s->pc - s->cs_base); +                gen_eob(s); +            } else { +                gen_svm_check_intercept(s, pc_start, SVM_EXIT_READ_DR0 + reg); +                tcg_gen_ld_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,dr[reg])); +                gen_op_mov_reg_v(ot, rm, cpu_T[0]); +            } +        } +        break; +    case 0x106: /* clts */ +        if (s->cpl != 0) { +            gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); +        } else { +            gen_svm_check_intercept(s, pc_start, SVM_EXIT_WRITE_CR0); +            gen_helper_clts(cpu_env); +            /* abort block because static cpu state changed */ +            gen_jmp_im(s->pc - s->cs_base); +            gen_eob(s); +        } +        break; +    /* MMX/3DNow!/SSE/SSE2/SSE3/SSSE3/SSE4 support */ +    case 0x1c3: /* MOVNTI reg, mem */ +        if (!(s->cpuid_features & CPUID_SSE2)) +            goto illegal_op; +        ot = mo_64_32(dflag); +        modrm = cpu_ldub_code(env, s->pc++); +        mod = (modrm >> 6) & 3; +        if (mod == 3) +            goto illegal_op; +        reg = ((modrm >> 3) & 7) | rex_r; +        /* generate a generic store */ +        gen_ldst_modrm(env, s, modrm, ot, reg, 1); +        break; +    case 0x1ae: +        modrm = cpu_ldub_code(env, s->pc++); +        mod = (modrm >> 6) & 3; +        op = (modrm >> 3) & 7; +        switch(op) { +        case 0: /* fxsave */ +            if (mod == 3 || !(s->cpuid_features & CPUID_FXSR) || +                (s->prefix & PREFIX_LOCK)) +                goto illegal_op; +            if ((s->flags & HF_EM_MASK) || (s->flags & HF_TS_MASK)) { +                gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); +                break; +            } +            gen_lea_modrm(env, s, modrm); +            gen_update_cc_op(s); +            gen_jmp_im(pc_start - s->cs_base); +            gen_helper_fxsave(cpu_env, cpu_A0, tcg_const_i32(dflag == MO_64)); +            break; +        case 1: /* fxrstor */ +            if (mod == 3 || !(s->cpuid_features & CPUID_FXSR) || +                (s->prefix & PREFIX_LOCK)) +                goto illegal_op; +            if ((s->flags & HF_EM_MASK) || (s->flags & HF_TS_MASK)) { +                gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); +                break; +            } +            gen_lea_modrm(env, s, modrm); +            gen_update_cc_op(s); +            gen_jmp_im(pc_start - s->cs_base); +            gen_helper_fxrstor(cpu_env, cpu_A0, tcg_const_i32(dflag == MO_64)); +            break; +        case 2: /* ldmxcsr */ +        case 3: /* stmxcsr */ +            if (s->flags & HF_TS_MASK) { +                gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); +                break; +            } +            if ((s->flags & HF_EM_MASK) || !(s->flags & HF_OSFXSR_MASK) || +                mod == 3) +                goto illegal_op; +            gen_lea_modrm(env, s, modrm); +            if (op == 2) { +                tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0, +                                    s->mem_index, MO_LEUL); +                gen_helper_ldmxcsr(cpu_env, cpu_tmp2_i32); +            } else { +                tcg_gen_ld32u_tl(cpu_T[0], cpu_env, offsetof(CPUX86State, mxcsr)); +                gen_op_st_v(s, MO_32, cpu_T[0], cpu_A0); +            } +            break; +        case 5: /* lfence */ +        case 6: /* mfence */ +            if ((modrm & 0xc7) != 0xc0 || !(s->cpuid_features & CPUID_SSE2)) +                goto illegal_op; +            break; +        case 7: /* sfence / clflush */ +            if ((modrm & 0xc7) == 0xc0) { +                /* sfence */ +                /* XXX: also check for cpuid_ext2_features & CPUID_EXT2_EMMX */ +                if (!(s->cpuid_features & CPUID_SSE)) +                    goto illegal_op; +            } else { +                /* clflush */ +                if (!(s->cpuid_features & CPUID_CLFLUSH)) +                    goto illegal_op; +                gen_lea_modrm(env, s, modrm); +            } +            break; +        default: +            goto illegal_op; +        } +        break; +    case 0x10d: /* 3DNow! prefetch(w) */ +        modrm = cpu_ldub_code(env, s->pc++); +        mod = (modrm >> 6) & 3; +        if (mod == 3) +            goto illegal_op; +        gen_lea_modrm(env, s, modrm); +        /* ignore for now */ +        break; +    case 0x1aa: /* rsm */ +        gen_svm_check_intercept(s, pc_start, SVM_EXIT_RSM); +        if (!(s->flags & HF_SMM_MASK)) +            goto illegal_op; +        gen_update_cc_op(s); +        gen_jmp_im(s->pc - s->cs_base); +        gen_helper_rsm(cpu_env); +        gen_eob(s); +        break; +    case 0x1b8: /* SSE4.2 popcnt */ +        if ((prefixes & (PREFIX_REPZ | PREFIX_LOCK | PREFIX_REPNZ)) != +             PREFIX_REPZ) +            goto illegal_op; +        if (!(s->cpuid_ext_features & CPUID_EXT_POPCNT)) +            goto illegal_op; + +        modrm = cpu_ldub_code(env, s->pc++); +        reg = ((modrm >> 3) & 7) | rex_r; + +        if (s->prefix & PREFIX_DATA) { +            ot = MO_16; +        } else { +            ot = mo_64_32(dflag); +        } + +        gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); +        gen_helper_popcnt(cpu_T[0], cpu_env, cpu_T[0], tcg_const_i32(ot)); +        gen_op_mov_reg_v(ot, reg, cpu_T[0]); + +        set_cc_op(s, CC_OP_EFLAGS); +        break; +    case 0x10e ... 0x10f: +        /* 3DNow! instructions, ignore prefixes */ +        s->prefix &= ~(PREFIX_REPZ | PREFIX_REPNZ | PREFIX_DATA); +    case 0x110 ... 0x117: +    case 0x128 ... 0x12f: +    case 0x138 ... 0x13a: +    case 0x150 ... 0x179: +    case 0x17c ... 0x17f: +    case 0x1c2: +    case 0x1c4 ... 0x1c6: +    case 0x1d0 ... 0x1fe: +        gen_sse(env, s, b, pc_start, rex_r); +        break; +    default: +        goto illegal_op; +    } +    /* lock generation */ +    if (s->prefix & PREFIX_LOCK) +        gen_helper_unlock(); +    return s->pc; + illegal_op: +    if (s->prefix & PREFIX_LOCK) +        gen_helper_unlock(); +    /* XXX: ensure that no lock was generated */ +    gen_exception(s, EXCP06_ILLOP, pc_start - s->cs_base); +    return s->pc; +} + +void optimize_flags_init(void) +{ +    static const char reg_names[CPU_NB_REGS][4] = { +#ifdef TARGET_X86_64 +        [R_EAX] = "rax", +        [R_EBX] = "rbx", +        [R_ECX] = "rcx", +        [R_EDX] = "rdx", +        [R_ESI] = "rsi", +        [R_EDI] = "rdi", +        [R_EBP] = "rbp", +        [R_ESP] = "rsp", +        [8]  = "r8", +        [9]  = "r9", +        [10] = "r10", +        [11] = "r11", +        [12] = "r12", +        [13] = "r13", +        [14] = "r14", +        [15] = "r15", +#else +        [R_EAX] = "eax", +        [R_EBX] = "ebx", +        [R_ECX] = "ecx", +        [R_EDX] = "edx", +        [R_ESI] = "esi", +        [R_EDI] = "edi", +        [R_EBP] = "ebp", +        [R_ESP] = "esp", +#endif +    }; +    int i; + +    cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); +    cpu_cc_op = tcg_global_mem_new_i32(TCG_AREG0, +                                       offsetof(CPUX86State, cc_op), "cc_op"); +    cpu_cc_dst = tcg_global_mem_new(TCG_AREG0, offsetof(CPUX86State, cc_dst), +                                    "cc_dst"); +    cpu_cc_src = tcg_global_mem_new(TCG_AREG0, offsetof(CPUX86State, cc_src), +                                    "cc_src"); +    cpu_cc_src2 = tcg_global_mem_new(TCG_AREG0, offsetof(CPUX86State, cc_src2), +                                     "cc_src2"); + +    for (i = 0; i < CPU_NB_REGS; ++i) { +        cpu_regs[i] = tcg_global_mem_new(TCG_AREG0, +                                         offsetof(CPUX86State, regs[i]), +                                         reg_names[i]); +    } +} + +/* generate intermediate code in gen_opc_buf and gen_opparam_buf for +   basic block 'tb'. If search_pc is TRUE, also generate PC +   information for each intermediate instruction. */ +static inline void gen_intermediate_code_internal(X86CPU *cpu, +                                                  TranslationBlock *tb, +                                                  bool search_pc) +{ +    CPUState *cs = CPU(cpu); +    CPUX86State *env = &cpu->env; +    DisasContext dc1, *dc = &dc1; +    target_ulong pc_ptr; +    CPUBreakpoint *bp; +    int j, lj; +    uint64_t flags; +    target_ulong pc_start; +    target_ulong cs_base; +    int num_insns; +    int max_insns; + +    /* generate intermediate code */ +    pc_start = tb->pc; +    cs_base = tb->cs_base; +    flags = tb->flags; + +    dc->pe = (flags >> HF_PE_SHIFT) & 1; +    dc->code32 = (flags >> HF_CS32_SHIFT) & 1; +    dc->ss32 = (flags >> HF_SS32_SHIFT) & 1; +    dc->addseg = (flags >> HF_ADDSEG_SHIFT) & 1; +    dc->f_st = 0; +    dc->vm86 = (flags >> VM_SHIFT) & 1; +    dc->cpl = (flags >> HF_CPL_SHIFT) & 3; +    dc->iopl = (flags >> IOPL_SHIFT) & 3; +    dc->tf = (flags >> TF_SHIFT) & 1; +    dc->singlestep_enabled = cs->singlestep_enabled; +    dc->cc_op = CC_OP_DYNAMIC; +    dc->cc_op_dirty = false; +    dc->cs_base = cs_base; +    dc->tb = tb; +    dc->popl_esp_hack = 0; +    /* select memory access functions */ +    dc->mem_index = 0; +    if (flags & HF_SOFTMMU_MASK) { +        dc->mem_index = cpu_mmu_index(env); +    } +    dc->cpuid_features = env->features[FEAT_1_EDX]; +    dc->cpuid_ext_features = env->features[FEAT_1_ECX]; +    dc->cpuid_ext2_features = env->features[FEAT_8000_0001_EDX]; +    dc->cpuid_ext3_features = env->features[FEAT_8000_0001_ECX]; +    dc->cpuid_7_0_ebx_features = env->features[FEAT_7_0_EBX]; +#ifdef TARGET_X86_64 +    dc->lma = (flags >> HF_LMA_SHIFT) & 1; +    dc->code64 = (flags >> HF_CS64_SHIFT) & 1; +#endif +    dc->flags = flags; +    dc->jmp_opt = !(dc->tf || cs->singlestep_enabled || +                    (flags & HF_INHIBIT_IRQ_MASK) +#ifndef CONFIG_SOFTMMU +                    || (flags & HF_SOFTMMU_MASK) +#endif +                    ); +    /* Do not optimize repz jumps at all in icount mode, because +       rep movsS instructions are execured with different paths +       in !repz_opt and repz_opt modes. The first one was used +       always except single step mode. And this setting +       disables jumps optimization and control paths become +       equivalent in run and single step modes. +       Now there will be no jump optimization for repz in +       record/replay modes and there will always be an +       additional step for ecx=0 when icount is enabled. +     */ +    dc->repz_opt = !dc->jmp_opt && !(tb->cflags & CF_USE_ICOUNT); +#if 0 +    /* check addseg logic */ +    if (!dc->addseg && (dc->vm86 || !dc->pe || !dc->code32)) +        printf("ERROR addseg\n"); +#endif + +    cpu_T[0] = tcg_temp_new(); +    cpu_T[1] = tcg_temp_new(); +    cpu_A0 = tcg_temp_new(); + +    cpu_tmp0 = tcg_temp_new(); +    cpu_tmp1_i64 = tcg_temp_new_i64(); +    cpu_tmp2_i32 = tcg_temp_new_i32(); +    cpu_tmp3_i32 = tcg_temp_new_i32(); +    cpu_tmp4 = tcg_temp_new(); +    cpu_ptr0 = tcg_temp_new_ptr(); +    cpu_ptr1 = tcg_temp_new_ptr(); +    cpu_cc_srcT = tcg_temp_local_new(); + +    dc->is_jmp = DISAS_NEXT; +    pc_ptr = pc_start; +    lj = -1; +    num_insns = 0; +    max_insns = tb->cflags & CF_COUNT_MASK; +    if (max_insns == 0) +        max_insns = CF_COUNT_MASK; + +    gen_tb_start(tb); +    for(;;) { +        if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { +            QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { +                if (bp->pc == pc_ptr && +                    !((bp->flags & BP_CPU) && (tb->flags & HF_RF_MASK))) { +                    gen_debug(dc, pc_ptr - dc->cs_base); +                    goto done_generating; +                } +            } +        } +        if (search_pc) { +            j = tcg_op_buf_count(); +            if (lj < j) { +                lj++; +                while (lj < j) +                    tcg_ctx.gen_opc_instr_start[lj++] = 0; +            } +            tcg_ctx.gen_opc_pc[lj] = pc_ptr; +            gen_opc_cc_op[lj] = dc->cc_op; +            tcg_ctx.gen_opc_instr_start[lj] = 1; +            tcg_ctx.gen_opc_icount[lj] = num_insns; +        } +        if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) +            gen_io_start(); + +        pc_ptr = disas_insn(env, dc, pc_ptr); +        num_insns++; +        /* stop translation if indicated */ +        if (dc->is_jmp) +            break; +        /* if single step mode, we generate only one instruction and +           generate an exception */ +        /* if irq were inhibited with HF_INHIBIT_IRQ_MASK, we clear +           the flag and abort the translation to give the irqs a +           change to be happen */ +        if (dc->tf || dc->singlestep_enabled || +            (flags & HF_INHIBIT_IRQ_MASK)) { +            gen_jmp_im(pc_ptr - dc->cs_base); +            gen_eob(dc); +            break; +        } +        /* Do not cross the boundary of the pages in icount mode, +           it can cause an exception. Do it only when boundary is +           crossed by the first instruction in the block. +           If current instruction already crossed the bound - it's ok, +           because an exception hasn't stopped this code. +         */ +        if ((tb->cflags & CF_USE_ICOUNT) +            && ((pc_ptr & TARGET_PAGE_MASK) +                != ((pc_ptr + TARGET_MAX_INSN_SIZE - 1) & TARGET_PAGE_MASK) +                || (pc_ptr & ~TARGET_PAGE_MASK) == 0)) { +            gen_jmp_im(pc_ptr - dc->cs_base); +            gen_eob(dc); +            break; +        } +        /* if too long translation, stop generation too */ +        if (tcg_op_buf_full() || +            (pc_ptr - pc_start) >= (TARGET_PAGE_SIZE - 32) || +            num_insns >= max_insns) { +            gen_jmp_im(pc_ptr - dc->cs_base); +            gen_eob(dc); +            break; +        } +        if (singlestep) { +            gen_jmp_im(pc_ptr - dc->cs_base); +            gen_eob(dc); +            break; +        } +    } +    if (tb->cflags & CF_LAST_IO) +        gen_io_end(); +done_generating: +    gen_tb_end(tb, num_insns); + +    /* we don't forget to fill the last values */ +    if (search_pc) { +        j = tcg_op_buf_count(); +        lj++; +        while (lj <= j) +            tcg_ctx.gen_opc_instr_start[lj++] = 0; +    } + +#ifdef DEBUG_DISAS +    if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { +        int disas_flags; +        qemu_log("----------------\n"); +        qemu_log("IN: %s\n", lookup_symbol(pc_start)); +#ifdef TARGET_X86_64 +        if (dc->code64) +            disas_flags = 2; +        else +#endif +            disas_flags = !dc->code32; +        log_target_disas(cs, pc_start, pc_ptr - pc_start, disas_flags); +        qemu_log("\n"); +    } +#endif + +    if (!search_pc) { +        tb->size = pc_ptr - pc_start; +        tb->icount = num_insns; +    } +} + +void gen_intermediate_code(CPUX86State *env, TranslationBlock *tb) +{ +    gen_intermediate_code_internal(x86_env_get_cpu(env), tb, false); +} + +void gen_intermediate_code_pc(CPUX86State *env, TranslationBlock *tb) +{ +    gen_intermediate_code_internal(x86_env_get_cpu(env), tb, true); +} + +void restore_state_to_opc(CPUX86State *env, TranslationBlock *tb, int pc_pos) +{ +    int cc_op; +#ifdef DEBUG_DISAS +    if (qemu_loglevel_mask(CPU_LOG_TB_OP)) { +        int i; +        qemu_log("RESTORE:\n"); +        for(i = 0;i <= pc_pos; i++) { +            if (tcg_ctx.gen_opc_instr_start[i]) { +                qemu_log("0x%04x: " TARGET_FMT_lx "\n", i, +                        tcg_ctx.gen_opc_pc[i]); +            } +        } +        qemu_log("pc_pos=0x%x eip=" TARGET_FMT_lx " cs_base=%x\n", +                pc_pos, tcg_ctx.gen_opc_pc[pc_pos] - tb->cs_base, +                (uint32_t)tb->cs_base); +    } +#endif +    env->eip = tcg_ctx.gen_opc_pc[pc_pos] - tb->cs_base; +    cc_op = gen_opc_cc_op[pc_pos]; +    if (cc_op != CC_OP_DYNAMIC) +        env->cc_op = cc_op; +}  | 
